06 / 19Module 1: Writing Circom circuitsAdvanced
Circom circuits: introduction
A Circom circuit is a program, written in the Circom language, that specifies the constraints a zero-knowledge proof must satisfy. Circom is a domain-specific language (DSL) for building arithmetic circuits, the core building block of zk-SNARKs (Zero-Knowledge Succinct Non-Interactive Arguments of Knowledge).
With Circom, you express a computation so that someone can prove they performed it correctly without disclosing every input. A circuit has two parts:
- Signals: the circuit's variables. A signal can be an input, an output, or an intermediate value, and inputs can be either public or private.
- Constraints: equations that relate signals to each other. Every Circom constraint has to reduce to the quadratic form
A * B + C = 0, where A, B, and C are each linear combinations of signals.
Example of a Circom circuit
The circuit below proves that you know two numbers whose product equals a given output.
pragma circom 2.0.0;template Multiplier2() { // Private inputs signal input a; signal input b; // Public output signal output c; // Constraint c <== a * b;}component main = Multiplier2();How the pieces fit together:
aandbare private inputs. The prover holds their values; the verifier never sees them.cis a public output, so both prover and verifier know its value.c <== a * b;is a constraint requiringcto equal the product ofaandb. The<==operator does two things at once: it assigns the value and adds the constraint.
How to use a Circom circuit
Producing and checking a zero-knowledge proof from a Circom circuit takes several steps.
-
Writing the circuit: Put your circuit in a
.circomfile, as in theMultiplier2example. -
Compilation: Run the Circom compiler on the
.circomfile. It reports any syntax errors and produces two outputs:- The circuit's constraints, stored in R1CS (Rank-1 Constraint System) format.
- A program, typically WebAssembly, that computes the
witness.
bash circom multiplier.circom --r1cs --wasm --sym -
Witness calculation: The
witnessis a file holding the value of every signal for one particular run of the circuit, that is, a valid assignment to all signals. To produce it, you supply an input file containing the values of the public and private inputs. -
Proof generation: With the R1CS file and the witness in hand, you generate the zero-knowledge proof using a library such as
snarkjs. This step also needs a "proving key", which comes out of a trusted setup ceremony. -
Proof verification: A verifier takes the proof, the public inputs and outputs, a "verification key" (also produced by the trusted setup), and checks whether the proof is valid. A valid proof shows that the prover knew private inputs satisfying the circuit's constraints, while revealing nothing about what those inputs were.