Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
8036d67
ultra zk passes
iakovenkos Apr 6, 2026
7ceba30
fix mega zk tests, add masking factories
iakovenkos Apr 6, 2026
31ff8f0
eccvm works
iakovenkos Apr 6, 2026
467b162
unify ZK and non-ZK virtual round paths, remove padding_indicator_arr…
iakovenkos Apr 6, 2026
2984826
batched honk is ok
iakovenkos Apr 6, 2026
24e937e
removed padding indicator array
iakovenkos Apr 6, 2026
5a6ae03
rm masking tail data
iakovenkos Apr 6, 2026
04ec473
uniform layout
iakovenkos Apr 6, 2026
4451f34
deb
iakovenkos Apr 7, 2026
4890241
databus fix
iakovenkos Apr 7, 2026
de9907e
fix tests
iakovenkos Apr 7, 2026
cc5fc28
clean up debug
iakovenkos Apr 7, 2026
da351d0
share logic
iakovenkos Apr 7, 2026
3c7d2a0
further clean up
iakovenkos Apr 7, 2026
20d9acb
gemini non-dyadic
iakovenkos Apr 7, 2026
b324157
fix tests
iakovenkos Apr 7, 2026
8e1e10a
Merge remote-tracking branch 'origin/merge-train/barretenberg' into s…
iakovenkos Apr 9, 2026
cd0f7ef
Merge remote-tracking branch 'origin/merge-train/barretenberg' into s…
iakovenkos Apr 9, 2026
25203ef
test fixes
iakovenkos Apr 9, 2026
614dc87
fix
iakovenkos Apr 9, 2026
f339477
slightly better usage of trace offset
iakovenkos Apr 9, 2026
2aafcd2
rm dead code
iakovenkos Apr 9, 2026
ba00a52
clean up
iakovenkos Apr 9, 2026
e8fcb61
incorrect comment
iakovenkos Apr 9, 2026
4c9259b
fix
iakovenkos Apr 9, 2026
4476d07
upd vk hash
iakovenkos Apr 9, 2026
584d06d
fix avm logderivative
iakovenkos Apr 10, 2026
bb678f7
clean up masking tests
iakovenkos Apr 10, 2026
528eeef
mask missing databus wires, rm dead code
iakovenkos Apr 10, 2026
63f5b67
upd skill
iakovenkos Apr 10, 2026
dfbfeea
fix sol
iakovenkos Apr 10, 2026
76161ea
fix sol [2]
iakovenkos Apr 10, 2026
0494fdf
fix avm 2layer
iakovenkos Apr 12, 2026
54c1c64
upd merge md and fix segfault
iakovenkos Apr 12, 2026
86ac343
Merge remote-tracking branch 'origin/merge-train/barretenberg' into s…
iakovenkos Apr 12, 2026
3464132
Merge remote-tracking branch 'origin/merge-train/barretenberg' into s…
iakovenkos Apr 12, 2026
e3b57fd
eccvm trace offset
iakovenkos Apr 12, 2026
0c6b426
fix tests
iakovenkos Apr 12, 2026
70c4f90
upd vk hash
iakovenkos Apr 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 29 additions & 68 deletions barretenberg/.claude/skills/sumcheck/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ where `P_i` are multilinear witness/selector polynomials, `F` is the batched rel
- `sumcheck_round.hpp` — Per-round logic for both prover and verifier
- `sumcheck_output.hpp` — Output struct (challenge vector, claimed evaluations, ZK data)
- `zk_sumcheck_data.hpp` — Libra masking polynomials and running sums
- `masking_tail_data.hpp` — ZK masking values stored separately from short witness polys
- `Sumcheck.md` — Detailed mathematical documentation

### Supporting:
Expand All @@ -31,7 +30,6 @@ where `P_i` are multilinear witness/selector polynomials, `F` is the batched rel
- `flavor/partially_evaluated_multivariates.hpp` — Book-keeping table for partial evaluation
- `flavor/flavor_concepts.hpp` — Flavor dispatch concepts
- `relations/` — Relation types, utilities, parameters, nested containers
- `stdlib/primitives/padding_indicator_array/` — Recursive padding indicator computation

### Tests:

Expand Down Expand Up @@ -142,70 +140,43 @@ Three mechanisms provide zero-knowledge:
### 1. Libra Masking
Adds a structured random multivariate `G(X) = a_0 + g_0(X_0) + ... + g_{d-1}(X_{d-1})` scaled by a Fiat-Shamir challenge. Each round's univariate gets a Libra correction. The concatenated Libra univariates are committed and their evaluation is proved via SmallSubgroupIPA.

