The diff was three lines. It deleted a Num2Bits(64) range check on an intermediate signal and cut proof generation time by 31% on the team's benchmark — the number that went into the investor deck. It also meant any prover could supply a witness where a note's value was negative in the scalar field, wrapped around the modulus, and landed inside a range the commitment scheme accepted without complaint.
That diff shipped. Nobody flagged it. The audit that preceded it covered the Solidity verifier, the upgrade proxy, and the bridge escrow. The circuit — the arithmetic object that actually decides whether a withdrawal is legitimate — was out of scope, because the firm holding the contract did not have a circuit auditor on staff and did not say so.
I have read a version of that diff roughly forty times. Circom, Noir, Cairo, Halo2, Plonky3, SP1. The syntax rotates with the funding cycle. The failure mode does not move.
Under-constrained circuits are not a bug class. They are the resting state of any circuit that gets optimized before it gets differentially tested against its own specification.
A zero-knowledge circuit is not a program, and this is where engineering intuition breaks.
A program says: compute this. A circuit says: prove that these signals satisfy these relations. The prover is not a compiler obeying you. It is an adversary solving a puzzle, and every relation you forgot to write is a degree of freedom you handed it.
The canonical case is still Zcash. In 2019, a researcher found that a single bit inside the Sapling circuit's Pedersen hash gadget had never been constrained to be boolean. That gadget sits inside the note commitment. A prover who controlled that bit could construct a commitment to a note that never existed, spend it, and mint ZEC from nothing — silently, with a valid proof, at any scale. The fix was a handful of constraints. The exposure was the entire supply.
I was writing up the Sapling trusted-setup ceremony around that time, working through Groth16's MPC ritual line by line, because the mathematics of it was elegant and the operational security of it was terrifying. The thing that should have kept me awake was not the ceremony. It was the constraint system sitting beside it, unread.
We spent a decade ritualizing trust out of the trusted setup and then moved it, wholesale, into a constraint system that nobody reads.
That relocation is the story of this cycle. Groth16 required a per-circuit ceremony. PLONK, Halo2, KZG, and the transparent STARK-family setups do not. So the industry did the rational thing and switched. The universal setup removed a ceremony and, with it, removed the class of failure everyone had built vocabulary for. The security did not disappear. It migrated into the constraints, where it is now purely a property of code, and where the incentive to look is weakest.
Here is the taxonomy, roughly in order of how much money each has moved.

Missing range checks. Field elements wrap. The uint64 in your head is a 254-bit scalar in the arithmetic. Subtract one from zero and you get p − 1, a perfectly legal field element that every downstream multiplication will accept.
Unconstrained signals. The witness generator computes the value correctly. Nothing in the constraint system asserts anything about it. The honest prover is fine forever. The dishonest prover is unconstrained forever.
Mis-specification. The circuit is fully constrained and it constrains the wrong statement. Every signal has a relation. Every relation is enforced. The statement is simply not the one the protocol believes it is.
Under-constrained public inputs. The verifier contract accepts a proof, binds it to fewer public signals than the protocol assumes, and lets a valid proof be replayed against a different statement.
Aliasing. A value that is a field element inside the circuit and an integer inside the contract, bridged by a uint256 cast with no range check in between.
Tools exist. circomspect, Ecne, Picus, and Circom's own --inspect flag, which catches a meaningful slice of unconstrained signals before they ever leave a developer's laptop. Picus, published in 2023, performs automated detection of under-constrained circuits and it works on production Circom code. That is genuine progress and it deserves more credit than it gets.
They catch degree-of-freedom bugs. They do not catch mis-specification, and mis-specification is where the money is. No static analyzer can tell you that you proved the wrong theorem. That requires a human who knows what the protocol was supposed to mean, reading constraints against a specification, one relation at a time. There are perhaps two hundred people on earth who do this competently, and most of them are booked.
The deeper problem is that a circuit has no natural unit test. The property you need is universally quantified: for all witnesses satisfying the constraints, the extracted statement is the intended one. Testing samples. Sampling a space of 2^254 elements demonstrates nothing at all. The only honest verification is a refinement proof — write the specification formally, prove the circuit refines it — and I know of roughly four teams doing that, all of whom spend more on the proof than on their go-to-market.
Now the part that should worry you more than the bugs.
Proof generation time is the only circuit metric with an investor-facing number. Constraint count drives proving cost. Every optimization that matters in practice is a trade: custom gates, lookup arguments, folding schemes, recursion, aggregation. Each one buys latency by moving complexity somewhere the constraints no longer see it.
Lookup arguments are the clearest case. Plookup and its descendants replace expensive constraints with table membership, and they are genuinely sound — when the table is fixed and committed correctly. When the table is prover-supplied, or the multiplicity polynomial is under-constrained, the same argument becomes a machine for asserting falsehoods at a discount.
Folding schemes do this at a different layer. Nova and its descendants replace many constraint checks with a single relaxed R1CS check plus an accumulation step. The constraint count falls. The number of ways to get the accumulation wrong rises, and it rises in a place that no longer resembles the circuit.
Proving time and soundness margin are the same variable, measured from opposite ends.
I have skin in this. In the ZK-rollup standardization proposal I co-authored in 2024, our headline result was a 40% reduction in proof generation time, achieved through a different polynomial commitment scheme and a tighter arithmetization. That number is real and I stand behind it. So is the fact that the soundness argument now rests on three assumptions about the accumulation step that the naive construction did not require. We documented them in section four. Almost nobody read section four.
And even a flawless circuit is only as good as its inputs. A proof is a notarized statement about numbers it was handed. If the price arrives from a handful of permissioned nodes reporting into a feed whose update path is a 3-of-5 multisig, the circuit has become a notarization service for whatever those five keys decide. The constraint system is not the trust boundary. The trust boundary is wherever the inputs originate, and in most deployed rollups that is a sequencer which is one externally owned address behind a small multisig, wrapped in a governance token that has never once been used to vote against the foundation.

So why does this persist in a market with nine-figure rounds?
Because audits are priced by hours and scoped by pages. Nobody quotes for "prove this circuit refines this specification," because that sentence has no standard hour estimate and no standard deliverable. What gets purchased is a report. What the report purchases is a logo. What the logo purchases is a term sheet.
The teams know. The investors know. The incentive is not to be secure. It is to be legibly secure, and legibility has a price ceiling that soundness does not.
Bug bounties do not fix this shape of risk either. A circuit bug is worth more undetected than reported, because exploitation leaves no trace. The chain history looks normal. The withdrawal is valid. The proof verifies. Compare that to a bridge exploit — loud, immediate, telegenic — and you can see precisely which failure mode receives the bounty budget.
The vulnerabilities that matter this cycle are the ones that produce no anomalous event.
Here is the forecast.
The next eighteen months of rollup funding will be spent buying latency, because latency is the only thing users can feel and the only thing investors can chart. Every millisecond of proving time removed will be paid for somewhere, and a fraction of that payment will come out of constraints that were holding the money in. Those constraints will not announce themselves as security. They will look like overhead.
The failure will not look like a hack. It will look like a withdrawal. Valid proof, correct verifier, normal gas, no alert fired.
Privacy is a protocol, not a policy. Solvency is too.
