Zero-Knowledge Proofs
"I can prove I know x without revealing x." That sentence became a multi-billion-dollar industry. Here's the engineer's view — enough to build, without the PhD.
Goal — build a Circom circuit, generate and verify a zk-SNARK, and know when to reach for one in production.
1. What ZK actually gives you
- Completeness — honest prover always convinces verifier.
- Soundness — cheating prover almost never convinces verifier.
- Zero-knowledge — verifier learns nothing except "the statement is true".
- Succinctness (SNARK) — proof is tiny (~200 bytes) and fast to verify, regardless of computation size.
Analogy — a hash lets you prove you know a preimage by revealing it. A ZK proof lets you prove you know a preimage without revealing it. That's the leap.
2. Classic Ali Baba's cave intuition
Cave with two paths (A, B) meeting at a magic door.
Peggy knows the password. Victor doesn't.
Victor waits outside. Peggy enters a random path.
Victor shouts "come out via A!" (random).
If Peggy knows password → she can always comply (p=1).
If she doesn't → she's right 50% of the time; after 20 rounds → 1 in 10⁶.
Victor is convinced without ever learning the password.
3. SNARK vs STARK
| SNARK (Groth16, PLONK) | STARK | |
|---|---|---|
| Proof size | ~200 bytes | ~100 KB |
| Verifier | Constant, very fast | Larger, still fast |
| Trusted setup? | Yes (Groth16) / universal (PLONK) | No |
| Post-quantum? | No (pairing-based) | Yes (hash-based) |
| Used by | zkSync, Polygon zkEVM | Starknet |
4. The pipeline
1. Write a circuit (Circom, Noir, Halo2)
2. Compile → R1CS / arithmetic circuit
3. Trusted setup (or universal SRS) → proving key, verifying key
4. Prover: inputs + witness → proof
5. Verifier: proof + public inputs → true/false
6. On-chain: the verifier is a Solidity contract auto-generated from vkey
5. A tiny Circom circuit — prove you know a preimage
// preimage.circom
pragma circom 2.1.6;
include "circomlib/poseidon.circom";
template Preimage() {
signal input secret; // private
signal input hash; // public
signal output ok;
component h = Poseidon(1);
h.inputs[0] <== secret;
hash === h.out; // constraint: provided hash must match
ok <== 1;
}
component main { public [hash] } = Preimage();
# toolchain
npm i -g circom snarkjs
circom preimage.circom --r1cs --wasm --sym
# trusted setup (Powers-of-Tau, one-time, shared)
snarkjs powersoftau new bn128 12 pot12_0000.ptau
snarkjs powersoftau contribute pot12_0000.ptau pot12.ptau --name="me"
snarkjs powersoftau prepare phase2 pot12.ptau pot12_final.ptau
# circuit-specific
snarkjs groth16 setup preimage.r1cs pot12_final.ptau pre_0000.zkey
snarkjs zkey contribute pre_0000.zkey pre_final.zkey --name="me"
snarkjs zkey export verificationkey pre_final.zkey vkey.json
# prove
node generate_witness.js preimage_js/preimage.wasm input.json witness.wtns
snarkjs groth16 prove pre_final.zkey witness.wtns proof.json public.json
# verify (off-chain)
snarkjs groth16 verify vkey.json public.json proof.json
# export Solidity verifier for on-chain
snarkjs zkey export solidityverifier pre_final.zkey Verifier.sol
6. What "trusted setup" means
Groth16 needs a one-time ceremony where participants contribute randomness; as long as one participant is honest and destroys their secret, the setup is safe. Ethereum's KZG ceremony for EIP-4844 had ~140k contributors.
7. Real-world ZK use cases
- zk-rollups — one proof per batch of 10k txs.
- Private tx (Tornado Cash, Aztec) — deposit → later withdraw to a different address with a proof of ownership.
- zk-Identity / Sybil resistance (Worldcoin, Semaphore) — prove you're a unique human without revealing who.
- zk-Login — prove you hold a JWT from Google without revealing the JWT.
- zk-ML — prove a model produced a given output without revealing weights.
8. The cost model
- Circuit size (# constraints) dominates prover time. Keccak ≈ 150k constraints; Poseidon ≈ 300.
- Prefer ZK-friendly hashes (Poseidon, MiMC) inside circuits.
- Proving is slow (seconds to minutes). Verification is fast and cheap on-chain (~200k gas for Groth16).
Heuristic — if your computation is "the same 10 lines over 10,000 inputs", ZK is a win. If it's "1000 lines of branching business logic", it isn't yet.
9. Alternatives to learn alongside
- Noir (Aztec) — Rust-like syntax, better ergonomics than Circom.
- Halo2 (Zcash/ECC) — Rust, no trusted setup, used by Scroll / Taiko.
- RISC Zero / SP1 — write in Rust/Go, compile to zkVM proof. Lets you ZK-prove arbitrary programs at a gas cost.
10. Project
Deliverable — build a private airdrop: a Merkle tree of eligible addresses. Eligible users submit a zk-proof of "I know a leaf and a Merkle path to this public root" to claim, without revealing which address. Circom + Solidity verifier + a React claim UI.
Quiz
Q. You have a legitimate preimage x, and want to prove
hash = H(x) to a contract cheaply. Is ZK the right tool?- Yes — smaller proof than revealing x
- Only if you need x to stay private; otherwise just submit x and let the contract hash it (cheaper)
- No — ZK can't prove hashes
- Depends on gas price
ZK is about privacy/compression of computation, not a default. If the input is already public, a plain hash check is trivially cheaper.