Pick a depth. Each prompt opens in your AI pre-loaded with the lesson. Click a row to preview the prompt.
Every Circom program produces two artifacts: the R1CS (constraints) and the witness generator (WASM or C++ that computes signal values from inputs). The constraint system says 'these equations must hold.' The witness generator says 'given inputs, here's how to compute each signal.' They are separate artifacts. A bug in witness generation doesn't break soundness (the SNARK still proves the R1CS holds), but it does mean the prover can't produce valid witnesses for honest inputs. The <-- operator lets you write witness-only hints (like 'divide by inv'); the <== operator is <-- plus ===. Understanding this split is the mental model that unlocks debugging.
A divider circuit: prove 'I know a and b such that a/b = c (integer division, b ≠ 0).' Witness-gen: c = a / b, r = a mod b. Constraints: a === b * c + r, r < b (range check). The division happens in witness-gen as a hint; the SNARK verifies the multiplication relation.
pragma circom 2.1.6;
include "circomlib/circuits/comparators.circom";
template Divider() {
signal input a;
signal input b;
signal output c;
signal r;
// Witness-gen hint: do the real division
c <-- a \ b;
r <-- a - b * c;
// Constraints: a == b*c + r AND 0 <= r < b
a === b * c + r;
component lt = LessThan(32);
lt.in[0] <== r;
lt.in[1] <== b;
lt.out === 1;
}
component main = Divider();
// Input a=17, b=5 → c=3, r=2. Constraint: 17 = 5*3 + 2 ✓. r=2 < 5 ✓.c <-- a \ b with c <-- a / b (field division). Observe the weird integer results. Field division is not integer division.<-- lines entirely. Compile succeeds, but witness-gen fails with 'cannot compute signal.' The generator needs the hint to know what value to assign.