Line coverage of every crate with a test suite, measured per crate by .github/workflows/coverage.yml (cargo-llvm-cov) on every push to main and published to Codecov under one flag per crate.
| crate | system | library | what it teaches |
|---|---|---|---|
zkp1 |
zk-SNARK (Groth16) | arkworks | what a circuit / R1CS is; setup → prove → verify; the three ZK properties |
zkp2 |
zk-SNARK (Groth16) | arkworks | SNARK half of a matched pair — same statement as zkp3 |
zkp3 |
zk-STARK | winterfell | STARK half of a matched pair — same statement as zkp2 |
zkp4 |
Zcash-style shielded pool | arkworks | notes with hidden values, spend keys, nullifiers, in-circuit balance + range checks |
zkp5 |
Monero-style transactions | curve25519-dalek, bulletproofs | stealth addresses, Pedersen commitments, hand-written CLSAG ring signatures, key images |
zkp6 |
Tornado Cash mixer | arkworks | Merkle-membership withdraw proof, simulated contract with account balances, front-running / double-spend defences |
research |
Rand reference zkVM | Plonky3 (Goldilocks, LogUp, hiding FRI) | RV32I guest programs proved under one universal relation; the guidance circuit for the whole protocol |
rand-guest |
the guest toolchain: build (Rust, C), check, pack, run, info | rustc, clang, rust-lld | one binary from a guest's source to the image the chain deploys |
sbpf2rv |
Solana bytecode (sBPF) → RV32 ahead-of-time translator | sbpf-core, rand-guest |
native execution vs. interpretation for the same ABI; an image that refuses any ELF not loading to its source program, so hc binds the loaded program; a real SPL Token walkthrough, byte for byte against the interpreter |
evm2rv |
EVM (Solidity) bytecode → RV32 ahead-of-time translator | evm-core, evm-rt, rand-guest |
native execution vs. interpretation for the same ABI; an image that refuses any code but its source, so hc binds the code; gas_used is in the Outcome but not bound by public_output, so it matters on chain only through status; a real ERC-20 walkthrough, word for word against the interpreter |
Each crate: cargo run --release for a narrated demo, cargo test --release for the tests —
except the command-line tools rand-guest, sbpf2rv and evm2rv, which have no demo: their READMEs walk
through real runs.
Both prove: "I know a secret x₀ such that iterating x ← x³ + 42 sixty-three times ends at the public value y." Run both and compare the summary boxes. Measured on this machine (Apple Silicon, release build):
| zkp2 · SNARK (Groth16, BN254) | zkp3 · STARK (winterfell, f128) | |
|---|---|---|
| arithmetization | R1CS circuit, 127 gates unrolled | AIR: 64-row trace + 1 transition rule |
| setup | trusted, per-circuit, ~20 ms | none (transparent) |
| proving key | 29 KB | none |
| verifying key | 296 B | none (the AIR source is the "key") |
| commitment scheme | elliptic-curve points + pairings | Merkle trees of Blake3 hashes + FRI |
| assumption | discrete log on a pairing curve | collision-resistant hash |
| post-quantum | no | yes |
| field | 254-bit | 128-bit (chosen for FFT speed) |
| proof size | 128 B, constant | 12.4 KB, grows ~log²(N) |
| prove time | ~9 ms | ~2 ms |
| verify time | ~2 ms, constant | ~0.3 ms, grows ~log(N) |
| zero-knowledge | built in (proofs are randomized) | not in winterfell 0.13 (deterministic proofs) |
Things to notice in the code:
- Where the loop runs. In
zkp2/src/chain.rstheforloop executes at circuit-construction time and emits gates; N is frozen into the proving key. Inzkp3/src/stark.rsthe loop executes at prove time and fills a table; the rule inzkp3/src/air.rsis written once. - The function that only one side has.
zkp2/src/snark.rs::setuphas no counterpart in zkp3. That absence is what "transparent" means. - What the verifier needs. SNARK: verifying key + public inputs + proof. STARK: the AIR type + public inputs + proof + a minimum-security policy.
- Where security comes from. SNARK: the curve's group order, fixed.
STARK:
ProofOptions(queries, blowup, grinding) — tunable, and the demo prints the resulting bits. - Zero-knowledge is not free in STARKs. zkp3's demo shows two proofs of the same statement are byte-identical. Adding ZK means masking the trace with random rows; winterfell does not do this, and neither do StarkNet or Miden in production. Groth16 blinds every proof with two random scalars.
- Same computation, different y. The two crates print different numeric results because they compute mod different primes. Field choice is a first-class design decision in a STARK.
All three simulate a ledger you can watch: deposits, transfers, an observer's view, and a list of attacks the verifier must reject.
| Tornado (zkp6) | Zcash (zkp4) | Monero (zkp5) | |
|---|---|---|---|
| proof system | Groth16 SNARK | Groth16 SNARK | CLSAG ring sig + Bulletproofs |
| circuit size | 827 constraints | 1 679 constraints | no circuit |
| trusted setup | yes | yes | no |
| anonymity set | whole pool | whole pool | ring of 11 per input |
| amounts | fixed denomination | hidden, range-checked in circuit | hidden, Pedersen + range proof |
| ownership | whoever holds the note | spending key, pk = H(sk) | one-time key from stealth addr |
| double-spend tag | nullifier H(ν) | nullifier H(sk, ρ) | key image x·Hp(P) |
| spend proof size | 128 B | 128 B | ~1.5 KB |
| verify time (here) | ~1 ms | ~1 ms | ~3 ms |
Suggested order: zkp6 (simplest), then zkp4 (adds values and keys to the same Merkle circuit), then zkp5 (no circuit at all — see what the SNARK was buying you).
zkp1— learn what a constraint is, and the three phases.zkp2/src/lib.rs— the comparison table, thenchain.rs,snark.rs.zkp3/src/lib.rs— the same table from the other side, thenair.rs,stark.rs.research/README.md— the real thing: a zkVM, and what "confidential arbitrary computation" means concretely.