7Block Labs
Blockchain Technology

ByAUJay

How Exactly Does Batching Thousands of Groth16 Proofs Into One Aggregated Proof Get Settled On-Chain?

A practical, decision‑maker’s walkthrough of how Groth16 proof aggregation (e.g., SnarkPack) changes what you post to a chain, how your verifier contract works after Pectra, and what it really costs in gas and latency.

Summary: Aggregating thousands of Groth16 proofs collapses many independent verifications into a single on‑chain check that’s O(log n) in verifier work. On today’s Ethereum (post‑Pectra), you can verify these aggregated proofs on BN254 or BLS12‑381 precompiles; the main bottlenecks become calldata and a handful of pairing checks, not thousands of separate verifications. (research.protocol.ai)


TL;DR for startup and enterprise leaders

  • Aggregation turns k independent Groth16 proofs into one aggregated proof plus a compact “manifest” of the instances covered. On‑chain, you verify one object and commit to the included statements via hashes or Merkle roots. (research.protocol.ai)
  • On Ethereum since May 7, 2025 (Pectra), you can verify pairing‑based proofs either on BN254 (legacy) or on the new BLS12‑381 precompiles (EIP‑2537) with modern security and competitive gas per pairing. (blog.ethereum.org)
  • Real numbers you can plan for:
    • Groth16 single‑proof verify: ≈200k–220k gas baseline for BN254, plus ~7k per public input. (medium.com)
    • SnarkPack off‑chain: 8,192 Groth16 proofs aggregate in ≈8–9 s; aggregated proof verifies off‑chain in ≈33 ms; proof size ≈40–41 KiB. (research.protocol.ai)
    • On‑chain verification of an aggregated proof: dominated by O(log n) pairings plus calldata for the ≈40 KiB proof (16–40 gas/byte depending on EIP‑7623 thresholds). Budget ≈0.45–1.1M gas for pairings+overhead and 0.65–1.64M gas for calldata, depending on curve, pairing count, and whether the calldata floor applies. (eips.ethereum.org)

What does “aggregating Groth16 proofs” really do?

  • Baseline Groth16: Each proof π = (A ∈ G1, B ∈ G2, C ∈ G1) is verified by checking one pairing product equation that involves a constant number of pairings on the verifier’s curve (BN254 or BLS12‑381). Typical Solidity verifiers on BN254 run ~3–4 pairings per proof. (risencrypto.github.io)
  • Aggregation (SnarkPack): Using an inner‑product pairing argument (GIPA), you combine k Groth16 proofs into a single argument whose size and verification time are O(log k). Crucially, you do not need a new trusted setup; SnarkPack reuses Groth16’s existing “powers of tau” transcripts. (research.protocol.ai)

What you gain:

  • One aggregated proof to verify on‑chain instead of k proofs.
  • A logarithmic number of pairings and small extra scalar multiplications at the verifier.
  • A compact commitment (manifest) that binds exactly which original statements/proofs are included. (research.protocol.ai)

What lands on-chain when you batch thousands of Groth16s?

A typical settlement transaction contains:

  1. Aggregated proof bytes
  • ~40–41 KiB at k≈8,192 (numbers from Protocol Labs’ implementation; the curve used there is pairing‑friendly and comparable to BLS12‑381/BN254 encodings). (research.protocol.ai)
  1. Public IO commitments for the batch
  • Instead of posting all public inputs for all k instances, you post a compact digest (e.g., a Merkle root over per‑instance public IO or a domain‑separated transcript hash). This digest appears as a small set of public inputs to the aggregated verifier. Downstream contracts can verify inclusion with a Merkle proof later. (research.protocol.ai)
  1. Verifying key (VK) reference
  • Your verifier contract either hardcodes the VK, stores it once, or stores a hash of it. Most production flows hardcode or immutably store the VK to prevent “VK drift.” Tooling like snarkjs/gnark can export Solidity/Rust verifiers tied to a VK. (docs.gnark.consensys.io)
  1. Optional: a per‑batch “manifest”
  • Emitted as an event to avoid storage writes, it lists the batch ID, Merkle root(s), the circuits’ VK hashes, and any replay‑protection domain (chainId, programId). Consumers index it off‑chain and use inclusion proofs on demand. (7blocklabs.com)

