Chapter 23: Composition and Recursion

Could you build a proof system that runs forever? A proof that updates itself every second, attesting to the entire history of a computation, but never growing in size?

The only way to keep a proof from growing is to compress it at every step. That means each new proof must verify the previous proof and then replace it, absorbing all the history into a fixed-size certificate. The proof system must verify its own verification logic, "eating itself." For years, this remained a theoretical curiosity, filed under "proof-carrying data" and assumed impractical.

This chapter traces how the impossible became routine. We start with composition: wrapping one proof inside another to combine their strengths. We then reach recursion: proofs that verify themselves, enabling unbounded computation with constant-sized attestations. Finally, we arrive at folding: a recent revolution that makes recursion cheap by deferring verification entirely. The destination is IVC (incrementally verifiable computation), where proofs grow with time but stay constant-sized. Today's zkEVMs and app-chains are built on this foundation.

No single SNARK dominates all others. Fast provers tend to produce large proofs. Small proofs come from slower provers. Transparent systems avoid trusted setup but sacrifice verification speed. Post-quantum security demands hash-based constructions that bloat proof size. Every deployed system occupies a point in this multi-dimensional trade-off space.

But here's a thought: what if we could combine systems? Use a fast prover for the heavy computational lifting, then wrap its output in a small-proof system for efficient delivery to verifiers. Or chain proofs together, where each proof attests to the validity of the previous, enabling unlimited computation with constant verification.

These ideas, composition and recursion, transform SNARKs from isolated verification tools into composable building blocks. The result is proof systems that achieve properties no single construction could reach alone.

Composition: Proving a Proof Is Valid

Composition means generating a new proof that an existing proof was valid. The distinction from verification is that the output is itself a proof, not a yes/no verdict. You have a proof of some statement. Verifying is a computation. You express that verification as a circuit, then prove the circuit was satisfied. The result is a second proof that attests to 's validity, potentially with different properties (smaller, faster to verify, based on different assumptions) than itself.

Why do this? Different proof systems have different strengths. A STARK proves quickly but produces a 100KB proof. Groth16 produces a 128-byte proof but proves slowly. Composition lets you have both: prove the computation with a STARK, then compose the result into Groth16 for compact delivery. The formal treatment below shows why this combination inherits the fast prover of the first system and the small proof of the second, without either system's weakness dominating.

Inner and Outer

The names inner and outer describe the nesting:

  • The inner proof is created first. It proves the statement you actually care about ("I executed this program correctly," "I know a secret satisfying this relation").

  • The outer proof is the wrapper, created second. It proves "I ran the inner verifier and it accepted."

flowchart TB
    subgraph inner["INNER PROOF (fast prover, large proof)"]
        I1["Statement: 'I know w such that C(w) = y'"]
        I2["Inner Prover"]
        I3["Inner Proof π"]
        I1 --> I2 --> I3
    end

    subgraph outer["OUTER PROOF (small-proof system)"]
        O1["Statement: 'The inner verifier accepted π'"]
        O2["Outer Prover"]
        O3["Outer Proof π'"]
        O1 --> O2 --> O3
    end

    subgraph delivery["DELIVERY"]
        D1["Verifier receives only π'"]
        D2["Verifier checks π'"]
        D3["✓ Original statement validated"]
        D1 --> D2 --> D3
    end

    I3 -->|"becomes witness for"| O1
    O3 -->|"π discarded"| D1

The verifier of the outer proof never sees the inner proof or the original witness. They see only and check that it's valid. If the outer system is zero-knowledge, nothing leaks about or .

Think of it like nested containers: the inner proof is a large box containing detailed evidence. The outer proof is a small envelope containing a signed attestation that someone trustworthy opened the box and verified its contents. Recipients need only check the signature on the envelope.

The Composition Construction

Composition works because verification is itself a computation, and any computation can be proven. To see why the composed system inherits the best of both components, we trace the construction step by step and analyze its cost.

Consider two SNARKs with complementary profiles. Let denote the original circuit size.

Inner SNARK (fast prover, large proofs): prover time , proof size , verification time . Example: STARK-like systems.

Outer SNARK (slow prover, tiny proofs): prover time , proof size , verification time . Example: Groth16.

Step 1: Run the inner prover. The prover executes on the original circuit with witness , producing proof . Cost: .

Step 2: Arithmetize the inner verifier. The verification algorithm of the inner SNARK is a computation: it reads the proof, performs checks, outputs accept or reject. Express this verification as a circuit with public input (the original statement), witness , and output 1 iff accepts. Since has verification time, , far smaller than .

Step 3: Run the outer prover. The prover executes on the verifier circuit , using as the witness. Cost: , since the outer prover is superlinear but operates on a circuit of size , not .

Step 4: Deliver only the outer proof. The prover discards and sends only to the verifier. The inner proof was a means to an end; it never leaves the prover's machine.

Step 5: Verify. The end verifier runs on . Cost: (inherited from the outer system). The verifier never sees or .

The cost analysis makes the payoff visible. Total prover time is , dominated by the fast inner prover. The slow outer prover contributes negligibly because it only processes the small verifier circuit. Proof size and verification time both inherit from : constant and fast.

For a concrete sense of scale: a million-gate circuit () might take 5 seconds to prove with the inner STARK, producing a proof the verifier can check in operations. The verifier circuit has gates. Groth16 proves that 1000-gate circuit in about 1 second. Total: seconds. Proof size: bytes. Verification: 3 pairings. Without composition, running Groth16 directly on the full circuit would take minutes.

Soundness, witnesses, and delivery

The original witness is consumed entirely in Step 1. The outer proof's witness is (the inner proof), not . The outer system proves "I possess a valid inner proof," not "I know the original witness." The soundness chain is:

The outer proof transitively guarantees the original statement without directly involving . Only is delivered; is discarded. If the outer system is zero-knowledge, nothing leaks about or .

Field mismatches and verifier circuit blowup

The analysis above assumed the inner verifier circuit is small and easy to express in the outer system. But what if the inner and outer systems speak different languages? STARKs operate over one field; Groth16 operates over another. Encoding foreign field arithmetic can blow up the verifier circuit by orders of magnitude. Trusted setup requirements, field mismatches, and post-quantum concerns all constrain which combinations actually work. The later sections on The Verifier Circuit Problem and Curve Cycles address these issues in detail.

Adding Zero-Knowledge

Here's a bonus. Suppose the inner SNARK lacks zero-knowledge: some STARK variants reveal execution traces that leak witness information. But the outer SNARK is fully ZK.

The composed system inherits zero-knowledge from the outer layer. The final proof proves knowledge of a valid inner proof without revealing itself. Since depends on the witness , hiding suffices to hide .

The inner SNARK's lack of ZK is encapsulated and hidden by the outer layer.

Recursion: Composing with Yourself

If composing two different SNARKs is useful, what about composing a SNARK with itself?

Shrinking the verifier tower

Take a hypothetical SNARK where verifying a proof for a circuit of size costs operations. (This is pedagogical; real SNARKs have verification like Groth16, or like STARKs. The gives clean math for illustration.)

Now trace what happens when we recurse:

Layer 0: Prove the original circuit (size ). This produces proof . Verifying costs operations.

Layer 1: Wrap in another proof. The circuit being proved is now the verifier for , which has size . This produces . Verifying costs operations.

Layer 2: Wrap . The circuit is the verifier for , size . Verifying costs operations.

The pattern: each layer proves "the previous verifier accepted," and since verifiers are smaller than the circuits they verify, each layer's circuit shrinks.

After layers:

Verification cost reaches a constant after layers, which is the recursion threshold. The derivation is short: we need for some constant , giving , so . Each layer halves the exponent; doing this times reduces it to a constant.

We are not proving the original circuit over and over. Each layer proves a different (smaller) circuit: the verifier of the previous layer. The shrinking comes from the fact that verification is cheaper than computation.

Proof of Proof of Proof...

From the prover's perspective, deep recursion means building a tower of proofs:

  1. : proves "I know witness satisfying circuit "
  2. : proves "I know a valid proof "
  3. : proves "I know a valid proof "
  4. Continue until the verifier circuit is minimal

Each is a proof about the previous proof. The final can be verified in constant time regardless of the original computation's size.

The Strange Loop

A proof that proves a proof that proves a proof: the structure feels like it should be paradoxical. Gödel showed that sufficiently powerful formal systems can express statements about their own provability, and this self-reference produces incompleteness. "This statement is unprovable" is a sentence the system can formulate but cannot resolve.

Recursive SNARKs avoid the trap because they ask a different question. Gödel's self-reference asks "is this provable?", a meta-logical assertion the system cannot settle about itself. Recursive SNARKs ask "is this verifiable?", and verification is a concrete, bounded computation: read the proof, check some equations, output accept or reject. A proof system can prove statements about its own verifier for the same reason it can prove statements about any other circuit. The self-reference leads not to paradox but to compression.

The Extraction Caveat

Everything above assumed recursive SNARKs are sound. They are, in practice. But the standard way of proving soundness breaks down with recursion depth, and understanding why reveals a genuine gap between what we can prove and what we believe.

The problem in one sentence: proving a SNARK is secure requires running the attacker many times to extract a witness, and each layer of recursion multiplies the number of runs exponentially. At depth , the security proof requires runs of the attacker, where is the extraction cost per layer. For and , this is operations, far beyond anything meaningful. The security theorem degrades to vacuity even though no one can actually break the system.

To see where this exponential comes from, we need to trace how SNARK security proofs work. We cannot prove a cryptographic system is secure in an absolute sense (that would require proving and more). Instead, we prove relative security: "if someone can break system X, they can also break problem Y." If we believe Y is hard, then X must be hard too.

A SNARK security proof constructs an algorithm (the "reducer") that treats any successful attacker as a black box. If the attacker can forge proofs, the reducer extracts a valid witness from those proofs. The witness encodes a solution to a hard problem like discrete log, because the commitment scheme was constructed so that knowing a valid witness implies knowing a discrete log. Since we believe discrete log is hard, forging proofs must also be hard.

The extraction step is where the cost enters. To extract a witness, the reducer uses rewinding: run the prover once, record its state, then rewind to an earlier point and run it again with a different random challenge. Two runs with different challenges on the same commitment overdetermine the witness.

Worked example (rewinding in a -protocol). Consider a -protocol (Chapter 16) where the prover sends commitment , receives challenge , and responds with . The extractor recovers the witness as follows:

  1. Run the prover. It sends , you send challenge , it responds with .
  2. Rewind to just after the prover sent . (In a proof, we model the prover as a stateful algorithm we can checkpoint and restore.)
  3. Send a different challenge .
  4. The prover responds with .
  5. From and with the same , algebraically solve for the witness.

This works because -protocols have special soundness (Chapter 16): the commitment fixes enough structure that two different challenge-response pairs overdetermine the witness. In Schnorr's protocol, for instance, where is the secret. Two transcripts give , so . Not all interactive proofs have this property, but -protocols are designed for it.

The -protocol example needed only rewinds (two transcripts with different challenges). More complex SNARKs may need more: a protocol with rounds of interaction generally requires rewinds, where is the degree of the round polynomials. For a modern SNARK, might be in the hundreds. Recursion compounds this cost. For a single-layer proof, extraction costs prover runs. For a 2-layer recursive proof, you must:

  1. Extract the inner proof from the outer layer: runs
  2. For each of those runs, extract the witness from : more runs each
  3. Total: runs

For depth : runs. At depth 100 with , that's operations.

This breaks the security proof. Security theorems have the form: "if an attacker breaks the SNARK, our reducer solves discrete log."

But the reducer must be efficient. If the reducer takes operations to extract a witness, the theorem becomes: "if an attacker breaks the SNARK, discrete log can be solved in operations." This is useless. We already know discrete log can be brute-forced in operations. The reduction no longer rules out attackers, and the provable security level drops accordingly: each additional layer of recursion multiplies the reducer's running time by , weakening the security guarantee by bits per layer.

To be clear: this degradation is in the provable bound, not in the system's actual resistance to attack. More rewinds doesn't make the system easier to break. It makes our proof technique too slow to demonstrate security. The reducer's inefficiency is a problem for the theorist writing the proof, not for the attacker trying to exploit the system.

In practice, the system might be perfectly secure. No one has found attacks that exploit the recursive structure, and the underlying hard problems (discrete log, collision resistance) remain hard. What breaks is not the system but the proof technique: standard reductions become too expensive to carry through, so the security theorem degrades even though the system itself does not weaken.

The practical heuristic for recursive SNARKs is therefore: security degrades on paper with recursion depth, but not in reality. A system with 100 layers of recursion has the same concrete security as one with 2 layers (no known attack exploits the depth), but its provable security guarantee is weaker because the reduction's running time grows as . This parallels the random oracle model, where hash functions are used in ways that resist all known attacks but lack full theoretical justification. Practitioners accept the gap and ship; researchers work on tighter proof techniques (folding schemes, discussed next, partly sidestep this issue by avoiding deep recursion entirely).

The Curve Cycle Problem

For pairing-based SNARKs like Groth16, recursion faces a fundamental obstacle: field mismatch.

Two Fields, One Problem

Every pairing-based SNARK involves two distinct fields. To understand why, recall how elliptic curve cryptography works.

An elliptic curve is defined over a base field . Points on the curve have coordinates where . When you add two points or compute (scalar multiplication), you're doing arithmetic in : additions, multiplications, and inversions of these coordinates.

But the scalars live in a different field. The curve's points form a group under addition, and this group has an order : the number of elements in the group. For any point , we have (the identity). Scalars are integers modulo , giving us the scalar field .

A concrete example with BN254 (the curve Ethereum uses for precompiles):

  • Base field: where (coordinates of curve points live here; all curve arithmetic, including point addition and pairings, is computed over )
  • Scalar field: where (a completely different prime of similar size; scalars in , witness values, and circuit constraints live here)
  • A point on the curve: with
  • A Groth16 proof element: where (scalar field) and the result is a curve point with coordinates in (base field)

Where each field appears in Groth16:

  • Scalar field : Your circuit's witness values and all constraint equations. If you're proving "I know such that ," then . The reason constraints must live in is that the commitment scheme requires it. In Groth16, committing to a witness value means computing (a scalar multiplication on the curve). For this to be a well-defined group operation, must be an element of , because the curve group has order and scalars are taken modulo . The constraint system inherits this field because it constrains the same values the commitment scheme binds.

  • Base field : The proof elements themselves. The proof consists of elliptic curve points, which have coordinates in . Verification requires point additions and pairings, all computed over .

