name: groth16-circuit description: Create Circom circuits for zkVerify Groth16 proofs with snarkjs. Use when the user wants to create a ZK circuit, write circom code, or start a new zero-knowledge proof project. argument-hint: [circuit-type]
Create Circom Circuit for zkVerify
Help the user create a Circom circuit that will be used with zkVerify for proof verification.
Arguments
If invoked with an argument (e.g., /groth16-circuit hash-preimage), use it as the circuit type and skip asking what to prove.
| Argument | Circuit Type |
|---|---|
hash-preimage |
Prove knowledge of hash preimage |
range-proof |
Prove value is within range |
merkle-proof |
Prove membership in Merkle tree |
age-check |
Prove age meets minimum |
| (custom) | Use as circuit description |
If no argument provided, ask the user what they want to prove.
Pre-flight Check
Before creating the circuit, verify Node.js is available:
node --version
| Check | If Missing |
|---|---|
| node | Direct user to install Node.js |
If Node.js is missing, stop and help user install it first.
Note: Circom is NOT required for this skill. You can create .circom files without having circom installed. Circom will be automatically installed when the user runs /groth16-compile.
Scope
This skill is specifically for:
- Language: Circom
- Prover: snarkjs
- Proof System: Groth16
- Verifier: zkVerify
This skill is NOT for:
- Groth16 proofs from other libraries (arkworks/Rust, gnark/Go)
- Other proof systems (Ultraplonk, RISC0, SP1, Plonky2, etc.)
- Other circuit languages (Noir, Rust zkVM code, etc.)
If the user asks for a different proof system or language, inform them this skill only covers Circom/Groth16 and suggest they check zkVerify documentation for other proof systems.
Instructions
Ask about the circuit purpose if not specified:
- What computation should be proven?
- What are the private inputs (hidden)?
- What are the public inputs (revealed)?
Create the circuit file in
circuits/directoryAnalyze input dependencies to determine if a helper script is needed
Create valid inputs - either directly or via helper script
Critical: Analyzing Input Dependencies
Before creating the input file, analyze the circuit's inputs:
Question: Are any inputs computed from other inputs?
| Answer | Action |
|---|---|
| YES | Create a helper script in scripts/ to compute dependent values |
| NO | Create inputs/*.json directly with valid example values |
Examples of DEPENDENT Inputs (Need Helper Script)
- Hash verification:
hash = Poseidon(preimage)- hash depends on preimage - Merkle proofs:
root = hash(hash(...leaves))- root depends on leaves - Commitments:
commitment = hash(secret, nullifier)- commitment depends on secrets
Examples of INDEPENDENT Inputs (No Script Needed)
- Range proofs:
value=50, min=0, max=100- just numbers - Arithmetic:
a=10, b=20, expectedSum=30- just ensure math is correct - Age verification:
age=25, minAge=18- independent values
File Structure
circuits/
├── <circuit_name>.circom # The circuit
scripts/
├── compute_<name>_input.ts # Helper script (only if inputs are dependent)
inputs/
└── <circuit_name>_input.json # Valid input file
Important Circom Rules
- All signals must be constrained - Every signal must appear in at least one constraint
- Use
<==for assignment + constraint - Combines<--and=== - Use
<--only when necessary - Must add separate constraint with=== - Declare public inputs in main -
component main {public [input1, input2]} = Template(); - Use strings for large numbers in JSON to avoid precision issues
CRITICAL: zkVerify Public Signal Limit
zkVerify has a limit of 64 total public signals (inputs + outputs) for Groth16 proofs submitted via the Kurier API.
| Total Public Signals | Result |
|---|---|
| ≤ 64 | Works |
| > 64 | FAILS (optimisticVerify: failed) |
This limit is NOT documented by zkVerify. Proofs with >64 public signals will verify locally with snarkjs but fail on zkVerify with no useful error message.
How to Count Public Signals
Total Public Signals = Public Inputs + Public Outputs
Example:
component main {public [a, b, c]} = MyCircuit();with 1 output = 4 total signals
If Your Circuit Exceeds 64 Public Signals
Use hash commitments to reduce public inputs:
Instead of:
// BAD: 81 public inputs (puzzle grid)
signal input puzzle[9][9]; // All public
Do this:
// GOOD: 1 public input (hash of puzzle)
signal input puzzleHash; // Public: just the hash
signal input puzzle[9][9]; // Private: actual puzzle
// Prove: Poseidon(puzzle) === puzzleHash
Circuits That Often Exceed the Limit
| Use Case | Naive Approach | Problem | Solution |
|---|---|---|---|
| Sudoku | 81 public inputs (puzzle) | 81 > 64 | Hash the puzzle |
| Voting | N public voter IDs | N could be large | Merkle tree of voters |
| Batch proofs | Many public values | Sum exceeds 64 | Aggregate with hashes |
After Creating Circuit
If helper script was created: Run it to generate valid inputs
npm install circomlibjs ts-node typescript @types/node npx ts-node scripts/compute_<name>_input.ts <args>Install circomlib if circuit uses library components:
npm install circomlibNext step: Run
/groth16-compileto compile the circuit
Checklist
- Circuit file created in
circuits/ - Analyzed input dependencies
- Helper script created (if inputs are dependent)
- Input file has VALID values (not placeholders)
- Reminded user of next steps
Additional Resources
For detailed examples and reference material, see:
- examples.md - Complete circuit examples with helper scripts (hash preimage, range proof, Merkle proof, age check)
- reference.md - Circom syntax reference, circomlib components, and common errors