### 2. Row Disabling + Masking Tail
Last `NUM_MASKED_ROWS=3` positions (n-3, n-2, n-1) contain random masking values. The row disabling
polynomial `(1-L)` vanishes at the last 4 rows, so the relation sum is unaffected. This adds +1 to
the round univariate degree for ZK flavors.

**Masking Tail Optimization**: Witness polynomials are allocated to `trace_active_range_size()` (shorter
than `dyadic_size`) to save memory. Masking values are NOT written into the polynomials — they are
stored separately in `MaskingTailData` (see `sumcheck/masking_tail_data.hpp`), which uses the
**AllEntities pattern**: `AllEntities<bool> is_masked`, `AllEntities<Polynomial> tails` (small 3-element
tail polys with full virtual_size), and `AllEntities<std::array<FF, 2>> folded` (folded values for
sumcheck rounds). This enables zip-iteration with `extended_edges` in `compute_disabled_contribution`
instead of index-based lookups. The main sumcheck loop **excludes** disabled edge pairs via
`excluded_tail_size`; `compute_disabled_contribution` handles them separately using folded masking
values, multiplied by `(1-L)`, and **added** to the active contribution.

**Critical `excluded_tail_size` invariant**: Must be non-zero ONLY when `Flavor::HasZK && UseRowDisablingPolynomial<Flavor>`:
```cpp
size_t excluded_tail_size = (Flavor::HasZK && UseRowDisablingPolynomial<Flavor>) ? 4 : 0;
```
If set incorrectly for flavors that don't call `compute_disabled_contribution` (e.g., Translator,
MultilinearBatching), edge pairs are silently dropped, causing sumcheck round failures. The post-round-0
update `excluded_tail_size = 2` must also be guarded by `UseRowDisablingPolynomial<Flavor>`.
### 2. Row Disabling + In-Place Masking (Top-of-Trace)
The first `NUM_DISABLED_ROWS_IN_SUMCHECK=4` rows of the trace are reserved. For ZK flavors,
`NUM_MASKED_ROWS=3` random values are written directly into witness polynomials at rows 1, 2, 3
(row 0 is the zero row for shifts). The row disabling polynomial `(1-L)` vanishes at rows 0-3,
so the relation sum is unaffected. This adds +1 to the round univariate degree for ZK flavors.

`compute_effective_round_size` behavior:
- ZK + row disabling: `min(round_size - excluded_tail_size, witness_end_index)`
- ZK without row disabling (Translator): `round_size` (no exclusion, full iteration)
- Non-ZK: `min(round_size, witness_end_index)`
The layout is **uniform across ZK and non-ZK** — non-ZK flavors have zeros in these rows, so
relations are trivially satisfied there without row disabling.

Masking tail fold timing:
- Round 0: fold after `partially_evaluate` (no PE access needed)
- Rounds 1+: fold **before** `partially_evaluate_in_place` (rounds 2+ read PE neighbors)
**`excluded_head_size` invariant**: Must be non-zero ONLY when `Flavor::HasZK && UseRowDisablingPolynomial<Flavor>`:
```cpp
size_t excluded_head_size = (Flavor::HasZK && UseRowDisablingPolynomial<Flavor>) ? NUM_DISABLED_ROWS_IN_SUMCHECK : 0;
```
The main sumcheck loop skips the first `excluded_head_size` edge pairs; `compute_disabled_contribution`
handles them separately, multiplied by `(1-L)`, and added to the active contribution. For non-ZK
flavors, the head rows are all zeros and are processed by the main loop (no exclusion needed).

### 3. Witness Masking in PCS
`MaskingTailData::tails` stores small `Polynomial` objects (3 coefficients at positions {n-3, n-2, n-1},
full virtual_size). Commitments are adjusted as `C' = C_short + commit(tail_poly)` at `add_to_batch`
time — callers pass `&tails.field_name` directly or use `zip_view` over parallel getters (e.g.,
`zip_view(polys.get_wires(), tails.get_wires(), labels.get_wires())`). Non-masked tails are empty
and skipped automatically. In the PCS, tail polynomials are batched alongside base polynomials via
`add_tails_to_batcher` (Ultra Honk, Batched Translator). For ECCVM translation polys (which need
univariate evaluation at full size), tails are merged via `extended += tail` using named fields.
A Gemini masking polynomial ensures PCS opening proofs remain zero-knowledge.
Masking values are written directly into the polynomials (in-place), so `commit(poly)` produces the
correct masked commitment directly. A Gemini masking polynomial ensures PCS opening proofs remain
zero-knowledge.

