Zero-Knowledge Proofs
Build zero-knowledge proof circuits with Circom and integrate them into dApps.
Quick Start
// age-check.circom — Prove age >= 18 without revealing age
pragma circom 2.1.0;
include "circomlib/comparators.circom";
template AgeCheck(maxAgeBits) {
signal input age;
signal input threshold;
signal output isAdult;
component gt = GreaterEqThan(maxAgeBits);
gt.in[0] <== age;
gt.in[1] <== threshold;
isAdult <== gt.out;
}
component main { public [threshold] } = AgeCheck(8);
// Generate proof with snarkjs
import { buildPoseidon } from "circomlibjs";
async function generateProof() {
// Compile circuit
// circom age-check.circom --r1cs --wasm --sym
// Generate proving/verification keys
// snarkjs groth16 setup age-check.r1cs pot12_final.ptau circuit.zkey
// Generate witness
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
{ age: 25, threshold: 18 },
"age-check.wasm",
"circuit_final.zkey"
);
// Verify proof
const vKey = JSON.parse(fs.readFileSync("verification_key.json"));
const verified = await snarkjs.groth16.verify(
vKey,
publicSignals,
proof
);
console.log("Proven age >= 18:", verified);
return { proof, publicSignals };
}
Key Concepts
ZK proofs let you prove statements without revealing inputs. Circom defines arithmetic circuits. Groth16 produces small, fast-to-verify proofs. Common uses: private transactions, identity verification, and scalability (zk-rollups).
When to Use
- Privacy-preserving applications (private voting, identity)
- Blockchain scaling (zk-rollups, validiums)
- Verifiable computation (outsource computation with proof)
- Compliance (prove age, KYC, credit score without exposing data)
Step-by-Step
- Author the circuit in Citcom:
circom age-check.circom --r1cs --wasm --symcompiles to constraints, wasm, and symbols. - Generate the powers-of-tau ceremony artifact:
snarkjs powersoftau new bn128 12 pot12_0000.ptauthensnarkjs powersoftau prepare phase2. - Build proving + verification keys:
snarkjs groth16 setup circuit.r1cs pot12_final.ptau circuit.zkey. - Export the verification key and solidity verifier:
snarkjs zkey export verificationkeyandsnarkjs zkey export solidityverifier. - Compute a witness offline with
snarkjs wtns calculatethen generate the proof withgroth16 prove(timed for tester's threshold). - Verify in the dApp: call the on-chain verifier (or
snarkjs groth16 verify) with public inputs + proof.
Examples
// Client-side proof generation for private age check
import { buildPoseidon } from "circomlibjs";
import snarkjs from "snarkjs";
import fs from "fs";
const wc = await snarkjs.wtns.calculate(
{ age: 25, threshold: 18 },
"age-check.wasm",
"witness.wtns"
);
const { proof, publicSignals } = await snarkjs.groth16.prove(
"circuit_final.zkey",
"witness.wtns"
);
const vkey = JSON.parse(fs.readFileSync("verification_key.json", "utf8"));
const ok = await snarkjs.groth16.verify(vkey, publicSignals, proof);
console.log("age >= 18 proven:", ok, "public threshold:", publicSignals[0]);
# On-chain: deploy Verifier.sol, then verify with the packed call data
snarkjs zkey export solidityverifier circuit_final.zkey Verifier.sol
snarkjs generatecall
# paste the returned inputs into `verifier.verifyProof(...)`
Validation
- Circuit compiles with
circomwithout errors - Proof generation completes within acceptable time
- Verification passes for valid proofs, fails for invalid
- Public inputs are correctly revealed, private inputs are hidden