How the verifier contract actually works now (Ethereum, Jan 2026)

Two curve options exist for on‑chain pairing checks:

  • BN254 precompiles (alt_bn128): EIP‑196/197 added EC add/mul and pairing; EIP‑1108 repriced gas to make pairings affordable. Pairing gas ≈ 34,000·k + 45,000 for k pairs. This underpins most historical Groth16 verifiers. (eips.ethereum.org)
  • BLS12‑381 precompiles (post‑Pectra, EIP‑2537): Adds modern 128‑bit‑security curve ops, including a pairing precompile with gas ≈ 32,600·k + 37,700. It also adds MSM precompiles to speed multi‑scalar multiplications. This is now production‑usable on mainnet after May 7, 2025. (eips.ethereum.org)

Implication: An aggregated Groth16 verifier can be implemented on either curve. Teams migrating from BN254 to BLS12‑381 get stronger security and slightly cheaper pairing checks per pair, at the cost of larger point encodings (affects calldata). (eips.ethereum.org)


Cost model: turning cryptography into gas you can budget

The following model helps you estimate costs; adjust constants to your library’s concrete pairing count and proof size.

  • Pairing cost (BN254): gas_pair = 45,000 + 34,000·k. (eips.ethereum.org)
  • Pairing cost (BLS12‑381): gas_pair = 37,700 + 32,600·k. (eips.ethereum.org)
  • Calldata (post‑EIP‑7623): 4/16 gas/byte for “normal” transactions but a minimum 10/40 gas/byte floor kicks in for data‑heavy calls with low EVM execution relative to calldata; this caps worst‑case block size as blobs scale. (eips.ethereum.org)

Worked example (for planning):

  • Assume k = 8,192 individual Groth16s aggregated into one proof of ~41,000 bytes (≈40.98 KiB). Off‑chain aggregation ≈8–9 s; off‑chain verify ≈33 ms. (research.protocol.ai)
  • Aggregated verifier pairings: protocol‑dependent and O(log k). Many teams budget a low‑dozens pairing count at this scale; you should confirm the exact constant from your aggregator library. For budgeting, take k_pair = 12 as a conservative placeholder. (Engineering assumption for planning; check your implementation.) (research.protocol.ai)
  • On BN254: pairing gas ≈ 45,000 + 34,000·12 = 453,000 gas; BLS12‑381: ≈ 37,700 + 32,600·12 = 429,900 gas. EVM “scaffolding” + ECADD/ECMUL for IC accumulation adds a few thousand gas. (eips.ethereum.org)
  • Calldata:
    • If your tx qualifies as “normal” (sufficient EVM work vs data), 41,000 bytes × 16 = ≈656,000 gas.
    • If the EIP‑7623 floor applies, 41,000 × 40 = ≈1,640,000 gas. (eips.ethereum.org)

So your all‑in gas to verify and settle a “thousands‑batched” Groth16 aggregation on Ethereum lands in the ≈1.1M–2.1M gas range depending primarily on calldata pricing and curve, not counting any storage writes. That’s still dramatically smaller than k × 200k if you posted/verified individually. (medium.com)

Tip: Emit batch metadata as events (logs) and store only commitments on‑chain to avoid SSTORE costs; per‑instance details can be proven later with inclusion proofs. (7blocklabs.com)


What the Solidity/Rust verifier actually checks in an aggregated flow

At a high level:

  1. Recompute the VK‑linearized accumulator for the batch public inputs:
  • vk_x = IC[0] + Σ public_input[i] · IC[i] (EC multiexp on G1). On BN254 you’ll use ECADD/ECMUL (EIP‑196) and then call the pairing precompile (EIP‑197). On BLS12‑381 you can also leverage the G1/G2 MSM precompiles (EIP‑2537). (eips.ethereum.org)
  1. Verify the aggregated pairing equation(s):
  • The Groth16 check e(A,B) = e(α,β) · e(vk_x,γ) · e(C,δ) is folded into a logarithmic‑size argument via SnarkPack’s inner‑product reduction. Your on‑chain code ends up issuing one or a few multi‑pairing checks where the number of pairs grows O(log k). (deepwiki.com)
  1. Check transcript binding:
  • The verifier binds to: circuit/VK hash, batch ID, chainId, the manifest root(s), and often the block number or domain separators used in Fiat–Shamir. This prevents “mix‑and‑match” attacks across circuits or chains. (Best‑practice design pattern; ensure your library exposes these fields as public inputs.) (research.protocol.ai)
  1. Write minimal commitments and emit events:
  • Store the manifest root(s) and batch ID; emit an event with convenience fields so downstream contracts or indexers can attribute statements to a specific aggregated proof efficiently. (7blocklabs.com)

Practical example: an Ethereum L1 “aggregator-settlement” contract

  • Curves and keys:
    • Choose BN254 if you must interoperate with legacy Groth16 circuits produced by circom/snarkjs; choose BLS12‑381 if you control both prover and verifier and want 128‑bit security with competitive gas per pairing. (docs.circom.io)
  • Calldata layout:
    • [A_agg | B_agg | C_agg | manifest_root | vk_hash | domain_seps | small aux scalars]. Keep public inputs tiny; roll up per‑instance IO into one or two Merkle roots. (deepwiki.com)
  • Verification:
    • Accumulate IC coefficients with public inputs, call the pairing precompile once with the needed pairs, return true/false.
    • Gas planning: assume O(log k) pairs; plug into the EIP‑1108 or EIP‑2537 formula to budget. (eips.ethereum.org)
  • Post‑verify:
    • SSTORE: batchId → manifest_root (optional if you use only logs).
    • Events: emit BatchSettled(batchId, manifest_root, vk_hash, k, curveId). (7blocklabs.com)

Operational SLOs you can actually hit

  • Aggregation latency: SnarkPack aggregates 8,192 Groth16 proofs in ≈8–9 s on a 32‑core CPU; fewer proofs scale sub‑linearly. For 256–1,024 proofs, expect low single‑digit seconds on one server. (research.protocol.ai)
  • Off‑chain verify time: ≈33 ms for an 8,192‑proof aggregate; on‑chain verify then dominated by a handful of pairings and calldata size, not computation. (research.protocol.ai)
  • Throughput architecture:
    • Run multiple aggregation workers; close batches on size or time thresholds (e.g., “close every 2s or at 2,048 proofs, whichever first”) to bound tail latency for downstream chains. (7blocklabs.com)
    • If targeting Ethereum L1 post‑Pectra, prefer BLS12‑381 verifiers for new deployments; pairings are slightly cheaper per pair and security is stronger. (eips.ethereum.org)

Best emerging practices for 2026 deployments

  • Keep public inputs tiny
    • Hash or Merkle‑commit large per‑instance data; pass only the root as a public input to the aggregated proof. This keeps verify gas dominated by a few pairings rather than calldata. (deepwiki.com)
  • Bind everything in the transcript
    • Include chainId, programId, vk_hash, and manifest_root(s) in the Fiat–Shamir transcript to prevent replays across contracts or chains. (research.protocol.ai)
  • Prefer BLS12‑381 where you can
    • EIP‑2537 is live; per‑pair pairing gas is 32,600 with a 37,700 base, and you get ~128‑bit security. BN254 remains appropriate for legacy circuits and interop. (eips.ethereum.org)
  • Emit, don’t store
    • Emit batch metadata as events; store only what future on‑chain consumers must read synchronously (often nothing). (7blocklabs.com)
  • Gas‑aware proof sizing
    • If your aggregated proof is ≈40 KiB, factor EIP‑7623’s floor for data‑heavy txs. Design the settlement function to do enough EVM work (e.g., verification, domain checks) to stay in the 4/16 gas/byte band when possible; otherwise budget 10/40. (eips.ethereum.org)
  • Version and rotate VKs safely
    • Hardcode vk_hash in the verifier and expose an upgrade path only through a new contract address pinned by governance to avoid “VK drift.” Tooling like gnark/snarkjs outputs VK‑tied verifiers. (docs.gnark.consensys.io)

Pitfalls we see in audits

  • VK drift or mixed circuits in one batch
    • Aggregate only proofs for the exact same VK unless your aggregator explicitly supports heterogeneous VKs and binds each to its instance in the transcript. Otherwise you risk accepting “foreign” statements. (research.protocol.ai)
  • Over‑sharing public inputs
    • Posting all public inputs for k proofs balloons calldata. Commit to them and keep on‑chain IO constant‑size. (deepwiki.com)
  • Ignoring EIP‑7623 in budgets
    • A 40 KiB aggregated proof can cost 0.65–1.64M gas just to land on L1. Run the numbers with your batch size and block targets. (eips.ethereum.org)
  • Under‑estimating pairing count constants
    • O(log k) is a complexity class; your concrete constant depends on library choices and optimizations. Validate with your actual verifier before setting SLAs. (research.protocol.ai)

When aggregation is not the right hammer

  • If you control the proving stack end‑to‑end and you’re already using recursion (STARK→SNARK, Halo2, etc.), a single wrapped proof per batch may be cheaper to post (smaller proof bytes, similar pairing count). Aggregation shines when you must accept many independent Groth16s produced by heterogeneous parties/circuits and want one settlement transaction. (7blocklabs.com)

Implementation references you can adopt today

  • SnarkPack paper and engineering overview (inner‑product pairing argument; O(log n) verifier; filecoin‑grade benchmarks). (research.protocol.ai)
  • BN254 pairing and repricing (EIPs 196/197/1108), foundation for most Groth16 verifiers. (eips.ethereum.org)
  • BLS12‑381 precompiles (EIP‑2537) and Pectra meta‑EIP list; use these for 128‑bit security and efficient MSM/pairings. (eips.ethereum.org)
  • gnark and snarkjs exporters for Solidity verifiers (BN254 and, with updated backends, BLS12‑381). (docs.gnark.consensys.io)

Decision checklist for your roadmap

  • Target curve:
    • BN254 for legacy interop, or BLS12‑381 (EIP‑2537) for new systems? (eips.ethereum.org)
  • Batch size targets:
    • What are your steady‑state k and max k per settlement window? Verify aggregation and calldata budgets with EIP‑7623. (eips.ethereum.org)
  • Public IO handling:
    • Commit via Merkle roots and keep on‑chain public inputs constant‑size. (deepwiki.com)
  • Transcript binding:
    • Include chainId, vk_hash, manifest_root(s), and batchId in the Fiat–Shamir transcript. (research.protocol.ai)
  • Ops SLOs:
    • Aggregation latency budgeted (e.g., <10 s for 8k proofs), with parallel workers? (research.protocol.ai)

If you want us to turn this into a concrete verifier contract and gas/cost model tailored to your circuits, we’ll prototype both BN254 and BLS12‑381 paths and give you an apples‑to‑apples budget under today’s EIP‑7623 rules and your actual calldata footprint. (eips.ethereum.org)


Footnotes and sources:

  • SnarkPack performance and design: Protocol Labs research posts and project pages. (research.protocol.ai)
  • Ethereum precompiles and gas formulas: EIPs 196, 197, 1108, 2537; Pectra meta EIP. (eips.ethereum.org)
  • EIP‑7623 calldata pricing change (mainnet May 7, 2025): EF blogs and EIP text. (blog.ethereum.org)
  • Groth16 verification equation and proof structure for context. (deepwiki.com)

Description: Aggregating thousands of Groth16 proofs collapses on‑chain verification to a single O(log n) check, with real costs now dominated by a handful of pairings and calldata size under EIP‑7623. Post‑Pectra, BLS12‑381 precompiles (EIP‑2537) make aggregated verification both secure and gas‑competitive for production deployments. (research.protocol.ai)

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.