Zero-Knowledge WHIR II: Code-Switching in the Dark

May 30, 2026

Where Part 1 left us

Part 1 built the foundations of an HVZK IOPP for constrained interleaved codes:

  • Zero-knowledge encodings. Randomized encodings EncC ⁣:F×FrΣm\mathsf{Enc}_{\mathcal C} \colon \mathbb F^\ell \times \mathbb F^r \to \Sigma^m where any t\leq t coordinates of the codeword can be simulated from positions alone.
  • The committed sumcheck relation RC,Czk,sl\mathcal R_{\mathcal C, \mathcal C_{\mathsf{zk}}, \mathsf{sl}}. Succinct linear forms to kill the verifier’s Ω()\Omega(\ell) cost, plus small mask oracles folded into a joint constraint to hide the target μ\mu.
  • Composable HVZK for IORs. The standard HVZK definition breaks under composition (Part 1’s IOR1IOR2\mathsf{IOR}_1 \circ \mathsf{IOR}_2 counterexample). Fix: a distinguisher class D\mathcal D that the simulator must additionally handle, and a composition theorem that chains errors ζ1+ζ2\zeta_1 + \zeta_2 across the seam.
  • HVZK sumcheck reduction. Reduces a 2k2^k-interleaved code to its base code via small per-round masks (not a full 2×2\times mask), with mask oracles carried forward through the joint constraint. Drops problem size by a factor of 2k2^k at sublinear masking cost.

We can now test the interleaving of any zero-knowledge code in zero knowledge — by reducing to the base code. But the base code is still big. To get a polylog verifier we need to recursively shrink the code, and the move that does that is code-switching: an IOR from a test for code C\mathcal C to a test for some smaller code C\mathcal C'.

The non-zero-knowledge version is well-trodden ([RR20]; [BCFW25]; WHIR’s recursion shape). The zero-knowledge version breaks in two distinct places. This part fixes both, assembles the HVZK WHIR/Ligerito corollary, and tackles the R1CS reduction.

Code-switching without zero-knowledge

Code-switching follows a clean template. The verifier has an oracle ff that — honestly — equals C(m)\mathcal C(\boldsymbol m) for some message m\boldsymbol m satisfying a predicate Φ\Phi. The prover wants to convince the verifier of this fact using a smaller proximity test.

  1. Prover sends gg. A new oracle g(Σ)mg \in (\Sigma')^{m'}. In the honest case, g=C(m)g = \mathcal C'(\boldsymbol m) — the same message encoded under the new, smaller code C\mathcal C'.
  2. Verifier spot-queries ff. Sample x1,,xt[m]x_1, \dots, x_t \in [m], query f(x1),,f(xt)f(x_1), \dots, f(x_t).
  3. Run a proximity protocol for the new claim ”gg encodes some m\boldsymbol m such that Φ(m)=1\Phi(\boldsymbol m) = 1 and C(m)[xi]=f(xi)\mathcal C(\boldsymbol m)[x_i] = f(x_i) for every ii.”

The added clauses C(m)[xi]=f(xi)\mathcal C(\boldsymbol m)[x_i] = f(x_i) are the consistency check: they tie the new oracle’s message to the old oracle’s message. Without them, the prover could swap m\boldsymbol m at the switch.

This template is everywhere — [RR20], [RR22], [BCFW25], the [BMMS25] family of FICS/FACS protocols, WHIR’s recursion. It has not been applied to zero-knowledge codes outside [RW24], whose construction leans on specific properties of zero-knowledge tensor codes. We need a code-agnostic ZK version.

Why ZK encodings break the template

The first break: the consistency check assumes the codeword is a function of the message alone. With a ZK encoding it isn’t.

Concretely, f=EncC(m,r)f = \mathsf{Enc}_{\mathcal C}(\boldsymbol m, \boldsymbol r) depends on both the message and the randomness. The consistency check C(m)[xi]=f(xi)\mathcal C(\boldsymbol m)[x_i] = f(x_i) doesn’t even type-check anymore — you need r\boldsymbol r to predict ff.

The obvious fix: have gg encode both the message and the randomness under C\mathcal C'. That works, but it requires padding gg‘s message space, which means gg is twice the size, every subsequent sumcheck operates on a 2×2\times-blown object, and we’re back to 2×\geq 2\times overhead. Dead.

The actual fix reuses the committed sumcheck relation. Introduce an additional mask s=EncCzk(r,r)s = \mathsf{Enc}_{\mathcal C_{\mathsf{zk}}}(\boldsymbol r, \boldsymbol r'') encoding the randomness r\boldsymbol r under the ZK mask code Czk\mathcal C_{\mathsf{zk}}. Now write the per-symbol claim by splitting EncC\mathsf{Enc}_{\mathcal C}‘s generator matrix into a message part and a randomness part: GC=[GCmsgGCrnd]G_{\mathcal C} = [G_{\mathcal C}^{\mathrm{msg}} \mid G_{\mathcal C}^{\mathrm{rnd}}] where GCmsgG_{\mathcal C}^{\mathrm{msg}} acts on the message and GCrndG_{\mathcal C}^{\mathrm{rnd}} acts on the randomness. Then

f(x)  =  EncC(m,r)[x]  =  m,GCmsg[x]+r,GCrnd[x],f(x) \;=\; \mathsf{Enc}_{\mathcal C}(\boldsymbol m, \boldsymbol r)[x] \;=\; \langle \boldsymbol m,\, G_{\mathcal C}^{\mathrm{msg}}[x] \rangle + \langle \boldsymbol r,\, G_{\mathcal C}^{\mathrm{rnd}}[x] \rangle,

which is a linear constraint on (m,r)(\boldsymbol m, \boldsymbol r). The first piece accumulates into the existing main-oracle constraint via the succinct linear form sl\mathsf{sl}'; the second piece accumulates as a constraint on the mask ss. Both go into the joint constraint of the next committed sumcheck relation. No padding, no 2×2\times.

That handles the randomness break. But the bigger break is structural: the WHIR-style recursion uses out-of-domain sampling in the list-decoding regime, and OOD’s whole job is to leak information.

OOD sampling and the second tension

Quick recap (see the WHIR post for the proper treatment). In the list-decoding regime — where the verifier picks a proximity parameter δ\delta larger than the unique-decoding radius — multiple codewords in Λ(C,f,δ)\Lambda(\mathcal C, f, \delta) can be close to a malicious oracle ff. The verifier needs a way to force the prover to commit to one codeword, not a list.

WHIR’s version of the fix: the codewords are evaluations of low-degree polynomials, so the verifier sends a random point zz outside the evaluation domain and the prover answers with a claimed value y^\hat y. Two distinct low-degree polynomials agree at a random point with probability at most d/Fd/|\mathbb F|, so with high probability at most one polynomial in the list is consistent with (z,y^)(z, \hat y) — by answering, the prover has picked one. That’s an out-of-domain (OOD) sample.

The code-agnostic version abstracts the only property this used: evaluation at zz is a linear function of the message — the inner product of the coefficient vector with (1,z,z2,)(1, z, z^2, \dots). So replace “evaluate at a random point” with “answer a random linear probe.” The verifier samples randomness ρDze\rho \leftarrow D_{\mathsf{ze}}, which determines a public matrix ze(ρ)Ftood×\mathsf{ze}(\rho) \in \mathbb F^{t_{\mathsf{ood}} \times \ell} (ze\mathsf{ze} for zero-evader — defined next section), and the prover answers

y  :=  ze(ρ)m    Ftood,\boldsymbol y \;:=\; \mathsf{ze}(\rho) \cdot \boldsymbol m \;\in\; \mathbb F^{t_{\mathsf{ood}}},

toodt_{\mathsf{ood}} random linear combinations of the message coordinates. The verifier’s later checks enforce that the message it ends up trusting answers the probe with exactly y\boldsymbol y. This pins one codeword for the same reason the random point did: two distinct list codewords have distinct messages, their difference is a fixed nonzero vector, and a random probe sends a fixed nonzero vector to 0\boldsymbol 0 only with small probability — so no two list elements share an answer, and y\boldsymbol y selects at most one.

Now look at what the prover just sent: toodt_{\mathsf{ood}} linear combinations of m\boldsymbol m with public coefficients, in the clear. y\boldsymbol y leaks information about m\boldsymbol m, hence about the witness.

And the leak is inherent. OOD is designed to uniquely identify the codeword (i.e., the witness). Zero-knowledge forbids revealing the witness. The two cannot literally coexist — you have to weaken one.

The paper weakens neither. It restructures OOD so the leak hits randomness instead of the witness.

Zero-evaders and private zero-evaders

The revised OOD sample is

y  :=  ze(ρ)[mr],\boldsymbol y \;:=\; \mathsf{ze}(\rho) \cdot \begin{bmatrix} \boldsymbol m \\ \boldsymbol r \end{bmatrix},

with the difference being that the probe now also covers freshly sampled randomness rFr\boldsymbol r \leftarrow \mathbb F^r — not the encoding coins, new randomness whose only job is privacy. The function ze\mathsf{ze} is asked to satisfy two conditions:

  1. It’s still a good OOD sample. Equivalently, ze\mathsf{ze} is a zero-evader [BCFW25]: for every nonzero vector v\boldsymbol v, PrρDze[ze(ρ)v=0]εzero.\Pr_{\rho \leftarrow D_{\mathsf{ze}}}\big[\mathsf{ze}(\rho) \cdot \boldsymbol v = \boldsymbol 0\big] \leq \varepsilon_{\mathsf{zero}}. I.e. the matrix ze(ρ)\mathsf{ze}(\rho) is, in expectation, full-rank enough to detect any nonzero input difference. This is what enables list-decoding-regime soundness.
  2. It leaks no information about m\boldsymbol m. Formalized: ze\mathsf{ze} is an (r,ζze)(r, \zeta_{\mathsf{ze}})-private zero-evader if (ρ,y)(\rho, \boldsymbol y) can be efficiently simulated, with statistical error at most ζze\zeta_{\mathsf{ze}}, without knowing m\boldsymbol m.

Condition (2) sounds abstract; the construction that achieves it is not. Take any zero-evader and append rr columns sampled as a random rank-toodt_{\mathsf{ood}} matrix MFtood×rM \in \mathbb F^{t_{\mathsf{ood}} \times r}:

ze(ρ,M):=[ze(ρ)M],y=ze(ρ)m+Mr.\mathsf{ze}'(\rho, M) := \big[\, \mathsf{ze}(\rho) \mid M \,\big], \qquad \boldsymbol y = \mathsf{ze}(\rho) \cdot \boldsymbol m + M \boldsymbol r.

This is the surjectivity argument from Part 1’s ZK encodings again: MM‘s columns span Ftood\mathbb F^{t_{\mathsf{ood}}}, so with r\boldsymbol r uniform, MrM\boldsymbol r is uniform — a one-time pad on top of ze(ρ)m\mathsf{ze}(\rho) \cdot \boldsymbol m. Then y\boldsymbol y is uniform even conditioned on (ρ,M)(\rho, M), and the simulator just outputs a uniform y\boldsymbol y. Zero-evasion survives: a nonzero (v,w)(\boldsymbol v, \boldsymbol w) with w0\boldsymbol w \neq \boldsymbol 0 is caught by the random MM except with probability 1Ftood1\tfrac{1}{|\mathbb F|^{t_{\mathsf{ood}}} - 1}; with w=0\boldsymbol w = \boldsymbol 0 it reduces to the original ze\mathsf{ze}.

Lemma (informal, Lemma 2.13; formally Lemma 9.3). Any zero-evader with error εzero\varepsilon_{\mathsf{zero}} extends, for any rtoodr \geq t_{\mathsf{ood}}, to an (r,0)(r, 0)-private zero-evader with error εzero\approx \varepsilon_{\mathsf{zero}}. Perfect privacy costs nothing.

Side-by-side comparison: left, non-ZK OOD computes y = ze(ρ) · m and ships y in the clear, leaking a linear function of the witness message; right, private-zero-evader OOD computes y = ze(ρ) · (m, r) by mixing m with freshly sampled randomness r, so y is uniform from the verifier's view while ze still pins a unique message.

What the constraint can — and can’t — pin

Recall what the OOD constraint does with the list: a candidate codeword in Λ(C,f,δ)\Lambda(\mathcal C, f, \delta) survives if its message answers the probe with y\boldsymbol y. In the non-ZK world the encoding is a bijection — one message, one codeword — so “at most one surviving message” and “at most one surviving codeword” are the same statement, and [BCFW25] proves it.

A ZK encoding breaks the bijection. Decoding a candidate fˉΛ(C,f,δ)\bar f \in \Lambda(\mathcal C, f, \delta) now yields a pair (mˉ,rˉ)=EncC1(fˉ)(\bar{\boldsymbol m}, \bar{\boldsymbol r}) = \mathsf{Enc}_{\mathcal C}^{-1}(\bar f) — message part and coins — and the probe is applied to the message part only. So list elements can differ in two ways, and the constraint treats them very differently:

  • Same message, different coins. Both answer the probe identically. Both survive — the constraint cannot separate them. And it must not: privacy requires the honest codeword to be indistinguishable among all encodings of m\boldsymbol m; a constraint that singled out one encoding would itself leak the coins.
  • Different messages. The message difference is a nonzero vector, and a zero-evader kills nonzero vectors: they share an answer with probability at most εzero\varepsilon_{\mathsf{zero}}.

So the constraint partitions the list into classes — one per message — and with high probability at most one class is consistent with y\boldsymbol y. The message is pinned; which encoding of it sits near ff stays ambiguous. That is exactly the split we need: soundness pinned the thing it uses, privacy kept the thing it hides behind.

Why is a pinned message enough, when the non-ZK analysis pinned a codeword? Because “which codeword” was never the question. Everything downstream is a statement about the message: the predicate Φ(m)\Phi(\boldsymbol m), consistency between ff and gg across the switch, the witness the extractor must output. The non-ZK proofs pinned the codeword only because there it was the same thing.

Formally, stated for the joint probe over oracle-and-mask pairs:

Lemma (informal, Lemma 2.14). Take any two candidates (fˉ1,sˉ1),(fˉ2,sˉ2)(\bar f_1, \bar s_1), (\bar f_2, \bar s_2) with fˉiΛ(C,f,δ)\bar f_i \in \Lambda(\mathcal C, f, \delta), sˉiΛ(Czk,s,δzk)\bar s_i \in \Lambda(\mathcal C_{\mathsf{zk}}, s, \delta_{\mathsf{zk}}) that answer the probe identically. Then their decoded message parts are equal, except with probability εzero\varepsilon_{\mathsf{zero}} for that fixed pair — and some bad pair exists anywhere in the lists with probability at most Λ(C,δ)2Λ(Czk,δzk)2εzero|\Lambda(\mathcal C, \delta)|^2 \cdot |\Lambda(\mathcal C_{\mathsf{zk}}, \delta_{\mathsf{zk}})|^2 \cdot \varepsilon_{\mathsf{zero}} over ρ\rho. The coins are unconstrained.

The proof is the union bound you’d guess: fix two candidates whose message parts differ, zero-evasion kills their difference except with probability εzero\varepsilon_{\mathsf{zero}}, union over all pairs of list elements.

Equivalence-class diagram for Lemma 2.14: the product list Λ(C,δ) × Λ(C_zk, δ_zk) of nearby (codeword, mask) pairs is partitioned into colored classes by the underlying message; many distinct (f_i, s_i) pairs may satisfy the OOD constraint, but the lemma forces every satisfying pair into one message class, so soundness still pins the message even though the codeword is no longer unique.

There’s one last knot. The fresh randomness r\boldsymbol r must be included in the code-switch claim too — otherwise the next round won’t know about it. Padding r\boldsymbol r into C\mathcal C' would reintroduce the 2×2\times overhead we were just trying to avoid. The fix: a joint OOD sample of both gg and the mask ss, treating the pair as one object. The committed sumcheck relation, with its accumulating joint constraint, is exactly the right vehicle for this — and the entire reason it was designed the way it was.

The code-switch construction

Re-anchor what the IOR starts from, because everything below refers to it. The input is one committed-sumcheck claim over (C,Czk,sl)(\mathcal C, \mathcal C_{\mathsf{zk}}, \mathsf{sl}) — Part 1’s relation:

f=EncC(m,r),m,sl(st)+iξi,ui=μ.f = \mathsf{Enc}_{\mathcal C}(\boldsymbol m, \boldsymbol r), \qquad \langle \boldsymbol m, \mathsf{sl}(\mathsf{st}) \rangle + \sum_i \langle \boldsymbol \xi_i, \boldsymbol u_i \rangle = \mu.

Concretely the verifier holds: the oracle ff, hiding message m\boldsymbol m behind coins r\boldsymbol r; the mask oracles ξi\xi_i left behind by earlier rounds, hiding messages ξi\boldsymbol \xi_i; and the public data — state st\mathsf{st}, target μ\mu, and the coefficient vectors ui\boldsymbol u_i each mask enters through. The output must be a claim of the same shape, one size smaller: a new oracle under C\mathcal C', one more mask, a new target μ\mu', over (C,Czk,sl)(\mathcal C', \mathcal C_{\mathsf{zk}}, \mathsf{sl}'). That closure is what lets the step recurse.

The four pieces built so far — ZK-encoding-aware consistency, the joint OOD sample, the private zero-evader, and the committed sumcheck relation as the carrier — slot together like this:

One round of recursion, step by step
Prover — knows m,r\boldsymbol m, \boldsymbol r, and will sample fresh randomness r,s,r\boldsymbol r', \boldsymbol s, \boldsymbol r''
Verifier — holds μ,st,(ui)\mu, \mathsf{st}, (\boldsymbol u_i), and reaches f,(ξi)f, (\xi_i) only by query

Step 1  ·  Prover
Re-encode the same message under the smaller code, with fresh coins: g:=EncC(m,r)g := \mathsf{Enc}_{\mathcal C'}(\boldsymbol m, \boldsymbol r'). Then Part 1’s mask-oracle move — a small committed codeword whose message enters the joint constraint — reused for a new job: commit s:=EncCzk((s,r),r)s := \mathsf{Enc}_{\mathcal C_{\mathsf{zk}}}((\boldsymbol s, \boldsymbol r), \boldsymbol r''), whose message is ff‘s old coins r\boldsymbol r padded with fresh randomness s\boldsymbol s. The mask is how r\boldsymbol r reaches the next round without being padded into C\mathcal C' — the padding route was the 2×2\times dead end.
new oracle gg, mask oracle ss

Step 2  ·  Verifier
Draw the OOD randomness. ρ\rho selects the public probe matrix ze(ρ)\mathsf{ze}(\rho) — the random-linear-probe generalization of WHIR’s out-of-domain point, needed because we’re in the list-decoding regime and must force one candidate out of Λ(C,g,δ)\Lambda(\mathcal C', g, \delta).
OOD randomness ρDze\rho \leftarrow D_{\mathsf{ze}}

Step 3  ·  Prover
Answer the probe jointly over everything the next round must pin — the message and both pieces of the mask’s message:

y  :=  ze(ρ)(m,r,s)    Ftood\boldsymbol y \;:=\; \mathsf{ze}(\rho) \cdot (\boldsymbol m, \boldsymbol r, \boldsymbol s)^\top \;\in\; \mathbb F^{t_{\mathsf{ood}}}
ze\mathsf{ze} is a private zero-evader: the fresh s\boldsymbol s inside the probe plays the one-time-pad role, so y\boldsymbol y is uniform and leaks nothing — yet by Lemma 2.14 it still pins the decoded message parts.
joint OOD answer y\boldsymbol y

Step 4  ·  Verifier
Spot-check that gg‘s message really is ff‘s message: sample query positions on the old oracle, plus batching scalars. Random ε\boldsymbol \varepsilon fuses many linear constraints into one — Part 1’s batching move; the fused claim holds only if every piece does, bar 1/F1/|\mathbb F|.
queries x1,,xt[m]x_1, \dots, x_t \leftarrow [m], batching ε\boldsymbol \varepsilon

Step 5  ·  Verifier
Read f(x1),,f(xt)f(x_1), \dots, f(x_t) — the only touches of the old oracle. By the generator split from earlier, each read f(x)=m,GCmsg[x]+r,GCrnd[x]f(x) = \langle \boldsymbol m, G^{\mathrm{msg}}_{\mathcal C}[x] \rangle + \langle \boldsymbol r, G^{\mathrm{rnd}}_{\mathcal C}[x] \rangle is a linear constraint on (m,r)(\boldsymbol m, \boldsymbol r) — exactly the shape the joint constraint accepts. Assemble the output instance:

target μ:=μ+iεiyi+i,lεf(xi)l\mu' := \mu + \sum_i \varepsilon_i \cdot y_i + \sum_{i,l} \varepsilon_{\dots} \cdot f(x_i)_l
oracles gg, the old masks (ξi)(\xi_i), the new mask ss
constraint one joint constraint over (C,Czk,sl)(\mathcal C', \mathcal C_{\mathsf{zk}}, \mathsf{sl}'), packing four linear pieces:
the old main constraint m,sl(st)\langle \boldsymbol m, \mathsf{sl}(\mathsf{st}) \rangle, carried by sl\mathsf{sl}' with state st:=(st,ε,ρ,x1,,xt)\mathsf{st}' := (\mathsf{st}, \boldsymbol \varepsilon, \rho, x_1, \dots, x_t)
the carried mask constraints ξi,ui\langle \boldsymbol \xi_i, \boldsymbol u_i \rangle
the joint OOD constraint iεi(m,r,s),zei(ρ)\sum_i \varepsilon_i \cdot \langle (\boldsymbol m, \boldsymbol r, \boldsymbol s), \mathsf{ze}_i(\rho) \rangle
the consistency reads i,lε(m,r),GC[xi,l]\sum_{i,l} \varepsilon_{\dots} \cdot \langle (\boldsymbol m, \boldsymbol r), G_{\mathcal C}[x_{i,l}] \rangle
Same shape, one size smaller — feed it back to step 1.
Message-flow diagram for the code-switch IOR: prover sends g = Enc_{C'}(m, r') and mask s = Enc_{C_zk}((s, r), r''); verifier sends OOD randomness ρ; prover replies with joint OOD answer y = ze(ρ) · (m, r, s); verifier issues in-domain consistency queries x_1,…,x_t and batching scalars ε, reads f(x_i), and assembles μ' that bundles the original main-oracle constraint, the mask-oracle constraints, the joint OOD constraint, and the consistency constraints into the next committed sumcheck instance over (C', C_zk, sl').

A counterintuitive efficiency property: the prover and verifier time of this code-switch are independent of the starting code C\mathcal C. The dominant cost is the encoding under C\mathcal C' (the new, smaller code) and the verifier’s computation of μ\mu'. The starting code’s structure pushes through into the next relation’s succinct linear form sl\mathsf{sl}' — captured by a quantity TCT_{\mathcal C} the paper calls the code-switching friendliness of C\mathcal C — but doesn’t show up in this protocol’s local cost. People reach for “the slow code makes the switch slow”; that’s not what happens.

From building blocks to the theorems

Compose the HVZK sumcheck reduction (Part 1) with the code-switch IOR above, all of it under the composable HVZK-for-IORs framework, and three named theorems fall out. The composition is sound exactly because each IOR explicitly handles the “future distinguisher” — namely, the simulator of whatever IOR consumes its output — so HVZK chains end-to-end.

Theorem 1 — sublinear HVZK IOPP

Compose two things: the HVZK sumcheck reduction (Part 1’s §6), reducing RC2k,Czk,sl\mathcal R_{\mathcal C^{\equiv 2^k}, \mathcal C_{\mathsf{zk}}, \mathsf{sl}} to RC,Czk,sl\mathcal R_{\mathcal C, \mathcal C_{\mathsf{zk}}, \mathsf{sl}'}; followed by a non-succinct base-case HVZK IOPP (a generalization of the simple 2×2\times-overhead protocol from §2.2 of the paper, applied at a much smaller scale).

The sublinear regime comes from picking 2kmC2^k \approx m_{\mathcal C} — i.e. setting the interleaving factor to roughly the codeword block length. Then the encoded message in C2k\mathcal C^{\equiv 2^k} has size =2k\ell = 2^k \cdot \ell' with \ell' \approx \sqrt{\ell}, the post-reduction instance is \sqrt{\ell}-sized, and the base-case IOPP (which does pay 2×2\times overhead) runs on a N\sqrt{N}-size sub-instance. Total proof length: O(N)\approx O(\sqrt{N}) alphabet symbols. Verifier time: sublinear. Overhead vs the non-private IOPP: o(1)o(1) — because the 2×2\times from the base case is amortized over a N\sqrt{N}-size piece of the original NN-size problem.

The full mask in the base case is affordable only because it runs on the small piece. That asymmetry is how this paper buys sublinear masking globally.

Theorem 2 — HVZK code-switching

The full reduction from RC12k,Czk,sl1\mathcal R_{\mathcal C_1^{\equiv 2^k}, \mathcal C_{\mathsf{zk}}, \mathsf{sl}_1} to RC2,Czk,sl2\mathcal R_{\mathcal C_2, \mathcal C_{\mathsf{zk}}, \mathsf{sl}_2}, for arbitrary codes C1,C2\mathcal C_1, \mathcal C_2 that can themselves be interleaved. Composition: run the HVZK sumcheck reduction first (drops interleaving), then run the code-switch IOR (drops to C2\mathcal C_2). HVZK with respect to a bounded distinguisher class; RBR knowledge soundness with error 2λ\leq 2^{-\lambda} at suitable field size.

This is the engine of the polylog-verifier theorem.

Theorem 4 — polylogarithmic HVZK verifier

Iterate Theorem 2 along a chain of codes C1,C2,,Cn\mathcal C_1, \mathcal C_2, \dots, \mathcal C_n, picked so that the 2k2^k-interleaving of Ci+1\mathcal C_{i+1} has the same message size as Ci\mathcal C_i (non-interleaved). Each application of Theorem 2 reduces the problem size by a factor of 2k2^k; after nn steps the problem is small enough to finish with a small variant of the §2.2 base case. Theorem 4 is parametrized over an arbitrary chain length; specific instantiations (next subsection) pin nn.

This is the WHIR/Ligerito recursion shape — same idea, ZK-augmented. (See the WHIR post for the non-ZK version of this recursion.)

Corollary 3 — HVZK Reed–Solomon ⇒ HVZK WHIR/Ligerito

Instantiate Theorem 4’s chain with Reed–Solomon codes:

  • k:=O(loglog)k := O(\log \log \ell) (folding factor per step).
  • Each Ci\mathcal C_i is Reed–Solomon over an evaluation domain of size 2im\approx 2^{-i} m.
  • Czk\mathcal C_{\mathsf{zk}} instantiated with Reed–Solomon at rate 1/logλ1/\log \lambda, mask size O~(λ)\widetilde O(\lambda).

This is the parameter regime of WHIR ([ACFY25]) and Ligerito ([NA25]), with ZK encodings substituted for the plain encodings throughout. The result:

  • O(m)+O~(λ)O(m) + \widetilde O(\lambda) alphabet symbols sent by the prover; verifier makes O(λ)O(\lambda) queries.
  • RBR knowledge soundness error 2λ\leq 2^{-\lambda}.
  • Perfect honest-verifier zero-knowledge (ζ=0\zeta = 0, thanks to RS having perfect ZK encodings with appended randomness).
  • Proof length and query complexity match the non-private WHIR/Ligerito up to o(1)o(1) overhead.

This is the headline. Theorem 2 plus the building blocks above directly yield an HVZK variant of WHIR and Ligerito — a drop-in zero-knowledge replacement for the code-agnostic IOP family at 1+o(1)1 + o(1) overhead, in the list-decoding regime, with negligible RBR soundness error. The first such result.

Comparison

ProtocolOverheadSoundness regimeCode familyVerifier
[BCL22]O(1)O(1)Unique decodingAny ZK code (tensor-style)Polylog
[RW24]1+o(1)1 + o(1)Constant error onlyZK tensor codes
Naive full-mask ZK WHIR2×\geq 2\timesList decodingReed–Solomon onlySublinear / polylog
HVZK WHIR (this paper)1+o(1)\mathbf{1 + o(1)}List decodingAny F\mathbb F-additive ZK codePolylog

The two rows that previously each owned one of these properties no longer dominate. This work is the first to put all four boxes ticked at once.

R1CS reduction with sublinear masking

The IOPP above tests proximity to constrained interleaved codes. To turn it into an end-to-end HVZK argument for NP, you reduce R1CS to that proximity test. The paper does this with 1+o(1)1 + o(1) overhead by reusing the same masking idea.

Backbone: an interleaved variant of [ACFY25]‘s Σ\Sigma-IOP for R1CS. Standard R1CS-to-sumcheck-to-codeword pipeline, but with two types of sublinear masks plugged in:

  • R1CS witness masks. Protect the matrix-vector products AzA \cdot z, BzB \cdot z, CzC \cdot z from leaking. [BCL22] handles this by padding the R1CS instance with full-size random witness — O(1)O(1) overhead. This paper uses sublinear masks instead — o(1)o(1).
  • Sumcheck masks. Same as Part 1’s §6 — small per-round masks accumulated into the joint constraint.

Both mask types are syntactically identical from the relation’s perspective — they propagate into an instance of RC,Czk,sl\mathcal R_{\mathcal C, \mathcal C_{\mathsf{zk}}, \mathsf{sl}} which our protocols can test in zero-knowledge end-to-end. Compose with Theorem 1 or Theorem 4 and you get an HVZK IOP for NP with list-decoding soundness and o(1)o(1) overhead over sending a single interleaved codeword — where [BCL22] pays a constant factor and only reaches unique-decoding soundness.

One honest caveat: the verifier here is non-succinct on R1CS-side cost, because this paper deliberately does not employ holographic preprocessing (the way [BCL22] and [RW24] do). The IOPP itself remains polylog-verifier.

If you want background on the R1CS-to-sumcheck step, Spartan and SPARK cover it.

What we didn’t cover

A few honest cuts.

High-distance codes from dispersers (Section 12 of the paper). The protocols here are code-agnostic, so you can buy more distance via the ABNNR construction [ABNNR92] — distance amplification through bipartite-disperser graphs. Plugged into Reed–Solomon, this gives amplified Reed–Solomon (ARS): RS at a low-cost base distance, then distance-boosted by gathering disperser neighbors. ARS shaves encoding time in the regime where plain RS pays O(/γlog(/γ))O(\ell/\gamma \cdot \log(\ell/\gamma)) for FFT-based evaluation, replacing it with O(/γ+log)O(\ell/\gamma + \ell \log \ell). Cost: alphabet inflates by a factor of d=O(1/γ)d = O(1/\gamma), so spot-checks cost more bytes per opening. Under reasonable parameters the paper reports a ≈ 30% argument-size win. Orthogonal to the ZK story and worth a separate read.

Round-by-round knowledge soundness proofs. We stated the errors qualitatively but skipped the full mutual-correlated-agreement accounting (the paper’s §6 and §9). WHIR treats MCA and list-decoding-regime soundness analysis for the non-ZK case; the ZK accounting is structurally similar but adds extra terms for the mask oracles.

Memory-efficient streaming prover. The paper notes that Theorem 1 is plausibly amenable to space-efficient streaming proving (à la [BBHV22], [CTY11]) — the encoding of CK\mathcal C^{\equiv K} can be done in space proportional to a single base encoding, and the sumcheck can stream too. Not pursued in the paper but flagged as future work.

Malicious-verifier ZK. Out of scope. Fiat–Shamir turns HVZK into malicious-verifier ZK for free in the non-interactive setting, which is what all deployed hash-based SNARKs care about.

Conclusion

The construction lands on five interlocking moves:

  1. The committed sumcheck relation — succinct linear forms plus mask oracles folded into a joint constraint, set up so the protocol can carry mask state through composition.
  2. Composable HVZK for IORs — the distinguisher class D\mathcal D, the composition theorem, and the discipline of making “future queries” explicit so naive composition stops shipping the witness.
  3. HVZK sumcheck via interleaved folding — small per-round masks, encode-then-fold = fold-then-encode, and correlated queries over the bigger alphabet to keep list-decoding-regime soundness affordable.
  4. Private zero-evaders for ZK-compatible OOD — randomized OOD samples that still pin a unique message while looking uniform to the verifier, plus the joint OOD sample over the message and an extra mask piece.
  5. Code-agnostic assembly — Theorems 1/2/4 plus the RS instantiation give the first HVZK variant of WHIR and Ligerito.

The practical upshot is concrete. Anonymous-credential stacks like [Fs24], built on the sublinear hash-based zkSNARK in [AHIV17], pay 2×\geq 2\times overhead today because the underlying IOP is private but old. The corresponding non-private IOPs are no longer state of the art. This work gives a drop-in path: swap the old private IOP for an HVZK WHIR/Ligerito built from the protocols above, eat 1+o(1)1 + o(1) overhead instead of 2×\geq 2\times, and the effect compounds because we’re measuring against the modern non-private state of the art rather than the 2017 baseline.

The longer-term effect is structural. The code-agnostic IOP family was the wrong place to look for ZK because the existing zero-knowledge moves were tied to polynomial codes. This paper unties them. Once the framework is in place — ZK encodings, composable HVZK-for-IORs, sublinear masking, private OOD — the next ZK variant of the next code-agnostic IOP is a much shorter walk.

References

  • This paper — Chiesa, A., Fenzi, G., & Weissenberg, G. (Feb 2026). Zero-Knowledge IOPPs for Constrained Interleaved Codes.
  • WHIR — Arnon, G., Chiesa, A., Fenzi, G., & Yogev, E. (2025). WHIR: Reed–Solomon Proximity Testing with Super-Fast Verification. EUROCRYPT ‘25.
  • Ligerito — Novakovic, A., & Angeris, G. (2025). Ligerito: A Small and Concretely Fast Polynomial Commitment Scheme. eprint 2025/1187
  • BCFW25 — Bünz, B., Chiesa, A., Fenzi, G., & Wang, W. (2025). Linear-Time Accumulation Schemes. TCC ‘25.
  • BCL22 — Bootle, J., Chiesa, A., & Liu, S. (2022). Zero-Knowledge IOPs with Linear-Time Prover and Polylogarithmic-Time Verifier. EUROCRYPT ‘22.
  • RW24 — Ron-Zewi, N., & Weiss, M. (2024). Zero-knowledge IOPs Approaching Witness Length. CRYPTO ‘24.
  • RR20 — Ron-Zewi, N., & Rothblum, R. (2020). Local Proofs Approaching the Witness Length. FOCS ‘20.
  • RR22 — Ron-Zewi, N., & Rothblum, R.D. (2022). Proving as Fast as Computing: Succinct Arguments with Constant Prover Overhead. STOC ‘22.
  • BMMS25 — Baweja, A., Mishra, P., Mopuri, T., & Shtepel, M. (2025). FICS and FACS: Fast IOPPs and Accumulation via Code-Switching. eprint 2025/737
  • ABNNR92 — Alon, N., Bruck, J., Naor, J., Naor, M., & Roth, R.M. (1992). Construction of asymptotically good low-rate error-correcting codes through pseudo-random graphs. IEEE TIT 38(2).
  • AHIV17 (Ligero) — Ames, S., Hazay, C., Ishai, Y., & Venkitasubramaniam, M. (2017). Ligero: Lightweight Sublinear Arguments Without a Trusted Setup. CCS ‘17.
  • Fs24 — Frigo, M., & Shelat, A. (2024). Anonymous credentials from ECDSA. eprint 2024/2010
  • BBHV22 — Bangalore, L., Bhadauria, R., Hazay, C., & Venkitasubramaniam, M. (2022). On Black-Box Constructions of Time and Space Efficient Sublinear Arguments from Symmetric-Key Primitives. TCC ‘22.
  • CTY11 — Cormode, G., Thaler, J., & Yi, K. (2011). Verifying computations with streaming interactive proofs. VLDB.
  • ACFY25 — Arnon, G., Chiesa, A., Fenzi, G., & Yogev, E. (2025). WHIR: Reed–Solomon Proximity Testing with Super-Fast Verification. EUROCRYPT ‘25.
  • Companion blogs. WHIR, TensorSwitch I, TensorSwitch II, Spartan, SPARK, Part 1 of this series.