· 6 min read ·

Inside the UltraHONK Verifier: Why Moving to Multilinear Polynomials Changes Everything

Source: lobsters

The HashCloak team recently published a walkthrough of the UltraHONK verifier, tracing the protocol step by step through Aztec’s Barretenberg library. It is a useful piece of documentation for anyone trying to understand a proof system that does not have a single canonical paper behind it. What the article does not cover as explicitly is why the verifier looks the way it does: the design pressures that drove Aztec away from UltraPLONK’s structure and toward this particular combination of sumcheck, multilinear commitments, and logarithmic derivatives.

That evolutionary story is worth telling, because it makes the verifier code significantly easier to reason about.

From UltraPLONK to UltraHONK

PLONK (Gabizon, Williamson, Ciobotaru, 2019) was a breakthrough in universal SNARKs. Its core machinery relies on univariate polynomials evaluated over a multiplicative subgroup of a prime field. The verifier’s job is to check that a handful of polynomial identities hold at a random point, using KZG commitments and two pairing operations.

Aztec’s UltraPLONK extended this with custom gates, width-4 wires, and Plookup-style lookup tables. The result was a highly expressive circuit system; the verification path stayed largely the same. The prover still ran large-domain FFTs, and recursive proof composition still required computing KZG opening proofs inside a circuit, which costs roughly 6 pairing operations. Each pairing inside a BN254 circuit runs to several million constraints. Recursive verification of a UltraPLONK proof was technically possible but practically expensive enough to limit the depth of proof aggregation trees.

The pivot to UltraHONK was, at its core, a decision to fix that recursion cost. The HyperPlonk paper (Boneh et al., 2022) had shown that PLONK-style constraint systems could be reformulated over the boolean hypercube {0,1}^n using multilinear extensions, replacing the FFT-based univariate structure with the sumcheck protocol. The sumcheck prover does not need FFTs; verification reduces to evaluating a low-degree polynomial at a random point and checking one commitment opening. When the commitment scheme is an inner product argument (IPA) over the Grumpkin curve, there are no pairings anywhere in the recursive path.

The Verifier’s Structure

The UltraHONK verifier, as implemented in Barretenberg’s ultra_honk/ultra_verifier.cpp, runs four conceptually distinct phases.

Phase 1: Transcript and challenge derivation. All prover messages, including wire polynomial commitments, lookup commitments, and sumcheck round polynomials, flow into a transcript. Challenges are derived via the Fiat-Shamir transform: each challenge is a hash of the transcript up to that point. For native verification, Barretenberg uses BLAKE3 or Keccak; for recursive in-circuit verification, it uses Poseidon2, which costs roughly 50-200 constraints per hash rather than the ~25,000 that Keccak would require.

The challenges in order are: eta (Logup combination), beta and gamma (permutation argument), alpha (relation combiner), the per-round sumcheck challenges r_1, ..., r_n, and zeta (commitment opening). Getting this ordering right matters; a transcript that hashes messages out of order is not merely inefficient but insecure.

Phase 2: Sumcheck verification. The constraint system is encoded as a multilinear polynomial F_relations over {0,1}^n, where n = log_2(circuit_size). All gate constraints, copy constraints, and lookup constraints are combined into this single polynomial, weighted by powers of the challenge alpha.

The verifier receives n univariate polynomials s_1(X), ..., s_n(X), one per sumcheck round. For each round i, it checks:

s_i(0) + s_i(1) == claimed_sum_i

derives a new challenge r_i, and sets claimed_sum_{i+1} = s_i(r_i). After all n rounds, it holds a final scalar claim: the value that F_relations should take at the point r = (r_1, ..., r_n). The degree of each round polynomial is bounded by the maximum degree of the gate relations; for UltraHONK’s arithmetic gate, this is degree 5.

The verifier then evaluates F_relations at r using the prover-supplied witness evaluations and the verifier-computable selector evaluations, and checks that this matches the final sumcheck claim. The selector polynomials are part of the verification key (committed at circuit compilation time), so their evaluations at r can be extracted from commitment openings the prover includes in the proof.

Phase 3: Relation evaluation. Each relation contributes a multilinear polynomial identity. The UltraArithmetic relation checks the gate equation:

q_m * w_l * w_r + q_l * w_l + q_r * w_r + q_o * w_o + q_4 * w_4 + q_c + q_arith * ...

The permutation relation checks the grand product identity for copy constraints. The Logup relation checks the fractional sum identity for lookups. The verifier evaluates each of these at the challenge point r, using the claimed wire evaluations and the selector evaluations it extracted from the verification key commitments.

Phase 4: Commitment opening. The prover has claimed specific evaluations for all committed polynomials at the point r. The verifier must check these against the commitments. Two paths exist in Barretenberg.

With the KZG backend, Barretenberg uses Zeromorph (Kohrita, Towa, 2023). Zeromorph converts a multilinear evaluation proof into a univariate KZG proof via a quotient polynomial decomposition. The verifier performs one pairing check for the batch of evaluation claims. This requires the BN254 trusted setup and the Ethereum pairing precompile, making it suitable for on-chain verification.

With the IPA backend, the verifier runs the inner product argument verification loop over the Grumpkin curve. Each of the n rounds halves the vector length via a group element fold. No pairings are needed, and no trusted setup is required beyond a public reference string of random group elements. This is the path Aztec uses for its recursive rollup circuits.

Logup Replaces Plookup

The lookup argument deserves separate attention. UltraPLONK used Plookup, which encodes table membership via a sorted-and-merged multiset argument. The prover must sort the lookup witness alongside the table and commit to the sorted result, which adds columns and complicates the permutation argument.

UltraHONK uses Logup (Haböck, 2022), built on logarithmic derivatives. The identity is:

sum_i 1/(X - f_i) = sum_j m_j / (X - t_j)

where f_i are the lookup values, t_j are the table entries, and m_j are multiplicities. This holds (at a random X = beta) if and only if the lookup values are in the table with correct multiplicities. No sorting is required.

The fractional sums are computed via the GKR protocol (Goldwasser, Kalai, Rothblum, 2008), a sumcheck-based argument for layered arithmetic circuits. Rather than committing to all the intermediate 1/(beta - f_i) values as separate witness columns, the prover uses GKR to prove the computation of the sum directly. The GKR verification reduces to evaluation claims on the original witness polynomials, which fold into the main sumcheck. The net result is fewer committed columns and a cleaner verifier path.

What the Verifier Tells You About the System

Looking at the verifier in isolation is a reasonable way to understand what a proof system actually guarantees. The UltraHONK verifier is, mechanically, a small program: hash inputs to derive challenges, check n round polynomials, evaluate gate identities at a point, verify one batch commitment opening. That compactness is the design goal.

The Barretenberg codebase distributes the verifier across ultra_verifier.cpp, the sumcheck implementation in sumcheck/sumcheck.hpp, and the relation evaluators in relations/. Following those files alongside the HashCloak article gives a fairly complete picture of the protocol.

For anyone building with Noir, the compilation pipeline runs from .nr source to ACIR (Aztec Circuit IR) to Barretenberg’s native circuit format, before finally hitting this verifier. The verification key produced by bb write_vk encodes the selector polynomial commitments that the verifier uses in phase 3. Swapping the commitment scheme (KZG vs IPA) changes phase 4 but nothing else; the rest of the protocol is commitment-scheme-agnostic by design.

The shift from UltraPLONK to UltraHONK is not a complete redesign. The circuit expressiveness is essentially the same: the gate types, lookup tables, and copy constraints carry over. What changed is the verification path, specifically to make recursion cheap enough to build a production rollup on. The sumcheck protocol and multilinear extensions are the mechanism; the IPA over Grumpkin is the consequence.

Was this interesting?