Claude-skill-registry circom-dev

Zero-knowledge proof circuit development using circom. Use when implementing arithmetic circuits for zkSNARKs, including circuit design, constraint verification, witness generation, and testing. Triggers on tasks like "create a circom circuit", "implement ZK proof", "write Merkle tree circuit", "add range proof", or any zero-knowledge cryptography implementation requiring circom.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/circom-dev" ~/.claude/skills/majiayu000-claude-skill-registry-circom-dev && rm -rf "$T"
manifest: skills/data/circom-dev/SKILL.md
source content

Circom Development

Expert guidance for designing, implementing, and testing zero-knowledge proof circuits using circom.

Overview

This skill provides comprehensive support for circom circuit development:

  • Circuit Implementation: Design and write optimized arithmetic circuits
  • Constraint Verification: Ensure circuits are properly constrained and secure
  • Witness Generation: Create and test witness calculators
  • Testing: Comprehensive testing patterns for circuits
  • Best Practices: Security guidelines and optimization techniques

Quick Start

1. Project Setup

Initialize a new circom project with required dependencies:

# Copy package.json template
cp assets/package.json ./package.json

# Install dependencies
npm install

# Create project structure
mkdir -p circuits build scripts

2. Create Circuit

Use the circuit template as a starting point:

cp assets/template_circuit.circom circuits/your_circuit.circom

Edit the circuit with your logic:

pragma circom 2.0.0;
include "node_modules/circomlib/circuits/poseidon.circom";

template YourCircuit() {
    signal input in;
    signal output out;

    component hasher = Poseidon(1);
    hasher.inputs[0] <== in;
    out <== hasher.out;
}

component main = YourCircuit();

3. Compile Circuit

Use the compilation script:

bash scripts/compile_circuit.sh circuits/your_circuit.circom

This generates:

  • build/your_circuit_js/your_circuit.wasm
    - Witness calculator
  • build/your_circuit.r1cs
    - Constraint system
  • build/your_circuit.sym
    - Symbol mapping

4. Setup Proving Keys

Generate zkey and verification key:

bash scripts/setup_keys.sh build/your_circuit.r1cs

This generates:

  • build/zkey/your_circuit.zkey
    - Proving key
  • build/zkey/verification_key.json
    - Verification key
  • build/zkey/your_circuit_verifier.sol
    - Solidity verifier

5. Create Test

Use the test template:

cp assets/template_test.js test.js

Update test inputs and run:

node test.js

Workflow Decision Tree

┌─────────────────────────────────────┐
│ What do you need to do?             │
└──────────────┬──────────────────────┘
               │
       ┌───────┴────────┐
       │                │
   New Circuit    Modify Existing
       │                │
       ▼                ▼
  Start from      Read existing
   template        circuit first
       │                │
       ▼                ▼
  Implement       Understand
  constraints     constraints
       │                │
       ▼                ▼
   Compile         Make changes
       │                │
       ▼                ▼
  Setup keys      Recompile
       │                │
       ▼                ▼
  Write tests     Update tests
       │                │
       └────────┬───────┘
                ▼
          Run & verify
                │
        ┌───────┴────────┐
        │                │
    Success         Failure
        │                │
        ▼                ▼
    Complete      Debug & fix
                        │
                        └──> Repeat

Circuit Design Guidelines

1. Define Requirements

Before writing code, clarify:

  • Private inputs: What information must remain secret?
  • Public inputs: What can be revealed to the verifier?
  • Outputs: What statement are you proving?
  • Constraints: What rules must be enforced?

Example: Password authentication

  • Private: password
  • Public: passwordHash
  • Statement: "I know a password that hashes to passwordHash"

2. Choose Components

Consult circomlib_components.md for standard components:

  • Hashing: Poseidon, MiMC
  • Comparisons: IsZero, LessThan, IsEqual
  • Merkle Trees: SMTVerifier
  • Signatures: EdDSA

3. Write Constraints

Follow these principles:

Use

<==
for most operations (assigns AND constrains):

output <== input1 * input2;

Use

===
for explicit constraints:

component.out === expectedValue;

NEVER use

<--
alone (no constraint):

// DANGEROUS - prover can cheat!
temp <-- unconstrained_value;

4. Validate Inputs

Always constrain input ranges:

// Ensure value is less than maximum
component check = LessThan(32);
check.in[0] <== value;
check.in[1] <== maxValue;
check.out === 1;

See best_practices.md for security guidelines.

Common Circuit Patterns

Consult circuit_patterns.md for complete implementations:

Authentication

  • Password proof: Prove knowledge of password without revealing it
  • Credential verification: Prove possession of valid credentials

Merkle Trees

  • Membership proof: Prove element is in a set without revealing which
  • Tree update: Prove correct update of Merkle tree

Range Proofs

  • Value in range: Prove value is within bounds (e.g., age > 18)
  • Balance sufficiency: Prove sufficient balance without revealing amount

Voting

  • Anonymous voting: Vote without revealing identity
  • Weighted voting: Vote with weight based on holdings

Privacy

  • Private transfer: Transfer funds without revealing sender/receiver/amount
  • Nullifier pattern: Prevent double-spending

Testing Workflow

1. Unit Test Components

Test individual templates in isolation:

const circuit = await wasm_tester("circuits/component.circom");

// Test valid input
const input = { in: 10 };
const witness = await circuit.calculateWitness(input);
await circuit.checkConstraints(witness);

// Test expected output
await circuit.assertOut(witness, { out: 100 });

2. Test Edge Cases

Always test:

  • Zero values:
    { in: 0 }
  • Maximum values: Near field prime
  • Boundary conditions: Min/max range values
  • Invalid inputs: Should fail constraint checks

3. Integration Testing

Test full proof generation and verification:

const { proof, publicSignals } = await snarkjs.groth16.fullProve(
  input,
  wasmPath,
  zkeyPath
);

const vKey = JSON.parse(fs.readFileSync(vkeyPath));
const isValid = await snarkjs.groth16.verify(vKey, publicSignals, proof);
assert(isValid);

Use

scripts/verify_proof.js
for standalone verification:

node scripts/verify_proof.js -p proof.json -s public.json -v verification_key.json

Optimization Techniques

1. Minimize Constraints

Fewer constraints = faster proving time.

Check constraint count:

npx snarkjs r1cs info build/circuit.r1cs

Optimization tips:

  • Reuse components when possible
  • Minimize hash operations (expensive)
  • Use
    assert
    for compile-time checks (no runtime cost)
  • Combine operations where possible

2. Choose Efficient Components

  • Poseidon > MiMC: Poseidon is optimized for zkSNARKs
  • Bit operations: Use Num2Bits/Bits2Num for bit manipulation
  • Range checks: Use SafeLessThan for range-checked comparisons

3. Profile Circuit

Monitor statistics:

npx snarkjs r1cs info circuit.r1cs

Output shows:

  • Number of constraints
  • Number of public inputs/outputs
  • Number of private inputs
  • Number of wires

Security Checklist

Before deploying, verify:

  • All signals properly constrained (no
    <--
    without verification)
  • Input ranges validated
  • Division operations properly constrained (with remainder check)
  • Public/private signals correctly specified
  • Edge cases tested (0, max values, boundaries)
  • No under-constrained circuits (verify constraint count)
  • Code reviewed
  • Static analysis tools run (Circomspect, PICUS if available)

See best_practices.md for detailed security guidelines.

Troubleshooting

Common Issues

"Constraint doesn't match"

  • Check all signals are properly constrained with
    <==
    or
    ===
  • Verify arithmetic is correct
  • Look for signals using
    <--
    without corresponding constraints

"Not enough values"

  • Ensure all inputs are provided in test
  • Check array sizes match template parameters

"Scalar size exceeds field size"

  • Input value is too large for the field
  • Use range constraints to validate inputs

High constraint count

  • Review circuit for optimization opportunities
  • Consider refactoring complex logic
  • Check for unnecessary component instantiations

Debugging Tips

  1. Add debug signals: Create intermediate signals to inspect values in witness
  2. Use circom logger: Add
    log()
    statements in circuit
  3. Test incrementally: Build circuit piece by piece, testing each addition
  4. Verify constraint count: Monitor constraint growth as you add logic

Scripts Reference

compile_circuit.sh

Compile circom circuits to WASM and R1CS.

./scripts/compile_circuit.sh <circuit_file> [options]

Options:
  -o, --output DIR    Output directory (default: build)
  -h, --help         Show help

setup_keys.sh

Generate proving and verification keys.

./scripts/setup_keys.sh <r1cs_file> [options]

Options:
  -s, --size N        Circuit size (default: 12)
  -o, --output DIR    Output directory (default: build/zkey)
  -p, --ptau DIR      ptau directory (default: ptau)
  -h, --help         Show help

verify_proof.js

Verify a zero-knowledge proof.

node scripts/verify_proof.js [options]

Options:
  -p, --proof FILE     Proof JSON file
  -s, --signals FILE   Public signals JSON file
  -v, --vkey FILE      Verification key file
  -h, --help          Show help

References

This skill includes detailed reference documentation:

circomlib_components.md

Standard library component reference:

  • Hash functions (Poseidon, MiMC)
  • Comparators (IsZero, LessThan, IsEqual)
  • Multiplexers (Mux1, Mux3)
  • Bitwise operations (Num2Bits, Bits2Num)
  • Merkle trees (SMTVerifier)
  • Signatures (EdDSA)

best_practices.md

Security and optimization guidelines:

  • Constraint writing best practices
  • Security considerations
  • Optimization techniques
  • Testing and debugging
  • Common pitfalls and how to avoid them

circuit_patterns.md

Implementation patterns for common use cases:

  • Authentication patterns
  • Merkle tree patterns
  • Range proof patterns
  • Voting patterns
  • Privacy-preserving patterns

Example: Complete Workflow

Here's a complete example of implementing a password authentication circuit:

// 1. Create circuit: circuits/password_auth.circom
pragma circom 2.0.0;
include "node_modules/circomlib/circuits/poseidon.circom";

template PasswordAuth() {
    signal input password;
    signal input passwordHash;

    component hasher = Poseidon(1);
    hasher.inputs[0] <== password;
    passwordHash === hasher.out;
}

component main {public [passwordHash]} = PasswordAuth();
# 2. Compile
bash scripts/compile_circuit.sh circuits/password_auth.circom

# 3. Setup keys
bash scripts/setup_keys.sh build/password_auth.r1cs
// 4. Create test: test_password.js
const snarkjs = require("snarkjs");
const circomlibjs = require("circomlibjs");

async function test() {
  // Calculate expected hash
  const password = 12345;
  const poseidon = await circomlibjs.buildPoseidon();
  const passwordHash = poseidon.F.toString(poseidon([password]));

  // Generate proof
  const { proof, publicSignals } = await snarkjs.groth16.fullProve(
    { password, passwordHash },
    "build/password_auth_js/password_auth.wasm",
    "build/zkey/password_auth.zkey"
  );

  // Verify
  const vKey = require("./build/zkey/verification_key.json");
  const isValid = await snarkjs.groth16.verify(vKey, publicSignals, proof);
  console.log("Valid:", isValid);
}

test();
# 5. Run test
node test_password.js

Additional Resources