In a single proof, the two fields coexist without friction. The prover uses both (scalars from feed the constraint system and commitment scheme; curve arithmetic in constructs the proof elements), but the constraint system lives entirely in . The verifier checks the proof using operations (pairings, point arithmetic), but that happens outside any circuit. Recursion forces the two fields into the same circuit. When the inner proof becomes the witness of the outer circuit, the outer circuit must verify , which means performing pairing checks and point arithmetic on the coordinates of . Those coordinates are elements. But the outer circuit's constraints are polynomial equations over (the scalar field of whatever curve the outer system uses). The outer circuit must therefore manipulate values using arithmetic.

To do arithmetic inside an circuit, you must emulate it: decompose each element into multiple -sized limbs and implement multiplication with schoolbook carry propagation, as Chapter 13 described for non-native arithmetic in PLONK. A single multiplication can cost 50-100+ constraints. The verifier circuit explodes from a few thousand native operations to hundreds of thousands of emulated ones.

Two terms clarify this distinction. When we say arithmetic is native, we mean it's cheap inside the circuit: one field operation becomes one constraint. A circuit over can do arithmetic natively. It must emulate arithmetic, paying 100+ constraints per operation. The curve cycle trick ensures we're always doing native arithmetic by switching fields at every recursive step.

Cycles of Curves

For single composition, the fix is straightforward: choose an outer curve whose scalar field matches the inner curve's base field. If the inner verifier does arithmetic, use an outer system over . One wrap, native arithmetic, done.

For deep recursion, this isn't enough. After wrapping once, you have a new proof whose verifier does arithmetic in some other field. To wrap again natively, you need yet another matching curve. The solution is a cycle of elliptic curves :

  • has scalar field and base field
  • has scalar field and base field

The fields swap roles between curves. Recursion alternates:

  • Proof on curve : verifier performs arithmetic
  • Proof on curve : verifier performs arithmetic, can natively prove about 's verification
  • Proof on curve : can natively prove about 's verification
  • And so on indefinitely

Each step's verifier circuit uses native field arithmetic. The alternation continues as long as needed, with no expensive cross-field emulation at any layer.

Practical Curve Cycles

Pasta curves (Pallas and Vesta): A true cycle, meaning the scalar field of each equals the base field of the other. This enables unbounded recursion: the prover alternates between the two curves at every step, and each step's verifier circuit uses native arithmetic because the next curve's scalar field matches the previous curve's base field. Neither curve is pairing-friendly, but both support efficient Pedersen commitments and inner-product arguments. Used in Halo 2 and related systems.

