Chromia
Start Building

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.

circom
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:

  • a and b are private inputs. The prover holds their values; the verifier never sees them.
  • c is a public output, so both prover and verifier know its value.
  • c <== a * b; is a constraint requiring c to equal the product of a and b. 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.

  1. Writing the circuit: Put your circuit in a .circom file, as in the Multiplier2 example.

  2. Compilation: Run the Circom compiler on the .circom file. 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
  3. Witness calculation: The witness is 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.

  4. 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.

  5. 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.

Written for this site from Chromia’s public course. Code samples are the course’s own.