Open this lesson in your favourite AI. It'll walk you through the why, explain the demo, and quiz you on the try-it list.
Circom is a domain-specific language for writing arithmetic circuits that compile down to R1CS and ultimately to SNARK-friendly representations (consumed by snarkjs for Groth16 and PLONK). Three things it is NOT, and confusing these burns beginners: (1) Circom is not a general-purpose programming language — every variable must be a field element, every operation must be a field-arithmetic operation, and loops must have compile-time bounds. (2) Circom is not Solidity — it doesn't touch the EVM; you compile it, generate a SNARK verifier contract, and THAT contract goes on-chain. (3) Circom does not execute your circuit — it defines the circuit's constraints; a separate witness generator (also generated from your .circom file) computes the actual signal values.
A minimal Circom program: prove 'I know a and b such that a * b = c.' Two input signals (private: a, b), one output signal (public: c), one constraint (c === a * b). Compile with circom, generate witness, set up, prove, verify — the full SNARK pipeline in 15 lines of code + 5 shell commands.
// multiplier.circom — the canonical Circom hello-world
pragma circom 2.1.6;
template Multiplier() {
signal input a;
signal input b;
signal output c;
c <== a * b; // <== is assign-and-constrain (both together)
}
component main = Multiplier();<== with <-- — which is 'assign without constrain.' Generate a witness with a bogus value and observe the SNARK prover rejects it. Internalise: <-- without a matching === constraint is a classic under-constrained bug.a * b with a + b. Compile and run. Verify the output c equals 5+3=8. The template is truly generic — it's the compilation target that changes.Use these three in order. Each builds on the one before.
In one paragraph, explain what Circom is — what it compiles to, what it doesn't execute itself, and how it fits into the snarkjs toolchain.
Walk me through what happens step by step when I run `circom multiplier.circom --r1cs --wasm`: what files are produced, what each represents, and how they're used later in witness-gen and proof-gen.
I'm about to write a 10k-constraint circuit for a zk-rollup batch verifier. Walk me through: which circom constraint idioms blow up constraints, which circomlib helpers I should prefer over rolling my own, and where to watch for soundness bugs that snarkjs won't catch for me.