7Block Labs
Blockchain Development

ByAUJay

What’s the Cleanest Design Pattern for a Rollup That Can Swap Proof Formats Later Without Redeploying Everything?

Summary: A durable way to future‑proof your rollup is to keep the state machine and bridge immutable while routing all proof verification through a tiny Verifier Proxy + Adapters + Verification‑Key Registry. This pattern lets you move from Groth16→PLONK, BN254→BLS12‑381, or STARK→SNARK wrappers with a timelocked config change—not a redeploy—while preserving bridge addresses and user state.

TL;DR (for decision‑makers)

  • The cleanest, low‑risk pattern is: RollupCore (immutable) → VerifierProxy (upgradeable pointer) → VerifierAdapters (one per proof family) + VKRegistry (append‑only).
  • It’s now strictly easier and cheaper on Ethereum to evolve proofs: Dencun (EIP‑4844) gave rollups blob‑based DA and a KZG point‑evaluation precompile; Pectra (May 7, 2025) added BLS12‑381 precompiles, raised blob throughput, and repriced calldata, all of which favor swappable, modular verifiers. (eips.ethereum.org)
  • Real projects are already shipping modular, multi‑proof stacks (OP Stack’s “multi‑proof nirvana”; Arbitrum BoLD) that decouple “is this state root valid?” from “which proof produced it?”. Your L2 can mirror that architecture and switch proof formats with a config update. (blog.oplabs.co)

Why plan for proof‑format swaps now?

  • Cheap, abundant DA via blobs is here; the KZG point‑evaluation precompile at 0x0A lets contracts verify commitments without reading blob contents, which remain inaccessible to the EVM by design. This stabilizes interfaces around “commitments” instead of raw batch data. (eips.ethereum.org)

  • Pectra activated on mainnet May 7, 2025 and included:

    • EIP‑2537 BLS12‑381 precompiles (0x0b–0x11), enabling native BLS MSMs and pairings and making BLS‑friendly verifiers practical on L1.
    • EIP‑7691 blob throughput increase (target 6, max 9 blobs).
    • EIP‑7623 calldata cost increase, pushing DA away from calldata and into blobs. (blog.ethereum.org)
  • Optimism’s fault‑proof system was designed to be modular and evolve to “multi‑proof nirvana.” Arbitrum shipped BoLD to guarantee bounded dispute times with permissionless validation—both are patterns of abstraction around “proof of state” with pluggable backends. (blog.oplabs.co)

Result: you want a rollup that can adopt new curves, schemes, or wrappers as economics and L1 features change—without moving your bridge or re‑issuing assets.


The pattern: Verifier Proxy + Adapters + VK Registry

Think of verification as a replaceable module, with everything else staying still.

  • RollupCore (immutable)
    • Owns canonical L2 state root, deposit/withdraw message trees, and system config.
    • Has a single pointer to VerifierProxy.
  • VerifierProxy (upgradeable pointer via timelocked governance)
    • Dispatches verify() calls to the current VerifierAdapter for a given formatId.
  • VerifierAdapters (one per proof family)
    • Implement format‑specific logic (Groth16 on BN254, PLONK on BLS12‑381, STARK→SNARK wrapper, etc.).
    • Read verifying‑key metadata from VKRegistry and enforce public‑input binding.
  • VKRegistry (append‑only, timelocked)
    • Maps (formatId, circuitId, version) → vkHash + metadata (curve, pairings, pubInputsLayout, proofLen).
    • Emits events; no in‑place edits; entries can be deactivated but never mutated.

This mirrors how OP Stack decouples the fault‑proof framework from any specific proof and how Arbitrum separated the dispute protocol (BoLD) from chain logic. (blog.oplabs.co)

Minimal interfaces (Solidity)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

struct ProofContext {
  uint64  l2BlockNumber;
  bytes32 prevStateRoot;
  bytes32 newStateRoot;
  bytes32[] blobVersionedHashes; // EIP-4844 commitments
  bytes32 daCommitment;          // e.g., Merkle root or KZG commitment hash
  bytes32 stfVersion;            // hash of the state transition fn version
  bytes32 vkId;                  // (formatId,circuitId,version) -> VKRegistry key
}