### Batched Honk Translator Integration

The batched translator combines MegaZK and Translator into a joint sumcheck/PCS. Key masking tail
integration points in `batched_honk_translator_prover.cpp`:

1. **Sumcheck `do_round`**: MegaZK uses `+= compute_disabled_contribution(... rdp, masking_tail)`.
Translator has no disabled contribution.
2. **`fold_masking_values`**: Called per MegaZK round using MegaZK's `round_size` and PE.
3. **`excluded_tail_size = 2`**: Set after round 0 for `mega_zk_round` only.
4. **Claimed eval corrections**: Applied to `mega_zk_claimed_evals` using first `mega_zk_log_n`
challenges. Must also write corrected values back into `mega_zk_partial[0]` so that
`compute_virtual_contribution` in virtual rounds uses them.
5. **PCS**: `add_tails_to_batcher` on the joint batcher. Tails at MegaZK positions within
the larger joint batcher.
The batched translator combines MegaZK and Translator into a joint sumcheck/PCS.
MegaZK uses `+= compute_disabled_contribution(... rdp)` for the disabled head rows.
Translator has no disabled contribution (it uses tail masking independently).
After round 0, `excluded_head_size = 2` for the MegaZK round (disabled head collapses to 1 edge pair).

## Virtual Rounds and Padding

For constant proof size (recursive verification):
- **Real rounds** (0 to `multivariate_d - 1`): standard sumcheck
- **Virtual/padding rounds** (`multivariate_d` to `virtual_log_n - 1`): polynomials treated as zero-extended

A **padding indicator array** `[1,1,...,1,0,0,...,0]` tells the verifier which rounds are real vs padding. In recursive verification, this is computed in-circuit from a constrained `log_circuit_size` for constant gate count.
With top-of-trace masking, the row-disabling polynomial is circuit-size independent, so all sumcheck
rounds are processed uniformly by the verifier — no padding indicator needed.

## Transcript / Fiat-Shamir Protocol

Expand Down Expand Up @@ -254,24 +225,14 @@ The Fiat-Shamir transcript is the backbone of non-interactive soundness. Any inc

- **When replacing a PCS scheme, audit all implicit guarantees the old scheme provided.** Zeromorph provided degree checks that enforced polynomials are zero outside their domain. When it was replaced, that enforcement disappeared silently. Explicit relation constraints must replace any lost implicit guarantees (degree bounds, zero-outside-domain, etc.).

### excluded_tail_size and Flavor Guards
### excluded_head_size and Flavor Guards

- **`excluded_tail_size` must match whether `compute_disabled_contribution` is called.** If edges are
- **`excluded_head_size` must match whether `compute_disabled_contribution` is called.** If edges are
excluded from the main loop but no disabled contribution adds them back, they're silently lost.
This breaks sumcheck at round 1+ (round 0 is masked by `(1-L)=0`).
- **Guard `excluded_tail_size` on BOTH `HasZK` AND `UseRowDisablingPolynomial`.** Non-ZK flavors
(MultilinearBatching, MegaFlavor) and ZK flavors without row disabling (Translator) must have
`excluded_tail_size = 0`. Getting this wrong hangs or corrupts the sumcheck for those flavors.
- **When writing back corrected evaluations for virtual rounds** (batched translator), ensure the
corrections are applied to the PE multivariates (not just the claimed evals struct), so
`compute_virtual_contribution` reads the right values.
- **MaskingTailData uses AllEntities pattern** — `is_masked`, `tails`, `folded` are all
`AllEntities<T>` structs, enabling direct iteration via `get_masked()`/`get_shifted()` without
pointer matching or index lookups. Registration, folding, eval corrections, and PCS batching
all use these parallel getters. Shifted tails are derived at registration time
(shift[k] = unshifted[k+1]). Callers access tails by named field (e.g., `tails.w_l`) or
`zip_view` over parallel getters. Only `compute_disabled_contribution` in `sumcheck_round.hpp`
uses `is_masked.get_all()` with index-based access (to correlate with `extended_edges`).
- **Guard `excluded_head_size` on BOTH `HasZK` AND `UseRowDisablingPolynomial`.** Non-ZK flavors
have zeros in the head rows (harmless in the main loop), and ZK flavors without row disabling
(Translator) must have `excluded_head_size = 0`. Getting this wrong causes a segfault (non-ZK)
or corrupts the sumcheck (Translator).

### Merge and Refactoring Safety

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ script_path="$root/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_cha
# - Generate a hash for versioning: sha256sum bb-chonk-inputs.tar.gz
# - Upload the compressed results: aws s3 cp bb-chonk-inputs.tar.gz s3://aztec-ci-artifacts/protocol/bb-chonk-inputs-[hash(0:8)].tar.gz
# Note: In case of the "Test suite failed to run ... Unexpected token 'with' " error, need to run: docker pull aztecprotocol/build:3.0
pinned_short_hash="d519f639"
pinned_short_hash="704d1d9d"
pinned_chonk_inputs_url="https://aztec-ci-artifacts.s3.us-east-2.amazonaws.com/protocol/bb-chonk-inputs-${pinned_short_hash}.tar.gz"

function update_pinned_hash_in_script {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "barretenberg/goblin/goblin.hpp"
#include "barretenberg/goblin/goblin_verifier.hpp"
#include "barretenberg/goblin/merge_prover.hpp"
#include "barretenberg/goblin/mock_circuits.hpp"
#include "barretenberg/srs/global_crs.hpp"
#include "barretenberg/stdlib/honk_verifier/ultra_verification_keys_comparator.hpp"
Expand Down Expand Up @@ -51,7 +52,10 @@ class BoomerangGoblinRecursiveVerifierTests : public testing::Test {
MergeCommitments merge_commitments;
auto t_current = goblin.op_queue->construct_current_ultra_ops_subtable_columns();
auto T_prev = goblin.op_queue->construct_previous_ultra_ops_table_columns();
CommitmentKey<curve::BN254> pcs_commitment_key(goblin.op_queue->get_ultra_ops_table_num_rows());
MergeProver::shift_table_by_disabled_rows(t_current);
MergeProver::shift_table_by_disabled_rows(T_prev);
CommitmentKey<curve::BN254> pcs_commitment_key(goblin.op_queue->get_ultra_ops_table_num_rows() +
MergeProver::TRACE_OFFSET);
for (size_t idx = 0; idx < MegaFlavor::NUM_WIRES; idx++) {
merge_commitments.t_commitments[idx] = pcs_commitment_key.commit(t_current[idx]);
merge_commitments.T_prev_commitments[idx] = pcs_commitment_key.commit(T_prev[idx]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "barretenberg/circuit_checker/circuit_checker.hpp"
#include "barretenberg/common/test.hpp"

#include "barretenberg/flavor/mega_avm_flavor.hpp"
#include "barretenberg/goblin/mock_circuits.hpp"
#include "barretenberg/goblin_avm/goblin_avm.hpp"
#include "barretenberg/goblin_avm/goblin_avm_verifier.hpp"
Expand Down Expand Up @@ -53,15 +54,17 @@ class BoomerangGoblinAvmRecursiveVerifierTests : public testing::Test {
GoblinAvm goblin(inner_builder);
MockCircuits::construct_arithmetic_circuit(inner_builder);

// Merge the ecc ops from the newly constructed circuit
// Build a MegaAvm prover instance to get ecc_op_wire commitments matching the real flow.
auto mega_avm_instance = std::make_shared<ProverInstance_<MegaAvmFlavor>>(inner_builder);
CommitmentKey<curve::BN254> pcs_commitment_key(mega_avm_instance->dyadic_size());

auto goblin_proof = goblin.prove();

// Subtable values and commitments - needed for (Recursive)MergeVerifier
// Commit to ecc_op_wire polynomials from the MegaAvm prover instance
TableCommitments table_commitments;
auto ultra_ops_table_columns = goblin.op_queue->construct_ultra_ops_table_columns();
CommitmentKey<curve::BN254> pcs_commitment_key(goblin.op_queue->get_ultra_ops_table_num_rows());
for (size_t idx = 0; idx < MegaFlavor::NUM_WIRES; idx++) {
table_commitments[idx] = pcs_commitment_key.commit(ultra_ops_table_columns[idx]);
size_t idx = 0;
for (auto& wire : mega_avm_instance->polynomials.get_ecc_op_wires()) {
table_commitments[idx++] = pcs_commitment_key.commit(wire);
}

RecursiveTableCommitments recursive_table_commitments;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ template <class RecursiveBuilder> class BoomerangRecursiveMergeVerifierTest : pu
RecursiveMergeCommitments recursive_merge_commitments;
auto t_current = op_queue->construct_current_ultra_ops_subtable_columns();
auto T_prev = op_queue->construct_previous_ultra_ops_table_columns();
MergeProver::shift_table_by_disabled_rows(t_current);
MergeProver::shift_table_by_disabled_rows(T_prev);
for (size_t idx = 0; idx < InnerFlavor::NUM_WIRES; idx++) {
merge_commitments.t_commitments[idx] = merge_prover.pcs_commitment_key.commit(t_current[idx]);
merge_commitments.T_prev_commitments[idx] = merge_prover.pcs_commitment_key.commit(T_prev[idx]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,7 @@ class BatchedHonkTranslatorTests : public ::testing::Test {
* @param mega_zk_log_n log₂(circuit_size) of the MegaZK instance
* @param num_mega_zk_pub_inputs number of MegaZK public inputs
*/
static TranscriptManifest build_expected_batched_manifest(const size_t mega_zk_log_n,
const size_t num_mega_zk_pub_inputs)
static TranscriptManifest build_expected_batched_manifest(const size_t num_mega_zk_pub_inputs)
{
using MZK = MegaZKFlavor;
using Trans = TranslatorFlavor;
Expand Down Expand Up @@ -197,19 +196,7 @@ class BatchedHonkTranslatorTests : public ::testing::Test {
round++;

// ── Rounds 5..4+JOINT_LOG_N: joint sumcheck ──────────────────────────────
// Loop runs one extra iteration (i == JOINT_LOG_N) to place MegaZK evaluations in the
// post-sumcheck round when mega_zk_log_n == JOINT_LOG_N (no virtual rounds).
for (size_t i = 0; i <= JOINT_LOG_N; ++i) {
// MegaZK evaluations are sent immediately after the real rounds, before the first virtual
// round's univariate. In the transcript manifest they appear at the start of the round
// that contains univariate_{mega_zk_log_n} (or, when mega_zk_log_n == JOINT_LOG_N, in
// the post-sumcheck round before evaluations_translator).
if (i == mega_zk_log_n) {
m.add_entry(round, "Sumcheck:evaluations", MZK::NUM_ALL_ENTITIES);
}
if (i == JOINT_LOG_N) {
break; // No univariate/challenge for the extra iteration
}
for (size_t i = 0; i < JOINT_LOG_N; ++i) {
// Translator mini-circuit evaluations are sent after round LOG_MINI_CIRCUIT_SIZE-1
// and appear before univariate_{LOG_MINI} in the manifest.
if (i == LOG_MINI) {
Expand All @@ -223,6 +210,8 @@ class BatchedHonkTranslatorTests : public ::testing::Test {
}

// ── Post-sumcheck evaluations ─────────────────────────────────────────────
// MegaZK evaluations are sent after all sumcheck rounds (real + virtual).
m.add_entry(round, "Sumcheck:evaluations", MZK::NUM_ALL_ENTITIES);
m.add_entry(round, "Sumcheck:evaluations_translator", Trans::NUM_FULL_CIRCUIT_EVALUATIONS);
m.add_entry(round, "Libra:claimed_evaluation", Fr);
m.add_entry(round, "Libra:grand_sum_commitment", G);
Expand Down Expand Up @@ -416,7 +405,6 @@ TEST_F(BatchedHonkTranslatorTests, ProverManifestConsistency)
auto mega_zk_inst = std::make_shared<MegaZKProverInst>(mega_zk_circuit);
auto mega_zk_vk = std::make_shared<MegaZKVK>(mega_zk_inst->get_precomputed());

const size_t mega_zk_log_n = mega_zk_inst->log_dyadic_size();
const size_t num_mega_zk_pub_inputs = mega_zk_inst->num_public_inputs();

// Prove with manifest tracking enabled.
Expand All @@ -427,7 +415,7 @@ TEST_F(BatchedHonkTranslatorTests, ProverManifestConsistency)
[[maybe_unused]] auto __ = prover.prove(translator_key);

auto prover_manifest = prover_transcript->get_manifest();
auto expected_manifest = build_expected_batched_manifest(mega_zk_log_n, num_mega_zk_pub_inputs);
auto expected_manifest = build_expected_batched_manifest(num_mega_zk_pub_inputs);

ASSERT_EQ(prover_manifest.size(), expected_manifest.size()) << "Manifest round count mismatch";
for (size_t round = 0; round < expected_manifest.size(); ++round) {
Expand Down
Loading
Loading