From 'catch the liar' to 'prove you told the truth'
In the previous guide you met the optimistic rollup: a layer 2 where the sequencer posts a new state root and simply asserts it is right. For about seven days, anyone may challenge it by submitting a fraud proof during the challenge period. Picture a referee who lets every goal stand unless someone files a formal protest within the week. It works — but your money is innocent until proven fraudulent, and you wait a week to withdraw to L1.
A ZK-rollup flips the burden of proof. Instead of "assume valid, hope someone catches fraud," it attaches a mathematical receipt to every batch — a validity proof — that the batch was executed correctly. The L1 contract checks the receipt before accepting the new state. No goal counts until the proof verifies; there is no week-long protest window because there is nothing to protest. The sequencer cannot post a bad state and dare you to catch it: a false statement simply has no valid proof.
The deal: a tiny proof of a huge computation
The whole trick rests on an asymmetry. Generating the proof is expensive: a prover re-runs the entire batch inside a proving circuit and does heavy cryptography. But verifying it is cheap and fast — and, crucially, the verification cost is nearly constant no matter how many transactions the batch held. A proof covering 50 transfers and a proof covering 50,000 verify in roughly the same small amount of gas. That constant-cost check is the entire scaling engine: thousands of L2 transactions amortize down to one cheap proof verification on L1.
Real numbers make it concrete. A Groth16 SNARK verifies on Ethereum in roughly 200,000–300,000 gas and is only a few hundred bytes long — whether the batch moved 50 transactions or 50,000. What the prover is actually attesting is one statement: *"starting from the old state root S_old, applying this batch under the protocol rules yields the new state root S_new."* The proof is sound — if the sequencer tried to slip in an invalid transaction (spending coins it doesn't have, an off-by-one balance), no valid proof exists for that false claim, so the L1 verifier would reject it. You stop trusting the sequencer and start trusting the math.
The L1 verifier: where the whole thing bottoms out
On L1, a ZK-rollup ultimately reduces to one contract that stores the current state root and holds a proof verifier. Every batch stands or falls on a single `require` check. Here is the heart of it, stripped to the essentials:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IVerifier {
// Returns true iff `proof` attests that applying the batch
// takes the rollup from `oldRoot` to `newRoot`, and that the
// batch the proof covers hashes to `batchDataHash`.
function verifyProof(
bytes calldata proof,
bytes32 oldRoot,
bytes32 newRoot,
bytes32 batchDataHash
) external view returns (bool);
}
contract ZkRollup {
bytes32 public stateRoot; // the rollup's current state commitment
IVerifier public immutable verifier;
event BatchSettled(bytes32 oldRoot, bytes32 newRoot);
constructor(IVerifier _verifier, bytes32 genesisRoot) {
verifier = _verifier;
stateRoot = genesisRoot;
}
/// Settle one batch. Reverts unless the validity proof checks out.
function submitBatch(
bytes calldata batchData, // the L2 txs, posted for data availability
bytes32 newRoot,
bytes calldata proof
) external {
bytes32 oldRoot = stateRoot;
bytes32 dataHash = keccak256(batchData);
// The ENTIRE security of the rollup is this one line.
require(
verifier.verifyProof(proof, oldRoot, newRoot, dataHash),
"invalid validity proof"
);
stateRoot = newRoot; // final immediately - no challenge window
emit BatchSettled(oldRoot, newRoot);
}
}Two things deserve attention. First, `submitBatch` updates `stateRoot` in the same transaction that verifies the proof — there is no challenge window, so this L2 state is final the moment Ethereum includes the batch (subject only to L1's own finality). Second, `batchData` is still posted on-chain. The proof guarantees the transition is correct, but it does not by itself make the data available: anyone who wants to reconstruct the L2 — to run a node, prove ownership, or force an exit — still needs the raw transactions. That is the data availability problem, and it is the subject of the next guide. Post the proof but withhold the data and you've built a different beast called a validium, which trades the on-chain DA guarantee for cheaper costs.
A batch's life, end to end
- Users sign L2 transactions and send them to the sequencer, which orders them and hands back a soft pre-confirmation almost instantly.
- The sequencer executes the batch off-chain against the current state, producing the new state root S_new. This step is cheap — it is just ordinary execution, no cryptography yet.
- A prover — often a separate, GPU-heavy machine — re-runs the batch inside the proving circuit and generates the validity proof. This is the expensive, slow step: seconds to minutes, and a lot of hardware.
- The sequencer posts three things to L1: the batch's transaction data (for data availability), the new state root, and the proof.
- The L1 verifier contract checks the proof. If it passes, S_new is accepted and final immediately; if it fails, the entire submission reverts and nothing on L1 changes.
- Because state is final the instant it is proven, a withdrawal to L1 can be honored as soon as the batch containing it is proven and verified — no seven-day wait like an optimistic rollup.
Two flavors of proof: SNARKs, STARKs, and what you trust
Two proving families dominate. zk-SNARKs (Groth16, PLONK) give the smallest proofs and cheapest verification, but many need a trusted setup: a one-time ceremony that produces public parameters, whose secret randomness — the "toxic waste" — must be destroyed. The reassuring part is the *1-of-N* trust model: if even a single participant in a powers-of-tau ceremony is honest and deletes their share, the setup is safe; only if all of them colluded and kept it could they forge proofs. PLONK-style systems use one universal setup reusable across circuits; Groth16 needs a fresh setup per circuit.
zk-STARKs (StarkWare's Cairo) need no trusted setup at all — they are transparent, built only from hash functions, which also makes them plausibly post-quantum. The price is bigger proofs that cost more L1 gas to verify (millions of gas, versus a few hundred thousand for a SNARK). A popular compromise wraps a STARK inside a SNARK: you get STARK-style transparent, fast proving, then a final cheap SNARK verification on-chain. There is no free lunch — every choice trades proof size, prover speed, verification gas, and trust assumptions against each other.
The zkEVM challenge, and how to choose
The hard part of a general-purpose ZK-rollup is proving the EVM itself. Early validity rollups dodged this by being application-specific: Loopring and zkSync Lite proved a fixed menu of payment operations; StarkEx proved trading and NFTs for dYdX v3, Immutable X and Sorare. Proving a whole Ethereum-style world-state update under arbitrary smart contracts is far harder — every opcode, every storage write, every keccak has to be expressed inside the circuit. A zkEVM that can do this for general Solidity is the holy grail, and general-purpose zkEVMs only reached mainnet from 2023 onward (Polygon zkEVM, zkSync Era, Scroll, Linea; Starknet via Cairo).
Vitalik's Type 1–4 zkEVM taxonomy captures the central trade-off: EVM-equivalence versus prover speed. A Type 1 zkEVM proves exact Ethereum execution, perfectly equivalent but the slowest and most expensive to prove. Type 2 (e.g. Scroll, Polygon zkEVM) is EVM-equivalent at the bytecode level with minor internal tweaks. Type 4 compiles high-level Solidity down to a ZK-friendly VM (Starknet's Cairo, zkSync Era's approach) — the fastest to prove, but the least bytecode-compatible, so some tooling and edge-case contract behavior differs. The closer you sit to raw Ethereum, the heavier the prover you must run.
Set against the optimistic rollup from the previous guide, the trade-offs line up cleanly. Withdrawals — ZK: minutes-to-hours once proven; optimistic: ~7 days. Security — ZK: cryptographic, with no need for a watchful honest party, but carrying circuit and trusted-setup risk; optimistic: needs at least one honest watcher to challenge within the window, plus a censorship-resistant L1 so the fraud proof can land. Cost & maturity — ZK: heavy proving, harder EVM compatibility, newer; optimistic: cheap, simple, battle-tested earlier. And a point that catches people out: both still post their data to L1 — neither escapes data availability, which is the real, shared bottleneck the next guide tackles.