interface IVerifierAdapter {
  function verify(bytes calldata proof, ProofContext calldata ctx)
    external view returns (bool ok);
}

interface IVKRegistry {
  function metadata(bytes32 vkId) external view returns (
    uint32 formatId, bytes32 circuitId, uint16 version,
    bytes32 vkHash, bytes4 curveId, uint8 pairingCount, uint32 pubInputsLen
  );
  function active(bytes32 vkId) external view returns (bool);
}

contract VerifierProxy {
  address public owner; // behind timelock
  mapping(uint32 => address) public adapterOf; // formatId -> adapter

  function verify(uint32 formatId, bytes calldata proof, ProofContext calldata ctx)
    external view returns (bool)
  {
    address adapter = adapterOf[formatId];
    require(adapter != address(0), "NO_ADAPTER");
    // Adapters MUST re-derive and check all public inputs & blob commitments.
    return IVerifierAdapter(adapter).verify(proof, ctx);
  }
}
  • Keep RollupCore ignorant of proof specifics; it only cares that VerifierProxy returns true for the supplied context and new state root.
  • VKRegistry can follow the spirit of ERC‑1922 (standardized verifier params keyed by a verificationKeyId). (ercs.ethereum.org)

Practical migration example: Groth16 (BN254) → PLONK (BLS12‑381)

  1. Register the new verifying key
  • Append vkId for PLONK/BLS12‑381 in VKRegistry with pub‑input layout metadata.
  • Publish circuits and vkHash so watchers can rebuild and compare.
  1. Deploy a PLONK/BLS12‑381 adapter
  • Use EIP‑2537 precompiles: pairing cost is 32,600·k + 37,700 gas; k pairings. For many designs this is slightly cheaper per pairing than BN254 after EIP‑1108. (eips.ethereum.org)
  1. Dual‑accept window
  • Governance adds adapterOf[PLONK] in VerifierProxy but keeps Groth16 as default.
  • Sequencer/provers produce both proof types for overlapping checkpoints; RollupCore accepts whichever verifies first. This redundancy mirrors the OP Stack’s trajectory toward multiple proofs. (blog.oplabs.co)
  1. Flip default and deprecate the old vkId
  • After N days, disable new Groth16 proofs by deactivating legacy vkIds in VKRegistry.
  • Keep the legacy adapter around for historical verification or emergency fallbacks.
  1. Optional “proof‑of‑proof” safety net
  • For high‑value transitions, provide a small SNARK that attests “the old Groth16 verifier would have returned true on this input,” allowing quick rollback if an adapter bug is discovered. Teams like Succinct and RISC Zero ship recursion/wrapping stacks where SNARK‑of‑STARK or SNARK‑of‑SNARK patterns are productionized. (blog.succinct.xyz)

Gas, latency, and DA economics to budget against

  • Groth16 on BN254: ~207,700 gas fixed + ~7,160 gas per public input (post‑EIP‑1108). Aim to collapse public inputs to a single hash where possible (Polygon zkEVM uses an aggregator pattern to reduce inputs). (medium.com)
  • STARK verification on Ethereum: ~5–6M gas typical (e.g., Starknet’s SHARP/Stone trains), which is why many stacks SNARK‑wrap STARKs for L1. (community.starknet.io)
  • BLS12‑381 on Pectra: native MSMs + pairing checks at 0x0b–0x11; pairing gas ~32,600·k + 37,700. This unlocks verifiers and signature checks with modern security levels and competitive gas. (eips.ethereum.org)
  • Blobs vs calldata: Blobs are not readable by the EVM; contracts verify KZG openings using the 0x0A precompile against versioned blob hashes. With EIP‑7623 raising calldata floor cost, treat calldata as an emergency path; keep your on‑chain interfaces blob‑commitment‑oriented. (eips.ethereum.org)
  • Blob capacity: Pectra raised target/max to 6/9 blobs, improving L2 batch DA headroom and smoothing fees, which helps during dual‑proof migration windows. (eips.ethereum.org)

Case studies: what today’s leaders are doing

  • OP Stack: Fault proofs are modular and moving toward coexisting proof systems; the framework anticipates running multiple proofs side‑by‑side to achieve “Stage 2” decentralization. Your VerifierProxy/Adapter setup is the validity‑proof analogue. (blog.oplabs.co)
  • Arbitrum BoLD: A dispute protocol upgrade that guarantees bounded resolution time and permissionless validation on mainnet as of Feb 12, 2025—showing you can overhaul proof/dispute logic without touching user‑facing bridge addresses. (theblock.co)
  • Polygon zkEVM: Actively pursuing verifier cost reductions (e.g., FFLONK, public‑input aggregation to one hash) to keep on‑chain verification cheap and composable. (docs.polygon.technology)
  • STARK→SNARK wrappers: RISC Zero and Succinct’s SP1 compress large STARKs into small pairing‑friendly proofs for Ethereum verification—exactly the type of format swap your adapters should anticipate. (7blocklabs.com)

Design checklists you’ll actually use

Security essentials

  • Public input binding: Recompute the digest inside each adapter (e.g., keccak256 over [prevRoot, newRoot, l2BlockNumber, blob hashes, stfVersion, DA commitment]) and compare to the circuit’s single public input. Never accept a caller‑provided digest. Polygon’s docs illustrate why collapsing to one public input saves gas and simplifies binding. (docs.polygon.technology)
  • VK immutability: VKRegistry is append‑only; prohibit in‑place edits. Emit events for adds/removals and require a delay before activation.
  • Timelocks and kill‑switches: Put the timelock on VerifierProxy and VKRegistry, not on RollupCore. If needed, an emergency pause should only disable proof acceptance, never deposits/exits.
  • Subgroup and encoding checks: BLS12‑381 precompiles enforce subgroup checks for MSM/pairing; still validate encodings and infinity rules. (eips.ethereum.org)
  • Auditor and chaos tests: Exercise dual‑accept periods with adversarial proofs and malformed KZG inputs. Note that even top teams hit bugs—e.g., SP1’s 2025 verifier issue—so plan for rapid adapter swap‑outs. (blockworks.co)

Reliability and operations

  • Dual‑run policy: During migrations, produce both proof types for M checkpoints; only confirm a state root once either proof verifies on L1.
  • Deterministic STF hash: Version your STF (state transition function) with a content hash and bind it in ProofContext to block “silent circuit drift.”
  • DA discipline: Put blob versioned hashes in context and verify KZG openings for any fields you must check on‑chain (e.g., batch metadata) with precompile 0x0A. (eips.ethereum.org)

Governance and upgrade transparency

  • ERC‑1967/UUPS or Diamond: Use a well‑understood upgrade path for the VerifierProxy pointer. Announce vkId and adapter changes with a timelock and post reproducible builds for watchers. (eips.ethereum.org)

Implementation notes that save weeks

  • One public input: Follow Polygon’s pattern—make the outer verifier take a single public input that’s the hash of all inner publics. This cuts verification gas by ~7k per omitted public input and simplifies adapters. (hackmd.io)
  • BN254 vs BLS12‑381: With EIP‑2537 live, BLS12‑381 verifiers and BLS signatures are first‑class on L1. Pairing baselines: BN254 34,000·k + 45,000; BLS12‑381 32,600·k + 37,700. If you’re green‑field in 2026, prefer BLS12‑381 for security headroom and gas parity or better. (voltaire.tevm.sh)
  • STARK wrapping when to use: Native STARK verification (~5–6M gas) is still heavy; SNARK‑wrapping gives you tiny on‑chain footprints and blob‑friendly calldata. Use adapters so you can swap “which wrapper SNARK” later (Groth16→PLONK). (community.starknet.io)
  • Blob realities: The EVM can’t read blob contents; only versioned hashes and KZG proofs are visible. Structure ProofContext and circuits around commitments and point evaluations—not raw batch bytes. (eips.ethereum.org)

