Chapter 40: Quantum threats to ZK rollups
A ZK rollup verifier contract is a smart contract on Ethereum L1 that checks a cryptographic proof of correct execution of an off-chain rollup state transition. The verifier reads a proof byte string from calldata, runs the verifier algorithm in EVM bytecode, and emits a single bit: accept or reject. Under post-quantum primitives, the verifier-contract decomposes into four layers (L1 arithmetization, L2 commitment + consistency, L3 protocol logic, L4 non-interactivity + extraction) per the Ch 31 framework, and the quantum-sensitive primitive exposure is dominated by L2 and L4. The operator’s question is which layer to migrate first, what the per-proof gas-cost impact looks like across candidate configurations, and how the upgradeable-verifier-contract choreography on Ethereum gates the rollout.
Covered in Ch 37, Ch 38, and Ch 39: the per-spend transaction surface, the wallet-and-derivation surface, and the consensus-signature surface. The on-chain-verifier surface from the Ch 36 surface taxonomy is this chapter’s target. Ch 35 placed Zcash, ZKsync Era, and Starknet on the post-quantum bit-margin grid. This chapter picks up where Ch 35 leaves off and walks the operator from a placement to a deployment. Ch 41 covers the chain-governance choreography for the hard-fork activation block; this chapter covers the verifier-contract upgrade choreography that sits inside it.
A Strand on-chain-verifier surface migrating from SHA-256 inside a STARK to a wider hash
Section titled “A Strand on-chain-verifier surface migrating from SHA-256 inside a STARK to a wider hash”The Strand on-chain-verifier surface from the Ch 36 fixture deploys SHA-256 inside a FRI-based STARK at years (verifier parameter-set reuse window) and years (verifier-upgrade rollout window). The verifier contract sits at a single Ethereum L1 address. On each rollup cycle (one to two hours at chain-tip 2026), the rollup operator commits a proof to L1 and the verifier runs once per commitment. Under the Ch 36 strict-inequality framing, . The surface clears under any and breaches under .
The motivating example: a Strand operator considers migrating the L4 Fiat-Shamir hash from SHA-256 to a wider transcript hash. SHA-256 is an EVM precompile (address 0x02), and Keccak-256 is the native EVM opcode (0x20). SHAKE-256 has no precompile or native opcode at chain-tip 2026. A SHAKE-256-based verifier therefore either pays a bytecode-implementation tax for an in-EVM Keccak-f[1600] permutation or waits for a future EIP that introduces a SHAKE precompile. The pragmatic on-chain wider-output construction is a multi-pass Keccak-256 transcript using the native opcode. The chapter’s “wider-hash” line uses that pragmatic construction as the illustrative configuration.
The legacy SHA-256 STARK verifier in this chapter runs at roughly five million gas per proof on Ethereum L1. The figure is an illustrative pedagogical anchor, not a measured benchmark from a primary source. A wider-hash STARK at the multi-pass Keccak construction produces longer Fiat-Shamir transcripts and roughly thirty percent more on-chain hashing per proof under this illustrative sensitivity, putting the per-proof verifier cost at roughly six and a half million gas. A SHAKE-256 verifier implemented in EVM bytecode without precompile support would carry a much larger penalty, possibly an order of magnitude above the legacy baseline.
Three separate budget lines constrain that figure, and they are easy to conflate. Ethereum’s block gas limit in this chapter’s model is sixty million gas per block, the EIP-7935 execution-layer (EL) client-default coordination target (Ethereum Improvement Proposal authors, 2025) (Informational, written for the Fusaka cycle), the anchor used in Ch 37; the EIP recommends a client default and records no block-header measurement. This is a validator-and-client-default regime rather than a fixed protocol constant. The EIP-1559 base-fee mechanism targets half the limit per block, so sustained-throughput planning compares against the thirty-million target rather than the sixty-million ceiling. Fusaka, activated on mainnet on 3 December 2025, also carried EIP-7825 (Transaction Gas Limit Cap) in its inclusion list (Beiko et al., 2024; Ethereum Improvement Proposal authors, 2025a), which rejects any transaction whose gas limit exceeds gas, independent of the block gas limit.
| Budget line | Legacy, 5M per proof | Wider-hash, 6.5M per proof |
|---|---|---|
| 60M block ceiling (EIP-7935) | 12 proofs | 9 proofs |
| 30M sustained target (EIP-1559) | 6 proofs | 4 proofs |
| 16.78M per-transaction cap (EIP-7825) | clears | clears |
The per-transaction cap binds in both mempool admission and block validation, and it is the line that rules a configuration out rather than merely making it expensive. None of the illustrative configurations breaches it on a single verifier call: legacy at 5M, wider-hash at 6.5M, and the recursive outer proof at 7M all sit well under 16.78M. A bytecode-implemented SHAKE-256 verifier carrying an order-of-magnitude penalty against the legacy baseline would not fit in a single transaction at all. In that regime SHAKE-256 is not merely expensive: it is not deployable as a single verifier call without splitting the verification flow across multiple transactions, changing the proof shape, or waiting for a native SHAKE precompile.
The four-part operator playbook translates this gas figure into four decisions. The first decomposes the verifier into the four layers and picks a per-layer candidate. The second quantifies the per-proof gas-cost change against three candidate configurations. The third plans the verifier-upgrade cadence against the Mosca window. The fourth choreographs the Ethereum upgradeable-verifier-contract upgrade.
The Ch 31 four-layer decomposition under post-quantum primitives
Section titled “The Ch 31 four-layer decomposition under post-quantum primitives”The four-layer decomposition from Ch 31 names L1 (arithmetization), L2 (commitment plus consistency), L3 (protocol logic, the IOP), and L4 (non-interactivity plus extraction, the Fiat-Shamir transform). Each layer contributes a binding term to the soundness-error bound. Each layer carries its own quantum-attack story. For an on-chain ZK rollup verifier, the L1 arithmetization runs off-chain in the prover. L2 (the commitment-and-consistency check) and L4 (the Fiat-Shamir transform) carry the post-quantum primitives that run on-chain. L3 (the protocol logic) is mostly off-chain in the prover, but the verifier contract still encodes the L3-side verifier algorithm: the verification key, parameter constants, opening-point selection, and consistency checks against the L2 opening proofs. The quantum-sensitive primitive exposure is dominated by L2 and L4. The L3 verifier-side logic touches the chain through gas cost rather than through a primitive swap.
Figure 40.1 below decomposes the verifier contract into the four Ch 31 layers with per-layer post-quantum status. The operator’s first migration move follows from the L2 and L4 cells.
L2 carries the first of the two on-chain checks. Under a polynomial commitment scheme (PCS), the prover commits to its arithmetized witness off-chain and the verifier checks an opening proof on-chain at one or more challenge points. Ch 32 walks the four PCS families. KZG is pairing-based and Shor-broken on the d-strong Diffie-Hellman assumption (Kate et al., 2010). Merkle binding reduces to hash collision resistance (Merkle, 1989). FRI is Merkle plus the Fast Reed-Solomon Interactive Oracle Proof of Proximity (Ben-Sasson et al., 2018). Lattice-based PCS candidates are typically built on SIS / Module-SIS-style assumptions. They have no Shor-style polynomial-time quantum break, but concrete security still depends on lattice-estimator assumptions and known classical / quantum lattice attack costs. The first three are deployed in production rollup verifiers at chain-tip 2026; the fourth is research-grade.
L4 carries the second on-chain-exposed layer. The Fiat-Shamir transform (Fiat & Shamir, 1987) rewrites a public-coin interactive protocol into a non-interactive protocol by deriving the verifier’s challenges from a hash of the prover’s transcript so far. The Fiat-Shamir hash is not merely a collision-resistant transcript compressor. In the random-oracle model it stands in for the verifier’s public-coin randomness. The soundness loss depends on the protocol shape, the challenge space, the round count, and the adversary’s oracle queries. A hash collision is sufficient but not necessary for an attack. The QROM reduction governs the concrete post-quantum security loss.
Ch 33 walks the QROM analysis, and it sorts deployed systems into three tiers. The three-move case has a tight QROM reduction with loss factor on the interactive soundness error, which reads at , DFMS19’s reduction with the residue-free coefficient of DFMS20’s Theorem 2 (Don et al., 2019, 2020). The multi-round case has a QROM reduction, DFMS20’s Corollary 13, whose loss is on the interactive soundness error plus an additive term; Ch 33 prices it with a stipulated model that keeps and drops the additive term, so at is a model figure rather than a certified bound (Don et al., 2020). The FRI-based SNARK case carries an asymptotic QROM Fiat-Shamir bound, inherited via the Ben-Sasson-Chiesa-Spooner state-restoration lift from FRI’s round-by-round soundness (Block et al., 2023), plus a concrete-parameter audit at deployed settings that sits in the classical ROM rather than the QROM (Block & Tiwari, 2024). The deployment-parameter QROM accounting is open in the literature. Widening the transcript hash output is therefore a conservative engineering move rather than a complete proof of post-quantum soundness at deployed FRI parameters.
The on-chain L4 hash candidate set divides into two groups by EVM cost. The native group has direct EVM-opcode or fixed-address-precompile support. SHA-256 sits at precompile 0x02, 256-bit output, ~85 bits of post-quantum collision resistance under the BHT bound, a query count that assumes about entries of quantum-accessible memory (Brassard et al., 1998). Keccak-256 is the native KECCAK256 opcode 0x20, using the pre-standard Keccak padding rather than the finalized FIPS SHA3-256 padding, with the same capacity-derived collision-resistance margin against the generic-query quantum-collision bound as SHA3-256. The non-native group has no precompile. SHAKE-128 and SHAKE-256 are the SHA-3 extendable-output functions with named 128- and 256-bit security strengths and caller-chosen output lengths. A 512-bit SHAKE-256 output gives ~170 bits of post-quantum collision resistance under the BHT bound, the conservative-assumption target; a 256-bit SHAKE-128 output gives roughly the same ~85 bits as SHA-256.
Realizing the non-native group on-chain requires a bytecode implementation of the Keccak-f[1600] permutation (an order-of-magnitude gas tax against the native group) or a future EIP that introduces a SHAKE precompile. The pragmatic wider-output construction is a multi-pass Keccak-256 using the native opcode rather than a bytecode-implemented SHAKE-256. The multi-pass construction widens a 256-bit output to a 512-bit transcript via distinct domain-separation counter bytes per pass (for example, Keccak(transcript ‖ 0x00) and Keccak(transcript ‖ 0x01) for the two halves), giving two independent 256-bit chunks that concatenate to a 512-bit transcript without coupling.
Field hashes like Poseidon and Rescue-Prime are excluded from the on-chain L4 candidate set: those are designed for in-circuit prover-side transcripts, not for the on-chain Fiat-Shamir hash a verifier-contract opcode evaluates.
L1 and L3 carry off-chain candidate sets that the chapter records for completeness but does not bind on-chain. L1 candidates: AIR (the STARK-style algebraic intermediate representation (Ben-Sasson et al., 2018b, sec. 2.2)), R1CS (the rank-1 constraint system used in Groth16 (Groth, 2016) and Aurora (Ben-Sasson et al., 2019)), Plonkish (the PLONK gate set, deployed in ZKsync Era under the Boojum upgrade (ZKsync, 2023)). L3 candidates: Sumcheck-IOP (information-theoretic at the IOP layer), FRI-IOP (FRI proximity layered over Merkle commitment), IPA-IOP (inner-product-argument IOP, Shor-broken because IPA soundness reduces to discrete log (Bowe et al., 2019; Bünz et al., 2018)).
The four-part verifier-contract migration playbook
Section titled “The four-part verifier-contract migration playbook”Decompose the verifier into layers and pick a per-layer candidate
Section titled “Decompose the verifier into layers and pick a per-layer candidate”The Strand operator decomposes the verifier into the four layers and picks a per-layer candidate based on the post-quantum status. The first inline block walks the per-cell decision over a representative slice of the matrix. The function below is a pedagogical slice of zk_rollups.verifier_layers.lookup over the four layers and a small candidate set per layer.
# Block 1: pedagogical slice of zk_rollups.verifier_layers# (stdlib only).LAYERS = ( "L1-arithmetization", "L2-commitment", "L3-protocol-logic", "L4-fiat-shamir",)
# Per-cell decision: post-quantum status per (layer, candidate).# off-chain means the layer's work runs in the prover and the layer# carries no primitive the verifier contract can swap. It does not# mean the contract runs none of that layer: the L3 verifier# algorithm, its verification key, parameter constants and the# consistency checks against the L2 openings all execute on chain.MATRIX = { "L1-arithmetization": { "AIR": "off-chain", "Plonkish": "off-chain", }, "L2-commitment": { "KZG": "shor-broken", "FRI": "grover-weakened", "lattice-PCS": "pq-secure", }, "L3-protocol-logic": { "FRI-IOP": "off-chain", "IPA-IOP": "shor-broken", }, "L4-fiat-shamir": { "SHA-256": "grover-weakened", "SHAKE-256": "grover-weakened", "Keccak-256": "grover-weakened", },}
def lookup(layer, candidate): assert layer in LAYERS assert candidate in MATRIX[layer] return MATRIX[layer][candidate]
# Print the on-chain layers (L2 and L4) for a small# candidate set. L1 and L3 carry no primitive-swap decision of# their own, which is the only decision this table records.print(f"{'layer':<19} {'candidate':<14} pq_status")for layer in ("L2-commitment", "L4-fiat-shamir"): for candidate in MATRIX[layer]: status = lookup(layer, candidate) print(f"{layer:<19} {candidate:<14} {status}")# ==> layer candidate pq_status# ==> L2-commitment KZG shor-broken# ==> L2-commitment FRI grover-weakened# ==> L2-commitment lattice-PCS pq-secure# ==> L4-fiat-shamir SHA-256 grover-weakened# ==> L4-fiat-shamir SHAKE-256 grover-weakened# ==> L4-fiat-shamir Keccak-256 grover-weakenedEvery Python block this chapter prints is also a standalone file
in the companion repository, under chapter-code/ch40/, one file per block.
Appendix C covers the clone and
the environment they run on.
The block records the operator’s decision. At L2, KZG falls under Shor through pairing discrete log (Kate et al., 2010; Shor, 1994). FRI itself is an information-theoretic proximity test. The post-quantum degradation enters through the Merkle-commitment hash layer FRI binds. Grover-style preimage search gives roughly square-root speedups; BHT-style quantum collision search gives roughly cube-root query complexity for ideal collision finding, with quantum-accessible memory on the same scale (Brassard et al., 1998). Lattice-PCS, like the hash-based FRI and Merkle constructions, has no known polynomial-time quantum attack. The difference between the two post-quantum families is in what the quantum adversary does get: against lattices the best result is sieving, which trims the core-SVP exponent constant from 0.292 β to 0.265 β (Ch 13). Against hashes it is the generic Grover and BHT speedups above. Both are absorbed by parameters rather than by a structural change, which is what separates them from the pairing-based options, where Shor is structural. It is not broadly production-deployed in a rollup verifier and sits in the research-to-early-adoption category rather than the immediately-deployable group.
At L4, every available on-chain hash candidate is weakened by generic quantum search. Grover brings preimage search from about to about queries, and BHT brings collision search from the classical birthday cost of about to about queries with about entries of quantum-accessible memory, an exponent of rather than a cube root of the classical cost. Without that memory, CNPS reaches about time with qubits and about bits of classical storage (Brassard et al., 1998; Chailloux et al., 2017). The migration question at L4 is the effective transcript-output width and the EVM cost, not the family. A 512-bit transcript output gives roughly 170 bits of post-quantum collision resistance under the BHT bound, up from roughly 85 bits at a 256-bit transcript. The bit-margin math is a generic-query heuristic, not a full soundness theorem for deployed STARK parameters. The SHA-256 versus Keccak-256 decision sits below in the tradeoff table. Both are 256-bit outputs at the same post-quantum collision-resistance margin and differ on EVM cost and on transcript-padding conventions.
The deployed production systems at chain-tip 2026 sit at L2=FRI or FRI-plus-pairing-wrapper, with L4 in the SHA-2 / SHA-3 / Keccak family at specifics that vary across rollups and across upgrade cycles. ZKsync Era runs L1=Plonkish with a FRI-based inner proof under the Boojum proof system, announced in 2023 (ZKsync, 2023) and documented as the deployed system on L2Beat’s Era page on 15 September 2026 (L2BEAT, 2026). Public ZKsync documentation supports the operational picture that the L1 verification path includes SNARK-style verifier components around the Boojum-era inner proof. Under that framing the outer L2 cell sits in the pairing-based group, and the operator’s first migration move is to retire the outer pairing wrapper.
Starknet’s deployed path has two parameter points composed on one route to Ethereum, as Ch 34 and Ch 35 record. The Stwo (Circle STARK) prover, on Starknet mainnet since late October 2025 (L2Beat’s milestone log records 2025-10-19 (L2BEAT, 2026a), and Starknet announced it on 2025-11-03 (StarkWare, 2025)), proves the leaves: L1=AIR over the Mersenne-31 prime field, L2=Circle FRI. SHARP verifies those proofs off-chain with a Cairo verifier program and aggregates them recursively, and the recursion root is still proved by Stone, StarkWare’s first-generation prover, over the 252-bit Cairo field rather than the 61-bit base field of the ethSTARK reference configuration (Ben-Sasson, 2021, sec. 5.10), because that is the proof the Solidity verifier on Ethereum reads (StarkWare, 2025a). The transcript hash differs by surface, and none of them is SHA-256. The ethSTARK document specifies BLAKE2s for its reference measurements. The Solidity source StarkWare publishes for the Ethereum verifier draws its channel randomness with Keccak-256, the EVM’s native hash, in its Prng and VerifierChannel contracts (StarkWare, 2026). The SHA-256 transcript this chapter and the Ch 34 toy use is the book’s own pedagogical choice and no deployed Starknet channel. StarkWare announced on 31 March 2026 that the L1 verifier will be changed to verify an S-two circuit proof. On 7 September 2026 no public record of that change shipping was found, so this inventory is the last publicly documented state. The migration lesson is the same shape regardless. Starknet runs FRI-variant proximity testing all the way down. The first migration move at L4 is a wider transcript output, held against the current specification, and a wider hash does not by itself widen the challenge field the QROM rule sizes, which for the Stone root is the 252-bit field Ch 35 describes.
Starknet has since begun executing a published migration, on a different surface. StarkWare’s quantum-readiness roadmap replaces Pedersen hashing with BLAKE2 (StarkWare, 2026b), and Starknet v0.14.3 carries that change for the operating-system program hash and configuration hash, with mainnet dated 6 July 2026 in its prerelease notes and no after-the-fact deployment record found (StarkWare, 2026b). The contract-address and trie changes remain outstanding. That work sits at the chain’s state-commitment and addressing layer rather than at the L4 Fiat-Shamir transcript this playbook targets, so it does not settle the verifier-contract question. It is evidence of the sequencing this chapter argues for: an operator running the decomposition migrates one surface at a time.
Quantify the per-proof gas-cost change
Section titled “Quantify the per-proof gas-cost change”The second decision is the per-proof gas-cost change under the candidate verifier configuration. The second inline block walks the gas arithmetic across three configurations: legacy SHA-256 STARK, wider-hash STARK using the multi-pass Keccak illustrative construction, and a recursive STARK wrapper that amortizes across an inner-proof batch. The function below is a pedagogical slice of zk_rollups.gas_budget.evaluate.
# Block 2: pedagogical slice of zk_rollups.gas_budget# (stdlib only).ETH_BLOCK_GAS_LIMIT = 60_000_000 # EIP-7935 EL-client default; Fusaka, December 2025.
# Per-proof gas anchors at chain-tip 2026 (ethSTARK-shaped). All three# are illustrative pedagogical anchors, not measured benchmarks from a# primary source. The block gas limit above is a scenario too, matching# the 60M execution-layer client default EIP-7935 recommends, which is# a validator-and-client-default regime and not a fixed protocol constant.GAS_PER_PROOF = { "legacy-sha256-stark": 5_000_000, "wider-hash-stark": 6_500_000, "recursive-outer": 7_000_000,}RECURSIVE_BATCH = 100 # inner proofs per outer recursive proof.
def per_effective_proof_gas(config): if config == "recursive-stark-wrapper": return GAS_PER_PROOF["recursive-outer"] // RECURSIVE_BATCH return GAS_PER_PROOF[config]
def proofs_per_block_max(config): if config == "recursive-stark-wrapper": outer = ETH_BLOCK_GAS_LIMIT // GAS_PER_PROOF["recursive-outer"] return outer * RECURSIVE_BATCH return ETH_BLOCK_GAS_LIMIT // GAS_PER_PROOF[config]
CONFIGS = ( "legacy-sha256-stark", "wider-hash-stark", "recursive-stark-wrapper",)
legacy_gas = GAS_PER_PROOF["legacy-sha256-stark"]print(f"{'configuration':<26} {'gas/proof':>10} {'factor':>8} {'max/block':>10}")for config in CONFIGS: gas = per_effective_proof_gas(config) factor = gas / legacy_gas max_per_block = proofs_per_block_max(config) print(f"{config:<26} {gas:>10,} {factor:>8.3f} {max_per_block:>10}")# ==> configuration gas/proof factor max/block# ==> legacy-sha256-stark 5,000,000 1.000 12# ==> wider-hash-stark 6,500,000 1.300 9# ==> recursive-stark-wrapper 70,000 0.014 800The legacy configuration sits at roughly five million gas per proof: an illustrative pedagogical anchor, not a measured benchmark and not bound to any specific deployed verifier. The wider-hash configuration adds roughly thirty percent on top of legacy under the multi-pass Keccak illustrative sensitivity. The per-proof gas rises to six and a half million and the per-block proof count drops from twelve to nine. A bytecode-implemented SHAKE-256 without precompile support would land much higher than the thirty-percent figure (an order of magnitude is plausible). The thirty-percent number is a sensitivity case, not a measured benchmark. The recursive-wrapper configuration runs an outer recursive STARK at roughly seven million gas per outer proof. The wrapper verifies a batch of one hundred inner proofs per outer proof. The per-effective-proof gas comes out at seventy thousand and the per-block effective-proof count at eight hundred at the sixty-million gas-limit anchor.
Figure 40.2 below compares per-proof gas across three candidate verifier configurations. Bar lengths render proportional to per-effective-proof gas for the legacy and wider-hash configurations. The recursive bar at 70 K gas falls below one pixel at the chosen scale and is shown at minimum visible width with the gas figure annotated. The Ethereum gas-limit ceiling at sixty million (EIP-7935 EL-client default, Fusaka cycle) is annotated as the per-block ceiling.
The recursive figure is the one the operator plans against. The Strand fixture treats one hundred state-transition proofs per L1 commitment cycle as a pedagogical anchor for the per-cycle arithmetic. Under the legacy configuration the rollup spends roughly five hundred million gas per cycle, about eight full blocks at the sixty-million gas-limit ceiling. Under the recursive wrapper the rollup spends one outer-proof verifier invocation per cycle: seven million gas, about an eighth of one block. The amortization factor is the difference between competing for eight full blocks and competing for one-eighth of one block.
The per-proof gas figures are illustrative anchors, not precise benchmark numbers. Any production rollup operator measures against its own deployed verifier. Production verifier-side gas figures are observable on Etherscan against the deployed verifier-contract addresses for ZKsync Era and Starknet. Prover-side timing figures sit in each rollup’s published prover-time benchmarks rather than in the post-quantum signature-scheme benchmarks from earlier parts of the book.
Plan the verifier-upgrade cadence
Section titled “Plan the verifier-upgrade cadence”The third decision is the verifier-upgrade cadence against the Mosca window. The Strand on-chain-verifier surface from Ch 36 sits at and , so the signed margin is and the safe window is . The cadence functions below work with that signed margin. Ch 36’s exposure window is its positive part, so a negative margin is years of clearance, not a breach. The rotation the cadence counts is a verifier parameter or proof-system change. Redeploying the same bytecode restarts nothing, because the hash’s post-quantum margin is a property of the parameter set, not of the deployment’s age.
| Scenario | Margin | Safe window | |
|---|---|---|---|
| narrow | 4 | year | 2 years |
| central | 9 | years | 7 years |
| wide | 13 | years | 11 years |
The narrow scenario is the boundary case, and it is the only one of the three that breaches. With , the strict-inequality threshold sits at . This differs from the Ch 39 narrow anchor at because the boundary tracks each surface’s own sum, and the Ch 39 consensus surface has .
The central and wide scenarios both clear the surface. Central at is the value Ch 36 assigns to the two 2035 policy dates, NCSC and NSM-10. Wide at sits one year below Ch 36’s mid-2040 scenario () and gives a clearly-cleared case for the on-chain-verifier surface.
The third inline block runs the recommendation function over the three named scenarios. The function below is a pedagogical slice of evaluate_named_scenario in zk_rollups.verifier_mosca.
# Block 3: pedagogical slice of zk_rollups.verifier_mosca# (stdlib only).def breach_years(X, Y, Z): return X + Y - Z
def recommend(X, Y, Z): breach = breach_years(X, Y, Z) safe_window = max(0, Z - Y) # The second tuple element is the rotation interval the # recommendation prescribes. governance-trigger and # hard-fork-trigger are event-driven and carry interval 0. if breach <= 0: return ("governance-trigger", 0) if safe_window >= 1: return ("every-N-rollup-cycles", safe_window) return ("hard-fork-trigger", 0)
# Strand on-chain-verifier surface from Ch 36: X=3, Y=2.X, Y = 3, 2SCENARIOS = ( ("narrow Z=4", 4), ("central Z=9", 9), ("wide Z=13", 13),)for label, Z in SCENARIOS: cadence, interval = recommend(X, Y, Z) breach = breach_years(X, Y, Z) safe_window = max(0, Z - Y) print(f"{label}: breach={breach:>3}y -> {cadence}, interval={interval}y, safe_window={safe_window}y")# ==> narrow Z=4: breach= 1y -> every-N-rollup-cycles, interval=2y, safe_window=2y# ==> central Z=9: breach= -4y -> governance-trigger, interval=0y, safe_window=7y# ==> wide Z=13: breach= -8y -> governance-trigger, interval=0y, safe_window=11yUnder the narrow scenario, admits a two-year rotation interval. Treating the rotation interval as the effective parameter-set reuse window (), the strict-inequality check reads , which is false. The breach clears at the boundary per the Ch 36 strict-inequality framing. The operator picks every-N-rollup-cycles with a per-cycle interval that totals two years of wall-clock time (at one rollup cycle per hour, that is roughly seventeen thousand five hundred cycles). Under the central and wide scenarios, no breach exists. The operator rotates on a governance-trigger cadence. The governance window itself runs longer than the rotation interval the Mosca arithmetic implies, so the cadence is set by the governance council rather than by the inequality.
The per-rollup-cycle cadence is technically feasible (change the verifier’s parameters every cycle) but operationally prohibitive: every cycle would require a full propose-audit-multisig-approve-activate-transition choreography. The package records it as the zero-interval lower bound, the shortest schedule the model can name, and never recommends it; the zero is the rotation interval, not the overhead, which is exactly what makes the cost prohibitive. The hard-fork-trigger cadence is the always-feasible fallback for the case , where no calendar-time rotation interval exists. The operator coordinates the verifier rotation with an L1 hard-fork event.
The Ethereum upgradeable verifier-contract choreography
Section titled “The Ethereum upgradeable verifier-contract choreography”The verifier-contract upgrade choreography on an upgradeable rollup runs in five phases. The first is propose: a proposer (the rollup core team or a governance member) submits the new verifier-contract bytecode to a public proposal queue, alongside the proof system spec change (the candidate configuration the rollup is migrating to). The second is audit: an external auditor reviews the bytecode for soundness bugs, gas-cost regressions, and alignment with the spec. The audit window runs days to weeks. The rollup operator funds it under the protocol treasury.
The third is multisig-approve: a governance signer set approves the activation transaction. Approval thresholds at chain-tip 2026 are protocol-specific rather than a uniform multisig of the same shape across rollups. ZKsync Era’s expedited upgrade path requires all three of its Security Council, its Guardians and the ZKsync Foundation to reach their multisig thresholds, nine, five and three signers, while an ordinary protocol change goes through the Token Assembly with the Security Council’s technical review and the Guardians’ veto window (ZKsync Association, 2025). The Token Assembly renamed that path from Emergency Upgrades to Instant Upgrades on 24 August 2026, sorting each one as a security patch or an emergency response and leaving the authority, the thresholds and the execution procedures unchanged; the governance procedures themselves still carry the older name (Matter Labs, 2026). Starknet’s path, as L2BEAT records it for the contracts at the read date, puts a regular upgrade of the core contract behind a 2-of-6 multisig with an eight-day delay and of the shared SHARP verifier behind a 2-of-4 multisig with the same delay, and gives a 9-of-12 Security Council an upgrade path with no delay (L2BEAT, 2026a). The council’s published framework states its emergency threshold as 75 percent and its regular path as a seven-day delay after security vetting, applied first to the staking contracts with core infrastructure as a later phase (StarkWare, 2024). The Strand fictional fixture stands in for either pattern. The operator-side decisions per phase keep the same shape regardless of the specific signer set.
The fourth is activate: the activation transaction lands on L1 and the verifier-contract proxy points the bytecode pointer at the new verifier. The fifth is transition window: the legacy verifier and the new verifier may run in parallel during the transition window, with the rollup committing proofs under the old configuration on one queue and the new configuration on a second queue. The operator-side decisions run per phase. Propose decides which candidate configuration. Audit decides on the audit budget and timeline. Multisig-approve coordinates the signer set. Activate decides the activation block. Transition decides how long the legacy verifier accepts proofs.
The rollback path follows the same governance route as the original upgrade unless the protocol carries an emergency-upgrade authority. For production rollups, the emergency-path threshold, the delay period, and the pause authority are part of the migration risk model. In the non-emergency case the rollback runs as a second propose-audit-multisig-approve cycle that returns the verifier-contract proxy to the legacy bytecode. A misfired upgrade (a soundness bug in the new verifier, a gas-cost regression that breaks the rollup’s per-cycle proof volume) burns operator funds during the time between activation and rollback. The mitigation is to fund the audit phase generously and to keep the transition window long enough that any bug surfaces before the legacy verifier is decommissioned.
The work-stream owner per the Ch 30 framework is the rollup core team for each major rollup (Matter Labs for ZKsync, StarkWare for Starknet) (Starknet, 2022; ZKsync, 2023). The verifier-contract operator owner per the same framework is the governance multisig signer set running the activation. The chain governance owner per Ch 41 is the rollup-side governance forum coordinating the proposal calendar. The Ch 30 four-phase program runs the migration through discovery (audit and signer-set coordination), first-wave (early-adopter candidate configuration on a testnet rollup), broad-rollout (mainnet activation block), and end-of-migration (legacy verifier decommissioned at the close of the transition window).
Cryptanalysis: three on-chain-verifier-surface attack categories
Section titled “Cryptanalysis: three on-chain-verifier-surface attack categories”Three attack categories specific to the on-chain-verifier surface beyond the generic Shor break covered in Ch 04 and the per-output-type framing covered in Ch 37.
The first is verifier-contract bug exploitation during the upgrade window. The verifier upgrade introduces new bytecode at an upgradeable proxy address. Any soundness bug in the new bytecode lets an attacker forge a proof the legacy verifier would reject. The attacker submits the forged proof to the new verifier and the rollup accepts an invalid state transition. The attack window runs from activation to rollback. A misfired upgrade burns operator funds during this window. The funds at risk on a production rollup are the rollup’s bridge balance plus the per-cycle proof-acceptance throughput. The mitigation is the audit phase before activation plus the parallel-run transition window after.
The second is the Fiat-Shamir QROM gap at concrete deployment parameters. The L4 Fiat-Shamir transform is sound in the random-oracle model under the classical Fiat-Shamir analysis (Fiat & Shamir, 1987). Under the QROM, Ch 33’s stipulated model prices the multi-round Fiat-Shamir loss at on the interactive soundness error for an -round protocol, at , with DFMS20’s additive challenge-space term dropped, so the figure is a model output rather than a certified bound (Don et al., 2020). For a FRI-based SNARK at deployed parameters (the parameters Block and Tiwari tabulate for StarkWare’s Stone prover: the 252-bit Cairo prime, 18 FRI queries and 24 grinding bits to a 96-bit target (Block & Tiwari, 2024)), the analysis is partially complete.
Block et al. 2023 supply the asymptotic QROM Fiat-Shamir bound (Block et al., 2023). Block and Tiwari then audit concrete security in the classical ROM at the parameter settings of deployed pipelines (Block & Tiwari, 2024). Those settings include the stone-prover and SHARP configurations behind Starknet and the era-boojum configuration behind ZKsync Era. They report provable security running 21 to 63 bits below conjectured security in all but one of the surveyed sets. That is the sharpest published statement about the two verifiers this chapter treats. The deployment-parameter QROM accounting is open in the literature.
An adversary running BHT-style quantum collision search against the L4 hash is one attack model among several the QROM reduction must cover. The per-attack cost the BHT bound predicts is a generic-query attack-cost estimate in its memory model, an upper bound on the work one collision needs, not a complete soundness bound for the FRI Fiat-Shamir step. The rollup operator’s mitigation is the L4 hash swap to a wider effective transcript output, with the caveat that the QROM bound at FRI deployment parameters is still pending. The hash swap widens the transcript’s collision margin under BHT and CNPS. The challenge-space width the QROM rule sizes is set by the field the challenges are drawn from, which a wider hash does not change, and Ch 35’s Starknet transition path lists both knobs.
The third is recursive-proof composition under post-quantum primitives. A recursive proof system embeds the verifier of one proof inside the prover of another, producing a chain of proofs that compresses to a single outer proof. Halo builds its recursion on an inner-product argument adapted from Bulletproofs (Bowe et al., 2019; Bünz et al., 2018), and is Shor-broken at that layer. Bulletproofs itself composes no proofs: its own recursion halves the dimension inside a single inner-product argument, and its verifier is linear in the statement rather than succinct. Hash-based STARK recursion (one STARK verifier embedded inside another STARK prover) avoids introducing a Shor-broken group assumption, provided the recursive verifier, the commitment layer, the Fiat-Shamir transcript, and the hash output widths are parameterized consistently. It inherits the same FRI / Merkle / QROM caveats as the non-recursive STARK rather than automatically proving full post-quantum soundness. The mitigation is to pick a hash-based recursion (recursive STARKs over FRI commitments) rather than a pairing-based or IPA-based recursion. ZKsync Era’s outer pairing wrapper (ZKsync, 2023) is the canonical example of the cell to migrate away from.
Tradeoffs
Section titled “Tradeoffs”The layer-by-candidate tradeoff table records the per-cell decision across the post-quantum status, deployment readiness, and verifier-side cost.
| Layer | Candidate | PQ status | Deployment (chain-tip 2026) | Verifier-side cost driver |
|---|---|---|---|---|
| L2 | KZG | shor-broken | production | one pairing per opening |
| L2 | FRI | grover-weakened | production | Merkle tree depth plus proximity |
| L2 | lattice-PCS | pq-secure | research-to-early-adoption (no broad rollup deployment) | Module-SIS sampling on the prover, ring arithmetic in the verifier |
| L4 | SHA-256 | grover-weakened (~85 bits PQ collision under BHT at 256-bit output) | precompile 0x02 (legacy default) | low gas per byte |
| L4 | SHAKE-256 | grover-weakened (~170 bits PQ collision under BHT at 512-bit output) | bytecode-only on-chain (no precompile at chain-tip 2026) | 512-bit transcripts (multi-pass Keccak is the pragmatic substitute) |
| L4 | Keccak-256 | grover-weakened (~85 bits PQ collision under BHT at 256-bit output) | native EVM opcode 0x20 | cheapest on-chain per input byte, pre-standard Keccak padding |
The conservative-assumption preference moves the L4 hash to a wider effective transcript output. The wider-output realization is SHAKE-256 with a 512-bit output if an EIP introduces a SHAKE precompile, or a multi-pass Keccak-256 construction over the native opcode. The preference accepts the per-proof gas penalty, and treats the cube-root quantum-collision bound on the L2 Merkle hash as the binding security argument for the L2 layer. The byte-budget preference picks the recursive STARK wrapper at the L2 layer to amortize per-effective-proof gas across an inner-proof batch. The wrapper’s per-effective-proof gas falls below one tenth of legacy. The deployment-readiness axis is the binding constraint for any candidate selected at chain-tip 2026: lattice-PCS at L2 is not broadly production-deployed in a rollup verifier and sits in the research-to-early-adoption category rather than the immediately-deployable group.
An operator who picks the conservative-assumption path runs L1=AIR, L2=FRI, L3=FRI-IOP, L4 widened via multi-pass Keccak-256 over the native opcode (or SHAKE-256 if an EIP introduces a SHAKE precompile), with optional recursive STARK wrapping for amortization. An operator who picks the byte-budget path runs the same configuration with the recursive wrapper enabled by default. An operator who already runs the ZKsync Era Boojum configuration (L1=Plonkish, L2=FRI inner with pairing-based outer wrapper, L4=a Keccak transcript at the published outer wrapper and a Poseidon2 transcript at the wrapped stage, per the pinned proving pipeline (Matter Labs, 2026a)) has two migration moves. The first is dropping the pairing outer wrapper, an L2 swap to FRI all the way down. The second is widening the L4 transcript output via a multi-pass Keccak-256 construction over the native opcode, with a SHAKE-256 precompile as a future option if an EIP introduces one.
The per-proof gas tax against the legacy SHA-256 STARK baseline is the constraint the verifier-contract migration is planned around. The wider-hash configuration carries a one-point-three multiplicative factor against legacy under the multi-pass Keccak illustrative sensitivity; a bytecode-implemented SHAKE-256 without precompile support would carry a much larger factor (an order of magnitude is plausible). The recursive-wrapper configuration brings the factor down to roughly one-hundredth of legacy via amortization. The Ethereum gas limit in this chapter’s model (sixty million gas per block, the EIP-7935 EL-client default (Ethereum Improvement Proposal authors, 2025b), the anchor from Ch 37) caps per-block proof throughput at nine proofs (wider-hash) to eight hundred effective proofs (recursive wrapper) at the ceiling. Sustained-throughput planning compares against the thirty-million EIP-1559 target rather than the sixty-million ceiling. The rollup operator’s per-rollup-cycle proof volume determines which configuration clears the gas-budget headroom.
Exercises
Section titled “Exercises”-
Per-proof gas-cost change under a Fiat-Shamir hash swap. Compute the per-proof gas-cost change when the L4 Fiat-Shamir hash swaps from SHA-256 to a wider-output construction at a stated proof shape. Assume the legacy SHA-256 STARK runs at five million gas per proof and the illustrative wider-hash factor is one and three tenths (representing a multi-pass Keccak-256 construction over the native EVM opcode). A bytecode-implemented SHAKE-256 would carry a larger factor. Report the per-proof gas under the wider-hash configuration, the multiplicative factor against legacy, and the proofs-per-block maximum at the Ethereum gas-limit ceiling of sixty million.
-
Sketch the verifier-contract upgrade choreography. Name the five phases of the upgradeable-verifier-contract upgrade choreography (propose, audit, multisig-approve, activate, transition window). For each phase, identify which operator-side decision the phase resolves. State the rollback path and the operator-side action at each phase to enable rollback if a soundness bug surfaces in the new verifier bytecode.
-
Open-ended: four-layer decomposition on a stated rollup. Apply the Ch 31 four-layer decomposition to a stated rollup of your choice (the rollup you read about in Ch 35, or a different rollup you research independently). Identify which layers expose the on-chain surface and which layers run off-chain in the prover. State the L2 commitment scheme and the L4 Fiat-Shamir hash the rollup deploys at chain-tip 2026.
-
Strand verifier-upgrade cadence under . Strand commits one rollup cycle per hour. Each verifier-contract upgrade costs the operator three weeks of wall-clock time (propose, audit, multisig-approve, activate, transition). The Strand on-chain-verifier surface sits at and . Show that means no Mosca-mandated periodic rotation is required and the chapter’s recommended cadence is
governance-triggerrather thanevery-N-rollup-cycles. Then propose a governance-defined maximum verifier age (for example, one rotation review every twelve months) and compute the corresponding cycle count between rotations at one cycle per hour. -
Open-ended: map to the Ch 30 four-phase program. Map the verifier-contract migration to the Ch 30 four-phase program (discovery, first-wave, broad-rollout, end-of-migration). For each phase, name the verifier-contract operator work-stream owner per the same Ch 30 framework (rollup core team, governance multisig signer set, rollup governance forum, audit firm). Justify each mapping in one sentence.
Solutions to Exercises 1, 2, and 4 are in Appendix D, Chapter 40; Exercises 3 and 5 are open-ended and have editorial-note treatments rather than worked answers.
A separate track, for rebuilding rather than reading. The package
exercises/ch40-zk-rollups has every function the chapter teaches replaced by
a stub. Run PQC_IMPL=exercises pytest tests/ch40 to grade your version
against the suite that proves the reference one.
References
Section titled “References”Last updated: