Verification-Key Independence Collapse / Unfinalized Groth16 Setup
- Name: verification-key independence collapse / unfinalized Groth16 setup
- Category: exploit pattern / verifier-key soundness failure / trusted-setup finalization omission / Groth16 parameter collapse / forged-proof acceptance
- Summary: This pattern appears when a zk verifier is deployed with a cryptographically broken verification key, most cleanly when supposedly independent Groth16 parameters such as
gammaanddeltaare left identical or left at public placeholder values. The Solidity verifier code can be perfectly standard and still worthless if the verification key does not encode a sound statement. In the canonical 2026 cases, skipped or incomplete Phase 2 setup leftgamma2 == delta2 == G2 generator, so the pairing equation collapsed into a tautology. Attackers or whitehats could then forge proofs for arbitrary public inputs, choose fresh nullifiers or claim parameters at will, and turn ordinary withdrawal or prize-claim entrypoints into direct reserve drains without ever knowing a valid witness. - What it is:
- A Groth16-style verifier depends on a verification key whose elements are supposed to be independent outputs of a trusted setup ceremony.
- The deployed verifier key is malformed: critical parameters are identical, trivially related, or left at known placeholder points rather than randomized ceremony outputs.
- The application still treats
verifyProof(...) == trueas full authority for withdrawal, claim, or release logic. - Because the verifier key itself is unsound, a prover can forge proofs for attacker-chosen public inputs without possessing the private witness the application thinks it is checking.
- The exploit surface is not
the circuit relation was subtly underconstrainedorthe consumer settled the wrong effects; it isthe verifier key destroyed proof soundness before those questions even mattered.
- Typical preconditions:
- The protocol uses Groth16 or a closely related verifier with a ceremony-generated verification key baked into the contract.
- A deployment, ceremony, or export step left supposedly independent elements equal or otherwise trivially related.
- In the common
snarkjsvariant, the team rangroth16 setupand then exported the verifier from the initial*_0000.zkeyartifact before anyzkey contribute/ finalization step randomizeddelta. - The application exposes a money-moving path such as
withdraw()orcollect()that trusts verifier success directly. - The attacker can choose public inputs like recipient, nullifier, reward bits, denomination, or root from values the application will accept.
- No secondary provenance check exists beyond the forged proof itself.
- Common vulnerable surfaces:
- Tornado-style privacy pools whose fixed-denomination withdrawals trust Groth16 verifier output directly
- lottery, mixer, bridge, or claim systems that publish verifier keys onchain and let users supply arbitrary public inputs alongside proof elements
- deployments that run
groth16 setupbut skip the required contribution/finalization step before exporting the verification key - copied or test deployments that accidentally ship placeholder verification-key constants into production
- long-lived verifier contracts where nobody re-checks that the deployed key actually came from a completed ceremony
Three verifier-key authority promotions this pattern collapses
-
A verifier artifact exists at all
- A
zkey, verifier JSON, generated Solidity verifier, or copied constant tuple exists somewhere in the build and deploy pipeline. - This is only the reachability question:
there is some proof artifact the product could fund or reuse.
- A
-
That artifact deserves to count as finalized-safe statement truth
- The team still has to prove the artifact came from a completed setup, preserved the proof system’s independence assumptions, and matches the exact statement the application thinks it is enforcing.
- This is the finalized-truth question:
the verifier constants really encode a sound proof system for this statement, not merelya verifier file was generated.
-
That finalized-safe statement truth deserves funded payout or claim authority
- Even a finalized verifier should not automatically inherit unbounded custody reach; rollout still has to decide how much value the verifier can guard, which sibling deployments share its artifact lineage, and what rescue or pause path exists if that lineage is later reclassified as unsafe.
- This is the authority-budget question:
this verifier artifact really deserves this much live economic authority.
Healthy review keeps those promotions separate instead of collapsing them into one vague the verifier was generated, so the proof gate is live story.
- Exploit sequence:
- The attacker inspects the onchain verification key and notices that supposedly independent parameters are equal or otherwise public and degenerate.
- They derive a forged-proof construction from the broken pairing equation; in the 2026
gamma == deltacases, the canonical choice wasA = alpha,B = beta, andC = -vk_x. - They choose arbitrary public inputs that satisfy the application’s non-proof checks: a known root, a fresh nullifier, a recipient they control, and any reward or claim parameters within the contract’s payout rules.
- They compute
vk_xfrom the public inputs and the contract’s hardcodedIC[]points. - They submit the forged proof. The verifier returns
trueeven though no valid witness exists. - The application honors the forged proof and transfers real assets or prize value.
- The attacker repeats with fresh nullifiers or claim inputs until the reachable pool balance is exhausted.
- Variants:
- Phase-2 omission — the common
snarkjsfailure mode where the verifier key is exported beforezkey contribute, leavinggamma2anddelta2at the same generator point. - Placeholder-key deployment — production contracts ship with test or zero-ceremony constants that were never meant to secure real value.
- Backdoored or trivial setup — a ceremony technically happened, but the resulting key still preserves attacker-known relations that destroy soundness.
- Cross-deployment replication — the same broken verifier key is reused across chains or sibling contracts, widening blast radius once one instance is recognized as unsound.
- Phase-2 omission — the common
- Detection heuristics:
- Treat the deployed verification key as a first-class audit target; do not stop at circuit source or Solidity verifier logic.
- Diff
gamma,delta, and other ceremony-derived elements for equality, trivial generator values, or obvious low-entropy structure. - Recompute whether the verifier equation can be satisfied with public key material alone for arbitrary public inputs.
- Verify the full ceremony lifecycle: setup, contribution/finalization, exported key, deployed constants, and the exact artifact hash used onchain.
- For
snarkjsGroth16 deployments, explicitly check that Phase 2 contribution actually happened before the verification key was exported, and treat any verifier derived straight from a*_0000.zkeyor other zero-contribution artifact as presumptively unsafe. - Fingerprint malformed-key families and sweep siblings by deployed constants, not repo labels. If one funded verifier exposes a recognizable shape like
gamma2 == delta2 == G2 generator, search every reachable deployment and copied verifier family for the same constant tuple before triaging only the first address you noticed. - Add negative tests that choose arbitrary public inputs and assert no proof can be constructed from public verification-key elements alone.
- Defenses:
- Treat verification-key generation and deployment as treasury-critical release engineering, not as a copy-paste step after circuit work finishes.
- Record and review exact ceremony artifacts, contribution transcripts, exported verifier keys, and deployed constant hashes.
- Refuse deployment if supposedly independent ceremony outputs are equal, generator-valued, or otherwise obviously degenerate.
- Add pre-deploy and post-deploy checks that compare deployed verification-key constants against the finalized ceremony output.
- Keep emergency drains, pauses, or bounded-balance rollout controls around new proof-authorized systems until the deployed verifier key is independently revalidated.
- When the proof system protects custody directly, audit the verification key with the same seriousness as the circuit and the Solidity consumer.
What sound design looks like
- Healthy teams keep three verifier-key authority promotions explicit instead of inheriting them from artifact existence alone:
a verifier artifact exists,that artifact still deserves to count as finalized-safe statement truth, andthat finalized-safe truth deserves this much funded payout or claim authority. The failure mode here is exactly what happens when those three claims get blurred into one blandthe verifier was generatedfact. - The verifier key itself should be treated as part of the protocol’s root-of-trust object. A healthy deployment story is not just
the circuit source compiledorthe Solidity verifier matches the template, butthe exact onchain constants came from a completed ceremony that preserved the independence assumptions the proof system needs. - Healthy teams also keep a live verifier inventory rather than stopping at artifact provenance. They should know which exact contracts and entrypoints trust each deployed key, what balances or rights remain reachable through it, and whether the key is still active production authority or only a bounded residual path. Verification-key statement inventory and asset-reachability discipline is the positive-design companion for that live-authority map.
- Ceremony completion should be machine-checkable at the artifact level. For Groth16 stacks, operators should be able to show the Phase 1 artifact, the Phase 2 contributions, the finalized
zkey, the exported verification key, and the deployed constants, with simple equality checks proving the deployed verifier really came from that finalized pipeline. - Artifact-family quarantine should be explicit and fast. Zero-contribution
*_0000.zkeyoutputs, placeholderverification_key.jsonfiles, copied fixture constants, and any other pre-finalization verifier family should be treated asnever fund thisrelease classes rather than as ambiguous build leftovers. Trusted-setup finalization and verifier-family quarantine discipline is the direct proof-system rollout companion for turning those fingerprints into funded-deploy gates and sibling-deployment quarantine rules, while exact production artifact attestation and rollback discipline stays the broader release-integrity companion for making provenance legible at the exact artifact layer users and operators are really trusting. - A proof consumer should never be the first place anyone learns the verifier key was degenerate. Independent artifact review, property tests against forged public-input constructions, and deployment-time sanity checks should fail long before real value sits behind the verifier.
- The clean reviewer question is:
what exact evidence shows these onchain verification-key points were finalized from a sound ceremony rather than copied from a placeholder or half-finished setup artifact? - High-signal incidents:
- [[../../../incidents/proofs-verifiers/veil-cash-groth16-delta-gamma-setup-collapse-forged-withdrawals-2026-02-21|Veil Cash Groth16
delta == gammasetup collapse / forged withdrawals (2026-02-21)]] — a realized Base exploit where29forged withdrawals drained the full2.9 ETHpool without any deposit. - [[../../../incidents/proofs-verifiers/foom-cash-groth16-delta-gamma-setup-collapse-whitehat-rescue-2026-02-26|Foom Cash Groth16
delta == gammasetup collapse / whitehat rescue (2026-02-26)]] — a larger cross-chain near miss where whitehats used the same forged-proof construction to rescue nearly all reachable balances on Base and Ethereum before a malicious actor did.
- [[../../../incidents/proofs-verifiers/veil-cash-groth16-delta-gamma-setup-collapse-forged-withdrawals-2026-02-21|Veil Cash Groth16
Failure-prevention checklist
- Make verification-key lineage auditable from circuit build to deployment: finalized ceremony artifact hash, exported verifier-key hash, generated Solidity/verifier artifact hash, and deployed bytecode or constructor-argument hash should all reconcile automatically.
- Treat Groth16 Phase 2 completion as a hard deploy gate, not a runbook checkbox. If the stack cannot prove
contribute/finalize happenedand show the exact resultingzkey, it is not ready for funded deployment. - Make the three verifier-key authority promotions explicit in release review:
artifact exists,artifact is finalized-safe for this statement, andthat statement is allowed to guard this much live valueshould each need their own evidence instead of inheriting funded status from a generated file. - Teach CI and deployment tooling the common failure signature explicitly: refuse to export or deploy verifier constants from an initial
*_0000.zkey/ zero-contribution artifact unless the environment is clearly marked as a disposable test target. - Add explicit sanity checks for impossible-in-production key structure before deployment and after deployment:
gamma != delta, no ceremony-derived point equals a known placeholder generator unless that is explicitly expected, and no verifier constant set matches test fixtures or zero-contribution artifacts. - Run adversarial proof-forgery tests against the exact deployed verification key, not only against local circuit source. If public-key-only constructions can satisfy
verifyProof(), fail the release immediately. - Separate proof-system release ownership from application release convenience. The team shipping the app should not be able to swap verifier constants, copy old keys, or reuse test artifacts without an independent cryptographic signoff.
- Keep a machine-readable denylist of unsafe verifier artifact families and fingerprints: zero-contribution
*_0000.zkeyfiles, placeholder verifier JSON, test-fixture constant sets, and any known-bad exported key hashes should all fail release automatically. - Prebuild family-sweep tooling around those fingerprints: constant-tuple searches, verifier-hash registries, sibling-deployment grouping, and one emergency sheet that already knows which chains, entrypoints, and balances share the same malformed lineage.
- Keep new proof-authorized withdrawal or claim systems on a bounded balance sheet until deployed verifier constants are independently rederived and matched against ceremony outputs.
- Treat sibling deployments as one blast radius. If one chain or one product line reused a malformed verifier key, assume every copied deployment is compromised until the artifacts are proven distinct and sound.
- When one funded verifier fingerprints as a quarantined artifact family, open one shared response sheet keyed by artifact lineage or verifier-hash family rather than separate chain-by-chain tickets. The right question becomes
which copied deployments are already proven distinct?, notis the next one also bad? - Preserve emergency response around the proof gate anyway: pause, withdrawal caps, rescue authority, or migration rails should exist until the verifier root of trust has survived independent review under live deployment conditions.
- Store and publish the exact ceremony and export commands or CI steps used to generate the verifier so reviewers can reproduce artifact lineage instead of trusting screenshots or informal claims.
- Require deployment tooling to refuse ambiguous provenance.
I found this verifier JSON in the reposhould never be enough evidence to put custody behind it.
Audit prompts
- What exact artifact chain links the deployed verifier constants back to a completed trusted setup, and where can a reviewer independently recompute those hashes?
- Are any supposedly independent verification-key elements equal, generator-valued, copied from defaults, or otherwise structured in a way that would be implausible after real ceremony entropy?
- Can a reviewer derive a public-key-only forged proof construction for arbitrary accepted public inputs using only
alpha,beta,gamma,delta, andIC[]from the deployed verifier? - Are release reviewers explicitly separating
verifier artifact exists,artifact is finalized-safe for this statement, andartifact is allowed to guard live balances, or did funded authority slip through because those three claims were narrated as one? - Does the application treat
verifyProof() == trueas direct authority for withdrawals, claims, or releases, or is there any second provenance check that would still stop a broken verifier key from becoming a reserve drain? - Were the deployed constants generated from the exact circuit version the application expects, or could a stale / sibling / test verifier have been wired into production by mistake?
- How does deployment tooling prove that Phase 2 contribution/finalization happened before
verification_key.jsonor Solidity verifier generation? - If the protocol runs on multiple chains or products, are all deployments reusing the same verifier key, and if so is that intentional, documented, and safe?
- Are negative tests written against the real onchain constants, including attempts to choose arbitrary roots, nullifiers, recipients, or reward parameters and still force verifier success?
- Who has authority to pause or drain to safety if a verifier artifact is later found unsound, and is that response path still live on every funded deployment?
- If the team claims ceremony hygiene, what exact evidence shows the deployed verifier was reproduced from finalized artifacts rather than manually copied from an intermediate build directory?
Lessons that should already be standard
- The verification key is not just build output; it is custody-critical trust material. Auditing the circuit while hand-waving the verifier constants is like auditing multisig policy while ignoring the actual signer set.
- Trusted setup hygiene is release engineering, not ceremony theater. A half-finished setup can be as dangerous as an outright cryptographic break because the contract will still happily treat forged proofs as authority.
Verifier file existsis not a funding decision. Artifact existence, finalized-safe statement truth, and live payout authority are separate approvals, and collapsing them is exactly how placeholder verifier families graduate into custody surfaces.Standard verifier templateis not a defense. If the constants are wrong, a perfectly standard verifier becomes a perfectly standard asset-drain primitive.- Cross-chain reuse multiplies cryptographic mistakes. A malformed verifier copied into sibling deployments turns one setup omission into a fleet-wide liability surface.
- One recognizable malformed-key fingerprint is already a fleet incident, not a local bug. The burden should shift immediately to proving which sibling deployments are distinct and safe.
- Proof-authorized payout systems need rollout discipline just like new bridges or new custody code. Until the verifier artifact has survived independent scrutiny, balances should stay capped and rescue rails should stay available.
- The first person to check whether
gamma,delta, and other ceremony outputs look sane should not be the attacker reading the deployed contract after funding goes live. - Artifact provenance should be easier to verify than to narrate. If a team cannot quickly prove where the verifier key came from, reviewers should assume that uncertainty is itself part of the risk.
Where this pattern stops
- Use witness-public-input-binding-failure when the verification key is fine but the circuit statement itself is underbound because public inputs and witness-side claims were never forced to match.
- Use proof-to-effects-binding-failure when the proof may be sound and the verifier key may be sound, but a downstream settlement consumer still executes a different economically relevant effect set than the proof authenticated.
- Use broader custody or lifecycle notes only after the verifier-key problem is already localized. This family starts one step earlier: the proof system’s root verification artifact was already unsound.
- In short: this pattern begins when
proof verifiedstops meaning anything because the deployed verification key destroyed soundness, and it stops once the sharper story is instead a bad circuit statement or a bad proof consumer.
Internal linkages
- Incident exemplars: veil-cash-groth16-delta-gamma-setup-collapse-forged-withdrawals-2026-02-21, foom-cash-groth16-delta-gamma-setup-collapse-whitehat-rescue-2026-02-26.
- Positive-design companion: verification-key-statement-inventory-and-asset-reachability-discipline — use it when the sharper question is how teams should inventory live proving statements, trusted consumers, and reachable balances before one malformed key becomes a surprise payout surface.
- Direct setup-hygiene companion: trusted-setup-finalization-and-verifier-family-quarantine — use it when the sharper question is how completed setup, verifier-family classification, funded rollout gating, and sibling-deployment quarantine should have worked once a malformed-key fingerprint like
gamma == delta == G2 generatorbecame knowable. - Adjacent release-integrity companion: exact-production-artifact-attestation-and-rollback-discipline — use it when the sharper question is how reviewed source, finalized setup artifacts, exported verifier material, and deployed constants should have remained one attested production object instead of drifting into a fundable placeholder-family deployment.
- Adjacent proof-statement family: witness-public-input-binding-failure — there the verifier key may be sound, but the circuit proves the wrong statement.
- Adjacent proof-consumer family: proof-to-effects-binding-failure — there the proof can be sound, but the settlement consumer honors a different effect set.
Control surface
-
The leverage sits in ceremony artifacts, exported verification keys, deployed verifier constants, and application entrypoints that let verifier success directly authorize withdrawals or claims.
-
The recurring mistake is operational and cryptographic at once: teams audit circuits and verifier code, then quietly ship an unsound verification key that makes both irrelevant.
-
Sources:
- https://blog.zksecurity.xyz/posts/groth16-setup-exploit/
- https://github.com/DK27ss/VeilCash-5K-PoC
- https://github.com/DK27ss/FoomClub-1.6M-PoC/blob/main/README.md
- https://github.com/iden3/snarkjs/blob/9a8f1c0083d18b9b5e18f526cfd729e7259423be/src/zkey_new.js#L118-L129
- https://github.com/iden3/snarkjs/blob/9a8f1c0083d18b9b5e18f526cfd729e7259423be/src/zkey_contribute.js#L63-L64
-
Last reviewed: 2026-07-28 UTC