Migration runbook (calendarized)

  • T‑30 days: Publish RFC for new format (formatId, circuits, vkHash, pub‑input layout). Spin up shadow prover.
  • T‑21 days: Audit adapter + circuits. Deploy adapter on a canary L1 alias.
  • T‑14 days: Timelock adds adapterOf[formatId] and activates vkId entries; start dual‑run on testnet.
  • T‑7 days: Enable dual‑accept on mainnet; watchdogs compare newRoot across both proofs; alert on divergence >1 block.
  • T‑2 days: Announce flip block; post dry‑run metrics (proof latency, gas).
  • T day: Flip default to new format; keep legacy adapter and vkId active but deprioritized.
  • T+14 days: Deactivate legacy vkId; keep read‑only adapter for forensics; publish post‑mortem.

This is almost identical to how OP Stack ships alternate proofs on testnets before mainnet and how Arbitrum rolled BoLD while preserving user‑facing contracts. (blog.oplabs.co)


Example: STARK→SNARK wrapper adapter

  • Off‑chain: Produce a recursive STARK proof; generate a Groth16/PLONK wrapper with a single public input that binds prev/new roots, blob commitments, and STF version.
  • On‑chain: The adapter checks that the single public input equals keccak256(encoded ProofContext), then calls the BN254 or BLS12‑381 verifier. This is the path used by zkVM stacks (RISC Zero, SP1) to post succinct proofs to Ethereum. (7blocklabs.com)

What to monitor post‑Pectra

  • Throughput: With EIP‑7691’s higher blob capacity, your DA constraints ease, enabling overlapping proof windows during migrations; watch blob base‑fee dynamics under the new 2:3 target:max schedule. (eips.ethereum.org)
  • Costs: EIP‑7623 changed calldata pricing; keep calldata‑heavy verifiers out of hot paths and prefer compact proof encodings with minimal public inputs. (eips.ethereum.org)
  • Proof vendors: zkVM teams iterate quickly; real‑time proving milestones are landing, but also expect occasional critical bugs—preserve the ability to revert adapters fast under a timelock. (blog.succinct.xyz)

Antipatterns to avoid

  • Baking the verifier into RollupCore. You’ll need to redeploy the bridge or migrate user balances just to change a curve or VK—unacceptable for production chains.
  • Mutable VKs. Never edit a VK in place; only append, activate, or deactivate.
  • Unbound public inputs. If circuits accept a caller‑supplied digest, a single bad adapter can wedge the bridge or admit invalid state.
  • Assuming calldata availability. Post‑Pectra, DA belongs in blobs; design around commitments, not bytes. (eips.ethereum.org)

The upshot

A Verifier Proxy + Adapter + VK Registry architecture keeps your L2 “boringly stable” on the outside (same bridge address, same state root semantics) while giving you freedom to adopt better proof systems over time—Groth16→PLONK, BN254→BLS12‑381, or STARK→SNARK—using nothing more than a timelocked config change. That’s the cleanest, lowest‑risk way to evolve proofs without redeploying everything.

If you’d like a reference implementation, gas models for your circuits, or a migration dry‑run plan, 7Block Labs can help design, audit, and ship the full stack—from adapters to governance policies.


References

  • EIP‑4844 blobs and point‑evaluation precompile (0x0A); blobs are inaccessible to the EVM; use versioned hashes and KZG openings. (eips.ethereum.org)
  • Pectra (May 7, 2025): EIP‑2537 BLS12‑381 precompiles, EIP‑7691 blob throughput, EIP‑7623 calldata repricing. (blog.ethereum.org)
  • BN254 pairing gas and Groth16 verification cost formula; STARK L1 verification costs. (voltaire.tevm.sh)
  • OP Stack fault‑proof modularity and “multi‑proof nirvana”; Arbitrum BoLD mainnet rollout. (blog.oplabs.co)
  • Public‑input aggregation design (Polygon zkEVM). (docs.polygon.technology)

Like what you're reading? Let's build together.

Get a free 30‑minute consultation with our engineering team.

Related Posts

7BlockLabs

Full-stack blockchain product studio: DeFi, dApps, audits, integrations.

7Block Labs is a trading name of JAYANTH TECHNOLOGIES LIMITED.

Registered in England and Wales (Company No. 16589283).

Registered Office address: Office 13536, 182-184 High Street North, East Ham, London, E6 2JA.

© 2025 7BlockLabs. All rights reserved.