Skip to content

chore!: masking at the top of the trace#22334

Merged
notnotraju merged 58 commits into
merge-train/barretenbergfrom
si/eccvm-lagrange-last-integer
Apr 21, 2026
Merged

chore!: masking at the top of the trace#22334
notnotraju merged 58 commits into
merge-train/barretenbergfrom
si/eccvm-lagrange-last-integer

Conversation

@iakovenkos

@iakovenkos iakovenkos commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Motivation

Previously, ZK masking used tail masking: random values were stored in separate MaskingTailData polynomials at the end of the trace. This forced all PCS code to be dyadic_size dependent — the batched polynomial A₀, Gemini folds, and Shplonk quotient all had to be allocated and operated at dyadic_size because the masking tails extended to the last rows. The row-disabling polynomial was also circuit-size dependent (1 - L_{n-1} - ... - L_{n-4}), requiring a padding_indicator_array to handle padded sumcheck rounds.

What Changed

Top-of-trace masking: Random masking values are written directly into witness polynomials at rows {1, 2, 3} during allocation. The row-disabling polynomial disables the first 4 rows (0–3) instead of the last 4. lagrange_first moves from row 0 to row TRACE_OFFSET (= 4).

Key Benefits

  • PCS decoupled from dyadic_size: With masking at low indices, committed polynomials only extend to max_end_index() (the actual trace extent), not dyadic_size. Gemini fold polynomials already track actual_size from A_0.end_index(). This enables future non-dyadic PCS proving where sparse circuits avoid paying for the full power-of-2 padding.
  • Circuit-size-independent row-disabling: The polynomial 1 - ∏_{i≥2}(1-u_i) depends only on sumcheck challenges, not circuit size. The padding_indicator_array is eliminated.
  • Simpler code: Removed MaskingTailData (218 lines), padding_indicator_array (93+134 lines), and all tail-batching logic. Net -707 lines across 103 files.
  • Unified verifier paths: Sumcheck and Shplemini verifiers no longer take padding_indicator_array parameters. This simplifies native, recursive, and AVM verifier flows alike.

Merge Protocol and ECC Op Wire Changes

Moving lagrange_first from row 0 to row TRACE_OFFSET (= 4) shifts the entire execution trace down by 4 rows. In Mega, the ecc_op block now starts at trace_offset() = TRACE_OFFSET + 1 = 5, and ecc_op_wire data sits one row before at row TRACE_OFFSET = 4 (to satisfy the ecc_op_wire[row] == w_shift[row] constraint).

The merge protocol must match this layout: it prepends TRACE_OFFSET zeros to its table polynomials (L, R, and M in PREPEND mode) so that the prover's Shplonk quotient is consistent with the ecc_op_wire commitments held by the verifier. Previously, these polynomials started at row 0; now they start at row TRACE_OFFSET to align with the shifted circuit layout.

The Translator receives its op queue data from Mega's ecc_op_wires. Since the merge protocol handles the offset alignment in chonk, standard Mega → Translator flows work correctly. However, the two-layer AVM flow (AVM recursive verifier inside a Mega circuit) required adding TRACE_OFFSET = 0 to the standalone AVM flavor. The MegaAVM flavor inherits Mega's TRACE_OFFSET = 4, so ecc_op_wires in the outer Mega circuit are offset. The inner AVM has no disabled region (TRACE_OFFSET = 0), but since it runs inside a Mega wrapper that handles the merge protocol, the op queue data alignment stays consistent across the boundary.

Files Changed (103 files, +1484/-2191)

Deleted

  • sumcheck/masking_tail_data.hpp — tail masking infrastructure
  • stdlib/primitives/padding_indicator_array/ — virtual-round indicator for old row-disabling

Added

  • ultra_honk/zk_boundary.test.cpp — tests for masking layout, ecc_op alignment, row-disabling

Core changes

  • Prover instance (prover_instance.cpp): Polynomials allocated with add_masking() at rows {1,2,3}; lagrange_first at TRACE_OFFSET; trace blocks start at TRACE_OFFSET + 1
  • Oink prover (oink_prover.cpp): Removed all tail references; commitments use base polynomials directly
  • Ultra prover (ultra_prover.cpp): CRS sized to max_end_index() instead of dyadic_size() for ZK
  • ECCVM flavor (eccvm_flavor.hpp): Polynomial allocation with trace offset; Lagrange polys shifted
  • ECCVM prover (eccvm_prover.cpp): Removed tail batching and extend_with_tail copies for translation polys
  • Sumcheck (sumcheck.hpp): Unified ZK/non-ZK virtual round paths; removed padding_indicator_array from verifier
  • Row-disabling (row_disabling_polynomial.hpp): Simplified to 1 - ∏_{i≥2}(1-u_i)
  • Gemini (gemini_impl.hpp): Fold polynomials track actual_size from A_0.end_index()
  • Shplemini (shplemini.hpp): Removed padding_indicator_array from compute_batch_opening_claim
  • AVM (vm2/constraining/): Removed padding_indicator_array from native and recursive AVM verifiers; added TRACE_OFFSET = 0 to AVM flavor
  • Merge protocol (merge_prover.cpp/hpp, merge_verifier.cpp/hpp): Prepends TRACE_OFFSET zeros to table polynomials; verifier accounts for offset in commitment checks
  • Grand product / log-derivative: Start after disabled region via gp_start / start_index parameters
  • Databus (databus.hpp): DEFAULT_VALUE = 0 (point-at-infinity commitment matches default)

Known Issues / Follow-ups

  • The zk ultra prover efficiency needs to be investigated.

…ay from RDP

With top-of-trace masking, the row-disabling polynomial 1 - ∏_{i≥2}(1-u_i)
is circuit-size independent. The verifier evaluates it over ALL D challenges
without needing log_circuit_size or padding_indicator_array.

Prover: ZK virtual rounds now use compute_virtual_contribution * (1-L) + libra
instead of sending zero univariates. Libra univariates cover all D rounds.

Verifier: apply_zk_corrections evaluates RDP from challenge vector size.
compute_padding_indicator_array returns all-1s (unified path).
@iakovenkos iakovenkos self-assigned this Apr 9, 2026
@iakovenkos iakovenkos removed the ci-full Run all master checks. label Apr 15, 2026
@iakovenkos iakovenkos added the ci-full Run all master checks. label Apr 15, 2026
@notnotraju notnotraju self-requested a review April 16, 2026 07:30
MegaExecutionTraceBlocks() = default;

void compute_offsets()
void compute_offsets(size_t trace_offset = TRACE_OFFSET)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at l. 305 in this file (meta_execution_trace.hpp), stale comment

@fcarreiro fcarreiro removed their request for review April 16, 2026 10:51
Comment thread barretenberg/cpp/src/barretenberg/constants.hpp Outdated
notnotraju added 2 commits April 17, 2026 12:00
- Add merge_constants.hpp with MERGE_FULL_SHIFT and MERGE_APPEND_OUTPUT_SHIFT;
  merge_prover and merge_verifier now reference the shared constants instead
  of duplicating the formulas.
- Add static_assert in merge_prover.cpp that MERGE_APPEND_OUTPUT_SHIFT equals
  TranslatorFlavor::RANDOMNESS_START, since [M] is copy-constrained with the
  Translator's op queue wire commitments.
- Rewrite stale doc comments in mega_execution_trace.hpp (ecc_op-first
  requirement, now explained via merge shift) and constants.hpp (masking row
  range).
…R_OP

The reserved op-slot count at the start of the merged polynomial MUST be
APPEND_OUTPUT_SHIFT / NUM_ROWS_PER_OP. The previous formula subtracted
NUM_ZERO_ROWS, which was numerically equal by coincidence. Add a static_assert
that APPEND_OUTPUT_SHIFT is a multiple of NUM_ROWS_PER_OP.
@@ -0,0 +1,25 @@
// === AUDIT STATUS ===

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just added this for my own peace of mind.


namespace bb {

static_assert(MERGE_APPEND_OUTPUT_SHIFT == TranslatorFlavor::RANDOMNESS_START,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More static asserts can't hurt. (I was trying to figure out how everything matched.)

static_assert(MERGE_APPEND_OUTPUT_SHIFT % UltraEccOpsTable::NUM_ROWS_PER_OP == 0,
"MERGE_APPEND_OUTPUT_SHIFT must be a multiple of NUM_ROWS_PER_OP so that ops land "
"on even row boundaries after the shift");
constexpr size_t reserved_op_slots = MERGE_APPEND_OUTPUT_SHIFT / UltraEccOpsTable::NUM_ROWS_PER_OP;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small change here, to make it clear what the synthetic meaning of the constants is.

Add a "Trace Layout Alignment" section covering the Mega circuit preamble,
the Translator mini-circuit preamble, and MegaAvmFlavor, with row-by-row
diagrams and the constants tying them together (MERGE_FULL_SHIFT,
MERGE_APPEND_OUTPUT_SHIFT = RANDOMNESS_START).
- $R_j = t_j$ (current subtable)
- New operations are added at the end (with optional fixed offset)

## Trace Layout Alignment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iakovenkos I added this section on what the trace physically looks like, can you give it a quick look?

@notnotraju notnotraju self-requested a review April 20, 2026 19:22

@notnotraju notnotraju left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing, thank you!

// masked; its read_counts column is.
auto& values_poly = entities[0];
auto& read_counts_poly = entities[1];
if constexpr (bus_idx != 0) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@federicobarbacovi does it look reasonable to you?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it looks good 👍

@notnotraju notnotraju merged commit 7dbe0b2 into merge-train/barretenberg Apr 21, 2026
12 checks passed
@notnotraju notnotraju deleted the si/eccvm-lagrange-last-integer branch April 21, 2026 09:55
dipkakwani pushed a commit to dipkakwani/aztec-packages that referenced this pull request Apr 22, 2026
BEGIN_COMMIT_OVERRIDE
chore!: masking at the top of the trace (AztecProtocol#22334)
chore: Fix translator's static assert (AztecProtocol#22695)
chore: bump mem for large circuit test (AztecProtocol#22690)
END_COMMIT_OVERRIDE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-full Run all master checks.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants