Zero-knowledge proof circuit development using circom. Use when implementing arithmetic circuits for zkSNARKs, including circuit design, constraint verification, witness generation, and testing...
Expert guidance for designing, implementing, and testing zero-knowledge proof circuits using circom.
This skill provides comprehensive support for circom circuit development:
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
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();
Use the compilation script:
bash scripts/compile_circuit.sh circuits/your_circuit.circom
This generates:
build/your_circuit_js/your_circuit.wasm - Witness calculatorbuild/your_circuit.r1cs - Constraint systembuild/your_circuit.sym - Symbol mappingGenerate zkey and verification key:
bash scripts/setup_keys.sh build/your_circuit.r1cs
This generates:
build/zkey/your_circuit.zkey - Proving keybuild/zkey/verification_key.json - Verification keybuild/zkey/your_circuit_verifier.sol - Solidity verifierUse the test template:
cp assets/template_test.js test.js
Update test inputs and run:
node test.js
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā 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
Before writing code, clarify:
Example: Password authentication
Consult circomlib_components.md for standard components:
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;
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.
Consult circuit_patterns.md for complete implementations:
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 });
Always test:
{ in: 0 }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
Fewer constraints = faster proving time.
Check constraint count:
npx snarkjs r1cs info build/circuit.r1cs
Optimization tips:
assert for compile-time checks (no runtime cost)Monitor statistics:
npx snarkjs r1cs info circuit.r1cs
Output shows:
Before deploying, verify:
<-- without verification)See best_practices.md for detailed security guidelines.
"Constraint doesn't match"
<== or ===<-- without corresponding constraints"Not enough values"
"Scalar size exceeds field size"
High constraint count
log() statements in circuitCompile 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
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 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
This skill includes detailed reference documentation:
Standard library component reference:
Security and optimization guidelines:
Implementation patterns for common use cases:
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