BN254 / Grumpkin: Also a true cycle (Grumpkin is obtained by swapping BN254's base and scalar fields), enabling the same unbounded alternating recursion as Pasta. The difference is asymmetry: BN254 is pairing-friendly while Grumpkin is not, so the cycle alternates between pairing-based proofs (on the BN254 side) and inner-product-based proofs (on the Grumpkin side). Since BN254 has Ethereum precompiles, proofs that land on the BN254 side can be verified cheaply on-chain. Aztec uses this cycle for their rollup architecture.

The choice between cycles depends on the deployment target. BN254/Grumpkin is the default when on-chain Ethereum verification matters, because BN254 has EVM precompiles and pairings enable constant-size final proofs (Groth16-style). The cost is a trusted setup on the BN254 side. Pasta is preferred when transparency matters (IPA on both sides, no trusted setup), as in Zcash's Orchard protocol via Halo 2, but lacks EVM support and produces larger proofs.

BLS12-377 / BW6-761: Both curves are pairing-friendly, giving pairings on both sides of the recursion (unlike BN254/Grumpkin where only one side has pairings). BW6-761's scalar field matches BLS12-377's base field, allowing native verification of BLS12-377 proofs. The pair is called a "half-cycle" because the match goes in one direction only (BW6-761 can natively verify BLS12-377 proofs, but not vice versa), so it supports efficient one-step composition rather than unbounded alternating recursion. Aleo uses this pair for their proof system.

A related curiosity: embedded curves. BabyJubjub is defined over BN254's scalar field , so BabyJubjub point operations can be expressed natively as BN254 circuit constraints. This enables in-circuit cryptography: EdDSA signatures, Pedersen hashes, and other EC-based primitives. BabyJubjub doesn't form a cycle with BN254 (its group order is much smaller than BN254's base field), so it cannot be used for recursion. The reader might wonder why Grumpkin doesn't replace BabyJubjub entirely, since both have their base field equal to BN254's scalar field. In principle it could: any curve with the right base field supports native in-circuit arithmetic. The reason BabyJubjub persists is practical. It is a twisted Edwards curve with complete addition formulas (no special cases for doubling or identity), making in-circuit point addition slightly cheaper than on Grumpkin (a short Weierstrass curve). It also predates Grumpkin and has years of deployed implementations and audited libraries. In a greenfield design you could use Grumpkin for both roles; in existing ecosystems the two curves coexist because they were optimized for different concerns.

Finding curve cycles is mathematically delicate. The size constraints (both fields must be large primes), security requirements (curves must resist known attacks), and efficiency demands (curves should have fast arithmetic) severely restrict the design space.

Incrementally Verifiable Computation (IVC)

Composition combines different proof systems; the recursion we've seen so far compresses proofs through towers of wrapping. But there is a different problem that recursion solves, one that isn't about shrinking proofs or mixing systems: proving computation that hasn't finished yet.

A blockchain processes transactions one by one. A verifiable delay function (VDF) computes a hash chain for hours, proving that real time elapsed. A zkVM executes a program instruction by instruction. In each case, the computation is sequential: step depends on step . You can't parallelize it. You can't wait until the end to start proving (the end might be days away, or never).

What you want is a proof that grows with the computation. After step 1, you have a proof of step 1. After step 1000, you have a proof of steps 1 through 1000. Crucially, the proof at step 1000 shouldn't be 1000× larger than the proof at step 1. And creating the proof for step 1000 shouldn't require re-proving steps 1 through 999.

This is incrementally verifiable computation, or IVC: proofs that extend cheaply, verify in constant time, and accumulate the history of an unbounded sequential process. The term appears throughout the literature; systems like Nova, SuperNova, and ProtoStar are "IVC schemes."

The Setting

Consider a function iterated times:

For iterations, directly proving this requires a circuit of size : billions of gates. Even fast provers choke on circuits this large. And you'd have to wait until iteration completes before generating any proof at all.

The Incremental Approach

Generate proofs incrementally, one step at a time:

  • : trivial (base case, no computation yet)
  • : proves " and I know a valid "
  • : proves " and I know a valid "
  • : proves " and I know a valid "

Each has constant size and proves the entire computation from to . The proof for step subsumes all previous proofs.

The Recursive Circuit

At step , the prover runs a circuit that:

  1. Verifies : Checks that the previous proof is valid
  2. Computes : Performs one step of the function
  3. Produces : Outputs a new proof

The circuit size is : the cost of verifying the previous proof plus the cost of one function evaluation. The overhead of recursion is , the verifier circuit size.

For a SNARK with efficient verification, might be a few thousand gates. If is also a few thousand gates (a hash function, say), the overhead roughly doubles the per-step cost. For larger , the overhead is proportionally smaller.

Where IVC Shines

Verifiable Delay Functions (VDFs). The canonical example: repeated squaring for an RSA modulus . Each squaring depends on the previous result; you can't compute faster than sequential multiplications (without knowing the factorization of ). After computing , the prover produces a proof that is correct, verifiable in time much less than . IVC is natural here: the function is inherently sequential, and the proof accumulates with each step.

Succinct Blockchains. Each block contains a proof that:

  1. This block's transactions are valid
  2. The previous block's proof was valid

A new node syncing to the chain verifies a single proof (the most recent one) rather than replaying every transaction since genesis. Mina Protocol pioneered this approach.

Proof Aggregation. Multiple independent provers generate separate proofs. An aggregator combines them into one proof via recursive composition. Batch verification becomes constant-time regardless of the number of original proofs.

Folding Schemes: Cheaper IVC

The IVC construction above requires the prover to fully verify the previous proof at every step. This verification is itself a circuit (), and it can be large. If the verifier circuit has gates and the step function has gates, the prover spends 90% of each step proving it verified correctly, only 10% proving it computed correctly. For tiny (say, a single hash invocation), the ratio gets even worse. The verification overhead, not the computation itself, becomes the IVC bottleneck.

Folding schemes address this by replacing verification with something cheaper.

Folding instead of verifying

Instead of fully verifying at step , we fold the claim about step with the claim about step . Folding combines two claims into one claim of the same structure, without verifying either. The accumulated claim grows no larger than each individual claim. Verification is deferred entirely: the prover folds at every step (a few group operations each) and produces a single real SNARK proof only at the very end. The cost of folding per step is drastically cheaper than the cost of in-circuit verification, which is what makes IVC practical for millions of steps.

Nova's Approach

Nova, the pioneering folding scheme (Kothapalli, Setty, Tzialla, 2021), introduced a modified constraint system: relaxed R1CS. Recall from Chapter 19's Spartan section that standard R1CS demands:

where are sparse matrices and is the entrywise (Hadamard) product. This is the same equation Spartan proves via sum-check. Nova adds two slack terms to make folding possible:

where is a scalar and is an "error vector." A standard R1CS instance has and . Relaxed instances can have and , but satisfying a relaxed instance still proves something about the underlying computation.

Why Relaxation Enables Folding

What makes relaxed R1CS useful is that instances can be linearly combined. Suppose we want to fold two instances by taking a random linear combination with challenge :

This is the core of folding: two separate witnesses and become a single witness . The folded witness has the same dimension as the originals; we're not concatenating, we're combining. Think of it geometrically: and are points in ; the fold is another point on the line through them, selected by the random challenge .

What happens when we plug this combined witness into the constraint? Let's compute :

The Hadamard product distributes, but it creates cross-terms: the middle expression mixes the two instances. This is the "interaction" between them.

For standard R1CS, these cross-terms would break everything: the equation has no room for interaction terms that belong to neither instance. This is exactly why relaxation was introduced. The error vector acts as a "trash can" that absorbs the cross-terms, keeping the equation valid despite the algebraic mess that linear combination creates. Define:

This is the cross-term vector. To understand where each term comes from, recall that each relaxed R1CS instance has its own slack factor: instance 1 has and instance 2 has . When we expand the right side of the relaxed constraint using the folded values and :

The coefficient of on the right side is . On the left side (the Hadamard product expansion above), the coefficient of is . The cross-term is exactly the difference between these: what the left side produces at minus what the right side produces at . This mismatch gets absorbed into the error vector.

Note that the coefficient works out automatically: the left side gives and the right side gives , which is exactly instance 2's original constraint (up to ). The folded error absorbs the second instance's error at .

The Folding Protocol

A relaxed R1CS instance consists of a witness vector , an error vector , and a slack scalar . A fresh (non-folded) instance has and ; after folding, both accumulate non-trivial values.

Given two instances and , the protocol folds them into one. But the verifier doesn't see the actual witness vectors, since that would defeat the point. Instead, the verifier works with commitments.

What the verifier holds: Commitments to the witness vectors, commitments to the error vectors, and the public scalars . (Public inputs are also visible, but we omit them for clarity.)

The protocol:

  1. Prover computes the cross-term : The formula above requires knowing both witnesses, so only the prover can compute it.

  2. Prover commits to : Sends commitment to the verifier. This is the only new cryptographic operation per fold.

  3. Verifier sends random challenge .

  4. Both compute the folded instance using commitments:

    • (the verifier computes this from the commitments)
    • (public scalars, both can compute)
    • (again from commitments)

The verifier never sees , or directly. They work entirely with commitments. Because commitments are additively homomorphic (Pedersen commitments satisfy ), the folded commitment is a valid commitment to the folded witness , which only the prover knows.

Meanwhile, the prover computes the actual folded witness and the actual folded error . The prover holds these for the next fold (or for the final SNARK).

The folded error vector absorbs the cross-terms at the coefficient and the second instance's error at the coefficient. This is exactly what makes the constraint hold: the expansion of produces terms at powers , , and , and the folded and absorb them all.

Expanding using the folded values shows why this is sound: all terms cancel if and only if both original instances satisfied their constraints. The random acts as a Schwartz-Zippel check: a cheating prover who folds two unsatisfied instances would need the folded instance to satisfy the constraint, but this happens with negligible probability over random .

Two claims have become one, without verifying either. The prover paid the cost of one commitment (to ) and some field operations. No expensive SNARK proving.

IVC with Folding

Now we connect the folding protocol to the IVC setting from earlier in the chapter. Recall the problem: prove for large without circuits that grow with .

The folding protocol combines two relaxed R1CS instances into one. For IVC, we maintain a running instance that accumulates all previous steps and fold in each new step as it happens.

What gets folded: At each step, we have two things:

  • The running instance , where is the Pedersen commitment to the accumulated witness, is the commitment to the accumulated error vector, and is the accumulated scalar. Together these represent "all steps so far are correct."
  • The step instance , a fresh claim that "step was computed correctly." It is always a standard (non-relaxed) R1CS instance: and .

The step instance is always fresh: and because it comes from a standard (non-relaxed) R1CS. Only the running instance accumulates non-trivial slack.

The IVC loop in detail:

Step 0 (Base case): Initialize the running instance to a trivial satisfiable state. No computation yet.

Step (for ):

  1. Compute: Execute

  2. Create the step instance: Express "" as an R1CS constraint. Build the witness vector (the same object as or in the folding derivation above), encoding the input , output , and any intermediate values the step function produces. Commit to get .

  3. Fold: The two inputs to the folding protocol are the running instance from the previous iteration and the step instance just created. This is exactly the two-instance fold from the derivation above, with the running instance playing the role of instance 1 and the step instance playing instance 2:

    • Prover computes the cross-term (from the two witnesses and ) and commits to get
    • Challenge is derived via Fiat-Shamir from the transcript
    • Both parties compute the new running instance using the homomorphic update:
      • (fold the witnesses)
      • (fold the scalars)
      • (absorb the cross-term)
    • The error update is where the cross-terms go. Recall from the Hadamard expansion that folding produces a degree-1 cross-term in . The term absorbs it into the error commitment; the term accounts for the step instance's error (which is zero for a fresh instance, so this term vanishes in practice).
    • Prover updates the actual witnesses (which only the prover holds): ,
  4. Repeat: The new running instance becomes input to step

After steps: The prover holds a final running instance with (accumulated from all the folds). This single instance encodes the claim "all steps were computed correctly." The individual steps, their witnesses, and their cross-terms have all been absorbed into the running instance's commitments and scalar. No trace of the intermediate states remains.

The final SNARK: The prover now produces one conventional SNARK proof demonstrating that the running instance is satisfiable, i.e., that there exists a witness and error vector such that:

This is the only expensive cryptographic operation in the entire protocol. The preceding folds each cost only a few group operations (one commitment to , one scalar multiplication per running-instance component). The full SNARK proof happens once, at the end, not at every step.

The verifier's job is correspondingly simple. It receives three things: the final running instance , the SNARK proof for that instance, and the claimed output . It verifies the SNARK (confirming the running instance is satisfiable) and checks that matches the output encoded in the instance. If both pass, the verifier is convinced that . It never sees any of the intermediate states, witnesses, or fold challenges. The entire computation history has been compressed into one relaxed R1CS instance and one proof.

Security Considerations for Folding

Folding schemes have a reputation in the zkVM community for being where security problems arise. This isn't accidental; the architecture creates several subtle attack surfaces.

Deferred verification. Traditional recursion verifies at each step: if something is wrong, you catch it immediately. Folding defers all verification to the final SNARK. Errors compound silently across thousands of folds before manifesting. Debugging becomes archaeology, trying to identify which of 10,000 folds went wrong.

The commitment to must be binding. The cross-term must be committed before the verifier sends challenge . If the prover can open this commitment to different values after seeing , soundness breaks completely: the prover can fold unsatisfied instances and make them appear satisfied. Nova uses Pedersen commitments (computationally binding under discrete log), so breaking the binding property would require solving discrete log. But implementation bugs in commitment handling have caused real vulnerabilities.

Accumulator state is prover-controlled. Between folding steps, the prover holds the running accumulated instance . The final SNARK proves this accumulated instance is satisfiable, but doesn't directly verify it came from honest folding. A malicious prover who can inject a satisfiable-but-fake accumulated instance breaks the chain of trust. The "decider" circuit must carefully check that public inputs match the accumulator state.

Soundness error accumulates. Each fold uses a random challenge to combine two instances. A cheating prover escapes detection only if the degree- cross-term identity accidentally holds at that , which Schwartz-Zippel bounds at probability per fold. Over independent folds, a union bound gives total soundness error . For folds over a 256-bit field with , this is , negligible. But for smaller fields or exotic parameters, verify the concrete security.

Implementation complexity. Folding has more moving parts than traditional recursion: cross-term computation, accumulator updates, commitment bookkeeping, the interaction between folding and the final decider SNARK. Each is a potential bug location. Several folding implementations have had soundness bugs discovered post-audit. The abstraction is elegant, but the implementation details are unforgiving.

Post-quantum vulnerability. Every folding scheme discussed in this chapter (Nova, HyperNova, ProtoStar) uses Pedersen commitments for the cross-term and the accumulated witness. Pedersen binding relies on discrete log, which Shor's algorithm breaks. A quantum attacker who can open commitments to different values after seeing the folding challenge destroys soundness entirely, since they can fold unsatisfied instances and make them appear satisfied. The inner folding loop is therefore not post-quantum safe. The same vulnerability appears in composition: the STARK→Groth16 wrapper that production zkVMs use (the hybrid row in the compatibility table below) reintroduces pairing assumptions at the final step, making the on-chain proof quantum-vulnerable even though the inner STARK is hash-based. Lattice-based folding schemes (LatticeFold, Neo, SuperNeo) replace Pedersen with Module-SIS commitments to address this, though none are in production as of this writing. Hash-based full recursive verification (STARK proving a STARK verifier) avoids the problem entirely but at higher per-step cost.

None of this means folding is insecure against classical adversaries. It means the security argument is more delicate than "run a SNARK at each step." The efficiency gains are real, but so is the need for careful implementation, thorough auditing, and awareness of the quantum horizon.

Folding and the PIOP paradigms

Folding schemes operate at a different level of the proof-system stack than PIOPs or PCS:

  1. Constraint system: R1CS, Plonkish, CCS, AIR
  2. PIOP paradigm: How you prove constraints (quotienting or sum-check)
  3. Recursion strategy: How you chain proofs (full verification, folding, accumulation)

Nova's folding operates at level 3. It takes R1CS instances and folds them algebraically, without committing to a specific PIOP for the final proof. Folding originated in the sum-check lineage (Nova came from the Spartan team, and relaxed R1CS fits naturally with multilinear machinery), but it is no longer confined to it. The "Beyond Nova" section below develops how folding has generalized to CCS, Plonkish, and pairing-based systems, and a compatibility table after that section maps the full landscape of which components pair naturally across all three levels.

Folding versus traditional recursion

AspectTraditional RecursionFolding (Nova)
Per-step overheadFull SNARK verificationTwo group operations
Curves neededPairing-friendly or cycleAny curve works
Final proofProves last recursive stepProves folded instance
Prover bottleneckVerification overheadActual computation

For small (hash function evaluations, state machine transitions), folding is an order of magnitude faster than traditional recursion. The per-step cost drops from thousands of gates to tens of operations.

Beyond Nova: HyperNova and ProtoStar

Nova achieves cheap IVC for R1CS. R1CS is fully general (any NP computation can be expressed, as Spartan demonstrated in Chapter 19), but its degree-2 restriction means that higher-degree operations must be decomposed into auxiliary variables. Encoding Poseidon's S-box, for example, requires intermediate squaring steps that inflate the constraint count. Custom gates (which handle common operations in fewer constraints) and higher-degree constraints (which pack more logic per gate) can reduce this overhead substantially. Lookups and structured access patterns are a separate concern: R1CS-based systems handle these efficiently through memory checking (Chapter 21's Lasso), not through the constraint system itself. The motivation for CCS is primarily constraint degree and gate flexibility, not lookup support. The question is whether folding can extend to these richer constraint representations without losing Nova's per-step efficiency.

The CCS Abstraction

The answer starts with a common language for constraints. Customizable Constraint Systems (CCS), introduced in Chapter 8, unify R1CS, Plonkish, and AIR under one framework. As a reminder, the core equation is:

Each term takes a Hadamard product () over the matrix-vector products for matrices in multiset , then scales by coefficient . The multiset sizes determine constraint degree: R1CS uses (degree 2), higher-degree gates use larger multisets, linear constraints use .

CCS matters for folding because it gives the scheme designer one interface to target. HyperNova folds CCS instances directly, so any constraint system expressible as CCS, which includes R1CS, Plonkish, and AIR, inherits folding automatically. You can fold circuits written in different constraint languages without converting to a common format first. The abstraction pays for itself when you want custom gates, higher-degree constraints, or mixed constraint types within a single IVC computation.

HyperNova: Folding CCS

HyperNova extends Nova's folding approach to CCS, but the generalization isn't straightforward. The degree problem that Nova sidestepped returns with a vengeance.

The degree problem. Recall Nova's cross-term: when folding into a degree-2 constraint, the expansion produces terms at , , and . The error vector absorbs the cross-term at .

For a degree- constraint, folding produces terms at powers . Each intermediate power generates a cross-term that must be absorbed. Naive relaxation requires error vectors, each requiring a commitment. The prover cost scales with degree.

HyperNova avoids this by observing that if one of the two instances is already linear (degree 1), then the cross-terms don't explode. Folding a linear instance with a degree- instance produces at most degree , with manageable cross-terms.

LCCCS (Linearized CCS). HyperNova converts an accumulated CCS instance into a different form before folding. Recall from above that a CCS constraint is a vector equation: all entries of must equal zero. The "linearized" version collapses this to a scalar equation by taking a random linear combination of all constraints. Given random , weight each constraint by (the multilinear extension of the equality predicate from Chapter 4):

By Schwartz-Zippel, if any entry of the original vector is non-zero, this scalar equation fails with high probability over random . This is the standard "batch to a single equation" trick.

The resulting scalar can be expressed in terms of multilinear extension evaluations: is the MLE of evaluated at . The witness now appears only through these evaluation claims, which sum-check can reduce to polynomial openings.

Why call this "linearized"? The term refers to how the folding works, not the constraint degree. When folding an LCCCS (which is a scalar evaluation claim) with a fresh CCS instance (a vector constraint), the interaction between them produces manageable cross-terms. The scalar form of LCCCS means folding doesn't multiply the number of error terms the way naive CCS folding would.

HyperNova therefore folds asymmetrically: the two instances being combined have different shapes, and this mismatch is what prevents the cross-term explosion that would occur if both were high-degree.

To see the contrast, recall that Nova folds two things of the same shape: relaxed R1CS instance + relaxed R1CS instance → relaxed R1CS instance. Both are degree-2, and the error vector absorbs the single degree-1 cross-term.

HyperNova folds two things of different shapes:

  • Running instance (LCCCS): A scalar claim about polynomial evaluations
  • Fresh instance (CCS): A vector constraint over entries

You're not combining "vector + vector → vector." You're combining "scalar + vector → scalar." This asymmetry is what prevents cross-term explosion.

Sum-check is what bridges the two shapes. It takes a claim about a sum (the CCS vector constraint, batched into a scalar) and reduces it to an evaluation claim at a random point. After sum-check, both the running LCCCS and the fresh CCS have been reduced to evaluation claims at the same random point. These scalar claims can be linearly combined without degree blowup.

The loop:

  1. Running LCCCS: A scalar claim ""
  2. Fresh CCS arrives: A vector constraint that must hold at all positions
  3. Sum-check: Batch the CCS into a scalar claim at a new random point , then combine with the LCCCS
  4. Result: A new scalar claim at , another LCCCS ready for the next fold

The sum-check rounds are the cost of generality: rounds of interaction (or Fiat-Shamir hashing). But once sum-check finishes, combining the evaluation claims needs only one multi-scalar multiplication, the same per-fold cost as Nova regardless of constraint degree.

In Nova, the error vector absorbs degree-2 cross-terms algebraically. In HyperNova, sum-check absorbs arbitrary-degree cross-terms interactively. Different mechanisms, same goal: constant prover cost per fold.

Additional benefits:

  • Multi-instance folding: Fold instances simultaneously by running sum-check over all at once. The cost is additional sum-check rounds. This enables efficient PCD (proof-carrying data), where proofs from multiple sources combine into one.
  • Zero-knowledge requires additional work: Sum-check round polynomials leak witness information (their coefficients are linear combinations of witness entries). Neither Nova nor HyperNova is zero-knowledge out of the box. The BlindFold technique (discussed later in this chapter) addresses this by committing to round polynomial coefficients and deferring their verification via folding.

ProtoStar: Accumulation for Special-Sound Protocols

ProtoStar takes a different generalization path. Rather than targeting a specific constraint system (as Nova targets R1CS and HyperNova targets CCS), it provides accumulation for any special-sound interactive protocol, regardless of how many messages the prover and verifier exchange. Sigma protocols (Chapter 16) are the simplest case: 3 messages, special soundness from two transcripts. But many proof components (sum-check rounds, polynomial evaluation arguments, lookup arguments) are also special-sound protocols with more rounds. ProtoStar accumulates them all under one framework.

Why special-soundness enables accumulation. A special-sound protocol has a key property: the verifier's check is a low-degree polynomial equation , where is the public input, is the prover's messages, and is the verifier's challenge. The degree of in is typically small (often 1 or 2).

This algebraic structure is exactly what folding exploits. Given two protocol instances with the same structure, you can take a random linear combination:

If both and , then for any . If either is non-zero, with probability at most over random . The accumulated check is equivalent to both original checks, with negligible soundness loss.

The cost difference. ProtoStar's per-fold cost is roughly 3 scalar multiplications, compared to 1 MSM for Nova/HyperNova (the comparison table below summarizes this). This reflects different trade-offs:

  • Nova/HyperNova commit to the cross-term (or run sum-check), requiring one multi-scalar multiplication per fold
  • ProtoStar works directly with the protocol's algebraic structure, avoiding new commitments but requiring the prover to compute and send "error polynomials" that capture the cross-terms

For degree-2 checks (like most -protocols), this means a few scalar multiplications instead of an MSM. The MSM dominates for large witnesses, so ProtoStar can be faster when the step function is small.

Lookup support. ProtoStar handles lookup arguments with overhead in the lookup table size, compared to for HyperNova. The difference: HyperNova encodes lookups via sum-check over the table, adding rounds. ProtoStar accumulates the lookup protocol directly, paying only for the protocol's native degree. For applications with large tables (memory, range checks), this matters.

ProtoGalaxy. A refinement of ProtoStar that reduces the recursive verifier's work further. The key observation: when folding instances, naive accumulation requires verifier work. ProtoGalaxy uses a Lagrange-basis trick to compress this to field operations plus a constant number of hash evaluations. For multi-instance aggregation (combining proofs from many sources), ProtoGalaxy approaches the minimal possible overhead.

Comparison

FeatureNovaHyperNovaProtoStar
Constraint systemR1CS onlyAny CCS (R1CS, Plonk, AIR)Any special-sound protocol
Constraint degree2ArbitraryArbitrary
Per-step prover cost1 MSM1 MSM3 scalar muls
Lookup supportVia R1CS encoding
Zero-knowledgeRequires blindingFree from foldingRequires blinding
Multi-instanceSequential onlyNative supportNative support

When to Use What: A Practitioner's Guide

The progression from Nova to HyperNova to ProtoStar isn't a simple linear improvement. Each occupies a different point in the design space, and the "best" choice depends on your bottleneck.

The deciding factor is where the prover spends its time. Decompose total proving cost into two parts:

  • Step cost : Proving one iteration of your function (one hash, one VM instruction, one state transition)
  • Accumulation overhead : The cost of folding/recursing that step into the running proof

For traditional IVC (recursive SNARKs), is the verifier circuit size, typically thousands to tens of thousands of constraints. For folding, drops to a handful of group operations. The ratio determines whether folding helps.

Folding wins when is small:

  • VDFs (repeated squaring): a few hundred constraints per square
  • Simple state machines: hundreds to low thousands
  • Hash chain proofs: constraint count of one hash invocation

In these cases, traditional IVC spends most of its time proving the verifier, not the computation. Folding eliminates this overhead almost entirely.

Folding's advantage shrinks when is large:

  • zkVM instruction execution: to constraints per instruction
  • Complex smart contract proofs: dominates regardless
  • Batch proofs of many operations: amortization across the batch matters more than per-step overhead

When , the prover spends 95%+ of time on the step function whether using folding or traditional IVC. Folding's 100× reduction in becomes a 5% improvement in total cost.

Engineering maturity matters beyond raw performance:

  • Folding schemes are newer. Less battle-tested, fewer audits, more subtle security pitfalls.
  • AIR/STARK tooling is mature. Well-understood compilation, debugging, and optimization paths.
  • Folding debugging is harder. Errors compound across folds; traditional recursion catches bugs per-step.

Some production teams (Nexus, for example) explored folding and reverted to AIR-based approaches. Not because folding is inferior in theory, but because for their specific (complex zkVM execution), the engineering complexity didn't pay off.

The following table summarizes the decision:

ScenarioRecommended Approach
Small step function (< 1000 constraints), millions of stepsFolding (Nova/HyperNova)
Large step function (> 10000 constraints), complex logicTraditional IVC or STARK
Need multi-instance aggregationHyperNova or ProtoStar
Custom gates, non-R1CS constraintsHyperNova (CCS) or ProtoStar
Maximum simplicity, proven toolingSTARK/AIR
Smallest possible final proofFold, then wrap in Groth16

CycleFold: Efficient Curve Switching

All folding schemes face the curve-cycle problem from earlier in this chapter: the folding verifier performs group operations, which are expensive to prove in-circuit over a different field. But folding has a unique advantage here that traditional recursion doesn't: the "verifier work" per step is tiny (a few scalar multiplications), not a full SNARK verification. CycleFold exploits this.

In Nova's IVC loop, the prover updates the running commitment:

This is a scalar multiplication on curve . If our main circuit is over the scalar field of , we can't compute this operation natively. The curve points have coordinates in (the base field), and arithmetic inside an circuit is expensive. This is the same field mismatch from the curve cycle section above, but now at the folding level rather than the full-verification level. CycleFold's solution relies on having a curve cycle (Pasta, BN254/Grumpkin) where a second curve has scalar field , making the expensive operation native on .

Traditional recursion would embed the entire verifier (including pairings) in the circuit, paying hundreds of thousands of constraints. But Nova's "verifier" is just this one scalar multiplication. Can we handle it more cheaply?

The CycleFold idea. Instead of proving the scalar multiplication in the main circuit, defer it to a separate accumulator on the cycle curve .

Recall the cycle: has scalar field and base field ; has scalar field and base field . The scalar multiplication on involves arithmetic (the curve operations). But circuits are natively over . So:

  1. Main circuit (on ): Proves " was computed correctly" and that the folding challenges were derived correctly. It takes the result of the commitment update as a public input but does not check the scalar multiplication that produced it. The main circuit trusts this value for now.

  2. Auxiliary circuit (on ): A tiny circuit that checks one claim: "given , , and , the output is correct." Because 's scalar field is , the curve arithmetic on points is native here. This circuit is roughly 10,000 constraints, compared to 100,000+ for emulating the same operation non-natively in the main circuit.

  3. Two parallel folding loops. The main accumulator on folds each step's computation claim, just as in standard Nova. The auxiliary accumulator on folds each step's commitment-update claim. Both accumulators grow in parallel: one step of the IVC loop produces one fold on each curve.

  4. Two final SNARKs. At the end, the prover produces one SNARK on (proving the accumulated computation is correct) and one SNARK on (proving all commitment updates were correct). The verifier checks both. Each SNARK operates over its native field, so neither requires emulation.

The two accumulators are coupled through shared public inputs: the commitment values that the main circuit assumes correct and the auxiliary circuit verifies. Soundness holds because a cheating prover who fakes a commitment update on the main side will fail the auxiliary accumulator's check at the end.

After steps:

  • Main accumulator: one final SNARK on proves " was applied correctly times"
  • Auxiliary accumulator: one final SNARK on proves "all commitment updates were computed correctly"

Both SNARKs are over their native fields. No cross-field emulation anywhere.

The cost breakdown:

  • Per step: ~10,000 constraints on the cycle curve (the scalar multiplication circuit)
  • Final proof: two SNARKs, one on each curve
  • Total overhead: roughly constraints across all steps, versus without CycleFold

For long computations, this is a 10× reduction in the curve-cycle overhead.

CycleFold only works for folding, not traditional recursion. Traditional recursion embeds the entire verifier in circuit at each step, including pairings, hash evaluations, and complex checks that are all entangled with the soundness argument. You cannot split these across two curves because the verifier's logic is monolithic. Folding's per-step verifier, by contrast, is just a few scalar multiplications on commitments, cleanly separable from the computation proof. This modularity is what lets CycleFold put the two concerns on different curves.

CycleFold applies to Nova, HyperNova, and ProtoStar, making all of them practical over curve cycles like Pasta (Pallas/Vesta) or BN254/Grumpkin.

The full compatibility landscape

With Nova, HyperNova, ProtoStar/ProtoGalaxy, and Mira all developed, we can now map the full landscape of which proof-system components pair naturally. Chapter 22 observed that "one choice determines the rest": picking univariate polynomials implies roots of unity, FFT, and KZG or FRI; picking multilinear polynomials implies the hypercube, the halving trick, and IPA or Dory. The same principle extends to the recursion layer, though the boundaries have softened as folding generalizes:

LineageConstraintsPIOP mechanismPCSRecursion
Sum-check nativeR1CSSum-check (multilinear)IPA, Dory, HyraxNova (relaxed R1CS folding)
Sum-check + CCSCCS (captures R1CS, Plonkish, AIR)Sum-check (multilinear)IPA, Dory, HyraxHyperNova (CCS folding via sum-check)
Quotienting nativePlonkishQuotienting (univariate)KZGMira (pairing-based folding); or curve cycles
STARKAIRQuotienting (univariate)FRIFull verification (recursion via hash-based PCS)
HybridMixedSTARK inner, Groth16 outerFRI inner, KZG outerComposition (STARK→Groth16 wrap)

ProtoStar and ProtoGalaxy do not appear in a single row because they are protocol-agnostic: they accumulate any special-sound interactive protocol, whether it comes from the sum-check or quotienting tradition. A ProtoStar-based system can accumulate sum-check rounds (landing in the sum-check rows) or Plonkish quotient checks (landing in the quotienting row). They sit orthogonally to the lineage distinction, operating at the level of the interactive protocol's algebraic structure rather than the constraint system or PCS.

HyperNova also crosses boundaries: it uses sum-check as its PIOP mechanism but folds CCS instances, which can express Plonkish and AIR constraints. The constraint system and the PIOP mechanism are not always from the same lineage.

Within each pure lineage (sum-check native, quotienting native), each component exists because the previous one requires it. In the sum-check lineage, Nova's folding produces a linear combination of witness vectors (). The folded witness is a multilinear polynomial's evaluation table, because R1CS witnesses are evaluation tables over the Boolean hypercube (Chapter 19). Sum-check is the natural PIOP for multilinear polynomials because it reduces claims about exponentially many hypercube evaluations to a single random evaluation (Chapter 3). The PCS must then open a multilinear polynomial at a random point in , which is exactly what IPA and Dory are built to do (Chapter 22). Each layer's choice is forced by the previous layer's output format.

In the quotienting lineage, the chain starts differently. Plonkish constraints are verified via quotient polynomials over roots of unity, which FFT computes. KZG commits to the same univariate polynomials the FFT produces, reusing the evaluation domain. Mira folds pairing-based arguments directly, staying within the univariate/KZG world. For STARK recursion, FRI replaces KZG; the verifier is hash-based and the recursion proceeds through full in-circuit verification rather than folding.

The hybrid row exists for a different reason: complementary strengths across stacks. STARKs (quotienting + FRI) give fast transparent proving; Groth16 (quotienting + KZG) gives tiny constant-size proofs. Composition bridges them by proving "the STARK verifier accepted" with Groth16, using hash-based transparency for the bulk and pairing-based compactness for on-chain delivery.

The heuristic for choosing a recursion strategy follows from which constraints matter most. For pure sum-check IVC with R1CS (simplest folding, smallest per-step overhead), Nova suffices. For richer constraint languages (custom gates, higher degree) with sum-check-based folding, HyperNova over CCS. For existing Plonkish/KZG infrastructure, Mira for pairing-based folding or ProtoGalaxy for general accumulation. For the smallest final proofs on-chain, fold with whichever machinery suits your stack and compose with Groth16 at the very end.

BlindFold: Folding for Zero-Knowledge

Most deployed "zkVMs" were not, for years, truly zero-knowledge. They were succinct (short proofs, fast verification) but the proofs leaked information about the prover's private inputs. The reason: the sum-check protocol at their core is not zero-knowledge. Each round polynomial is a deterministic function of the witness, and its coefficients are linear combinations of the witness entries. After enough rounds, a verifier accumulates enough constraints to recover the witness entirely. Chapter 18 showed this leakage concretely.

BlindFold (Section 7 of HyperNova, Kothapalli, Setty, Tzialla, 2023) resolves this in three moves:

Move 1: Hide the round polynomials. For a degree- round polynomial , the prover sends Pedersen commitments instead of the field elements . The verifier sees only opaque group elements. This immediately hides the witness, but the verifier can no longer check anything (the consistency check requires knowing the actual coefficients).

Move 2: Encode the verifier as an R1CS. Every check the sum-check verifier would have performed becomes a constraint. The consistency check (verifying equals the previous claim) is one linear constraint. The evaluation check (verifying at the Fiat-Shamir challenge) is another, with the powers baked into the R1CS matrices as constants since both parties derive them from the public transcript. For a sum-check with rounds, this R1CS has roughly constraints, a tiny system.

Move 3: Fold with a random witness, then prove. Proving this R1CS with Spartan directly would reintroduce the problem (Spartan's own sum-check messages would leak the witness). BlindFold breaks the circularity by folding the real witness with a uniformly random witness before running Spartan. The folded witness is uniformly distributed for any , because the map is an affine bijection on . This is the algebraic one-time pad: just as is uniform for uniform key regardless of message , the folded witness is uniform regardless of the real witness. Spartan can now prove the folded R1CS in the clear without being zero-knowledge itself, because the data it operates on reveals nothing about .

The folding uses the same Nova protocol from earlier in this chapter (cross-term computation, commitment, Fiat-Shamir challenge, linear combination of witnesses and error vectors). The only difference from IVC folding is that the second instance is random rather than derived from a computation step. The final SNARK proves the folded instance is satisfiable; the verifier folds commitments homomorphically (Pedersen's additive structure) and checks the Spartan proof without ever seeing any witness value.

The cost is remarkably low. The blinded Phase 1 proof (Pedersen commitments replacing field elements) is often shorter than the original non-ZK proof. The Phase 2 BlindFold proof (Spartan over constraints) adds roughly 3 KB. In multi-stage protocols, BlindFold encodes all stages' verifier checks into a single R1CS and folds once; the marginal cost of adding another sum-check stage is two more constraints. Earlier approaches (masking polynomials per Libra, -protocol wrappers per Hyrax) pay their overhead per stage and scale with the number of sum-check invocations.

Choosing a strategy

The chapter has covered three recursion-related techniques, each at a different level of the proof system stack. The decision tree is:

  1. Composition is for combining two different proof systems. Use it when you need a small final proof from a fast-but-large-proof inner system (the STARK→Groth16 pattern), or when wrapping a non-ZK system in a ZK outer layer. It is a one-time operation, not an iterative one.

  2. IVC (folding or traditional recursion) is for unbounded sequential computation. Nova handles R1CS; HyperNova generalizes to CCS (capturing Plonkish and AIR); ProtoStar/ProtoGalaxy accumulate any special-sound protocol. The choice between them depends on the step function size relative to the verifier overhead and on which constraint system your application uses; the "When to Use What" guide in the Beyond Nova section develops this in detail. The compatibility table above maps which folding scheme pairs with which PCS and PIOP.

  3. Direct proving (no recursion) is appropriate when the computation fits in a single circuit. Every recursive system has a minimum useful circuit size: if , recursion adds overhead without benefit. Traditional recursion has a high threshold (- constraints for in-circuit verification). Folding lowers it dramatically (under 100 group operations per step), which is why folding made recursion practical for small step functions where it was previously absurd.

Key Takeaways

  1. Composition combines complementary proof systems. The outer prover handles only the inner verifier circuit, which is much smaller than the original computation. This is why Groth16 wrapping doesn't reintroduce the slow prover: Groth16 proves a circuit of size , not .

  2. Recursion compresses through self-reference. Each recursive layer proves a smaller circuit (the previous layer's verifier). After layers, verification cost reaches a constant. IVC extends this to unbounded sequential computation, where each step's proof attests to the entire history.

  3. Field mismatch is the main obstacle to recursion. Pairing-based verifiers do arithmetic, but circuits constrain in . Emulation blows up circuit size by 100×. Curve cycles (Pasta, BN254/Grumpkin, BLS12-377/BW6-761) solve this by alternating between matched curve pairs where each step's verifier arithmetic is native.

  4. Deep recursion weakens security proofs but not security. Extraction requires rewinds for depth , degrading provable security by bits per layer. No known attack exploits the depth; the gap is between what we can prove and what we believe.

  5. Folding replaces per-step verification with accumulation. Two relaxed R1CS claims fold into one via random linear combination, with the error vector absorbing the cross-terms. Only the final accumulated claim requires a SNARK. Per-step cost drops from thousands of constraints to a handful of group operations, making IVC practical for small step functions where traditional recursion's overhead dominated.

  6. Folding has generalized beyond R1CS. HyperNova folds CCS (which captures R1CS, Plonkish, and AIR) via asymmetric folding with sum-check. ProtoStar and ProtoGalaxy accumulate any special-sound protocol, sitting orthogonally to the sum-check/quotienting divide. Mira folds pairing-based arguments directly.

  7. The proof-system stack has natural compatibility lanes. Choosing a constraint system, PIOP, PCS, and recursion strategy is not four independent decisions. Sum-check folding pairs with multilinear polynomials and IPA; quotienting pairs with univariate polynomials and KZG/FRI. HyperNova crosses the boundary by accepting quotienting-style constraints (via CCS) while using sum-check internally.

  8. Pedersen-based folding is not post-quantum safe. Nova, HyperNova, and ProtoStar all use Pedersen commitments (discrete-log binding). The STARK→Groth16 wrapper reintroduces pairing assumptions at the final step. Lattice-based folding (LatticeFold, Neo, SuperNeo) and hash-based full STARK recursion are the emerging PQ-safe alternatives.

  9. BlindFold adds zero-knowledge via the algebraic one-time pad. Commit to sum-check round polynomials, encode the verifier as a tiny R1CS, fold the real witness with a random one ( is uniform for uniform ), and prove the folded instance with Spartan. Spartan need not be ZK because the data it sees is already masked. Cost: KB.

  10. The decision tree has three levels. Composition for combining complementary systems (one-time wrapping). Folding or traditional recursion for unbounded sequential computation (the ratio determines which). Direct proving when the computation fits in a single circuit. Every recursive system has a minimum useful circuit size; folding lowered this threshold by orders of magnitude.