From 337d3bb97e4d044b42f953c23b8ba63c30ff09e6 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Wed, 6 May 2026 09:23:44 +0000 Subject: [PATCH 01/17] k shifts resurrected --- .../commitment_schemes/claim_batcher.hpp | 23 ++++++++++-- .../commitment_schemes/gemini/gemini.hpp | 36 +++++++++++++++++++ .../commitment_schemes/ipa/ipa.test.cpp | 28 +++++++++++++++ .../commitment_schemes/kzg/kzg.test.cpp | 34 ++++++++++++++++++ .../utils/mock_witness_generator.hpp | 35 ++++++++++++++++-- .../barretenberg/polynomials/polynomial.cpp | 11 ++++++ .../barretenberg/polynomials/polynomial.hpp | 6 ++++ .../polynomials/polynomial.test.cpp | 28 +++++++++++++++ 8 files changed, 195 insertions(+), 6 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp index 4f7e3370c2da..b724ceeb81b7 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp @@ -34,20 +34,26 @@ template struct ClaimBatcher_ { Fr scalar = 0; }; - std::optional unshifted; // commitments and evaluations of unshifted polynomials - std::optional shifted; // commitments of to-be-shifted-by-1 polys, evals of their shifts + std::optional unshifted; // commitments and evaluations of unshifted polynomials + std::optional shifted; // commitments of to-be-shifted-by-1 polys, evals of their shifts + std::optional right_shifted_by_k; // commitments of to-be-right-shifted-by-k polys, evals of their shifts + + // Magnitude of the right-shift-by-k applied to `right_shifted_by_k` polys (assumed even). + size_t k_shift_magnitude = 0; Batch get_unshifted() { return (unshifted) ? *unshifted : Batch{}; } Batch get_shifted() { return (shifted) ? *shifted : Batch{}; } + Batch get_right_shifted_by_k() { return (right_shifted_by_k) ? *right_shifted_by_k : Batch{}; } Fr get_unshifted_batch_scalar() const { return unshifted ? unshifted->scalar : Fr{ 0 }; } /** * @brief Compute scalars used to batch each set of claims, excluding contribution from batching challenge \rho - * @details Computes scalars s_0, s_1 given by + * @details Computes scalars s_0, s_1, s_2 given by * \f[ * - s_0 = \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right) \f], * - s_1 = \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right) + * - s_2 = r^{k} \times \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right) * \f] * where the scalars used to batch the claims are given by * \f[ @@ -81,6 +87,11 @@ template struct ClaimBatcher_ { shifted->scalar = r_challenge.invert() * (inverse_vanishing_eval_pos - nu_challenge * inverse_vanishing_eval_neg); } + if (right_shifted_by_k) { + // r^k ⋅ (1/(z−r) + ν/(z+r)) + right_shifted_by_k->scalar = r_challenge.pow(static_cast(k_shift_magnitude)) * + (inverse_vanishing_eval_pos + nu_challenge * inverse_vanishing_eval_neg); + } } /** * @brief Append the commitments and scalars from each batch of claims to the Shplemini vectors which subsequently @@ -100,6 +111,7 @@ template struct ClaimBatcher_ { size_t num_powers = 0; num_powers += unshifted.has_value() ? unshifted->commitments.size() : 0; num_powers += shifted.has_value() ? shifted->commitments.size() : 0; + num_powers += right_shifted_by_k.has_value() ? right_shifted_by_k->commitments.size() : 0; Fr rho_power = Fr(1); size_t power_idx = 0; @@ -128,6 +140,11 @@ template struct ClaimBatcher_ { // i-th shifted commitments will be multiplied by ρ^{num_unshifted + i} and r⁻¹ ⋅ (1/(z−r) − ν/(z+r)) aggregate_claim_data_and_update_batched_evaluation(*shifted); } + if (right_shifted_by_k) { + // i-th right-shifted-by-k commitment will be multiplied by ρ^{num_unshifted + num_shifted + i} and + // r^k ⋅ (1/(z−r) + ν/(z+r)) + aggregate_claim_data_and_update_batched_evaluation(*right_shifted_by_k); + } BB_ASSERT_EQ(power_idx, num_powers); } diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp index 0cc9972c85c2..d0436f4f15b3 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp @@ -132,15 +132,19 @@ template class GeminiProver_ { Polynomial batched_unshifted; // linear combination of unshifted polynomials Polynomial batched_to_be_shifted_by_one; // linear combination of to-be-shifted polynomials + Polynomial batched_to_be_shifted_by_k; // linear combination of to-be-right-shifted-by-k polynomials // Batched tails: small polynomials covering only the tail region (e.g. last NUM_MASKED_ROWS positions). // Populated during compute_batched if tails are registered. Polynomial batched_unshifted_tail_; Polynomial batched_shifted_tail_; + size_t k_shift_magnitude = 0; // magnitude of right-shift-by-k (assumed even) + public: RefVector unshifted; // set of unshifted polynomials RefVector to_be_shifted_by_one; // set of polynomials to be left shifted by 1 + RefVector to_be_shifted_by_k; // set of polynomials to be right shifted by k // Tails: small polynomials (e.g. masking values) to be batched with the same rho scalar // as their corresponding base polynomial. Pairs of (index in unshifted/shifted list, tail poly). @@ -156,10 +160,18 @@ template class GeminiProver_ { bool has_unshifted() const { return unshifted.size() > 0; } bool has_to_be_shifted_by_one() const { return to_be_shifted_by_one.size() > 0; } + bool has_to_be_shifted_by_k() const { return to_be_shifted_by_k.size() > 0; } // Set references to the polynomials to be batched void set_unshifted(RefVector polynomials) { unshifted = polynomials; } void set_to_be_shifted_by_one(RefVector polynomials) { to_be_shifted_by_one = polynomials; } + void set_to_be_shifted_by_k(RefVector polynomials, const size_t shift_magnitude) + { + // k must be even for the +/- evaluation formulas to share a sign convention + BB_ASSERT_EQ(shift_magnitude % 2, static_cast(0)); + to_be_shifted_by_k = polynomials; + k_shift_magnitude = shift_magnitude; + } void add_unshifted_tail(size_t batcher_index, Polynomial&& tail) { @@ -236,6 +248,13 @@ template class GeminiProver_ { full_batched += batched_shifted_tail_.shifted(); } + if (has_to_be_shifted_by_k()) { + batched_to_be_shifted_by_k = + Polynomial(full_batched_size - k_shift_magnitude, full_batched_size, /*start_index=*/0); + batch(batched_to_be_shifted_by_k, to_be_shifted_by_k); + full_batched += batched_to_be_shifted_by_k.right_shifted(k_shift_magnitude); // A₀ += X^k * H + } + return full_batched; } @@ -258,8 +277,25 @@ template class GeminiProver_ { A_0_pos = std::move(A_0_extended); } + // The k-shift contribution lives on [0, full_batched_size - k); ensure the running + // accumulators cover that range before adding (mirrors the tail-extension pattern above). + if (has_to_be_shifted_by_k()) { + const size_t needed_end = full_batched_size - k_shift_magnitude; + if (A_0_pos.end_index() < needed_end) { + Polynomial A_0_extended(A_0_pos, needed_end - A_0_pos.start_index()); + A_0_pos = std::move(A_0_extended); + } + } + Polynomial A_0_neg = A_0_pos; + if (has_to_be_shifted_by_k()) { + const Fr r_pow_k = + r_challenge.pow(static_cast(k_shift_magnitude)); // r^k (k even ⇒ same sign at ±r) + A_0_pos.add_scaled(batched_to_be_shifted_by_k, r_pow_k); + A_0_neg.add_scaled(batched_to_be_shifted_by_k, r_pow_k); + } + Fr r_inv = r_challenge.invert(); if (has_to_be_shifted_by_one()) { A_0_pos.add_scaled(batched_to_be_shifted_by_one, r_inv); diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp index 987bed0343c8..de4d6da57d90 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -309,6 +309,34 @@ TEST_F(IPATest, ShpleminiIPAWithShift) EXPECT_EQ(result, true); } +// Shplemini + IPA. Verifies a flow with both shift-by-1 and right-shift-by-k claims. +TEST_F(IPATest, ShpleminiIPAWithShiftAndKShift) +{ + auto mle_opening_point = this->random_evaluation_point(log_n); + MockClaimGenerator mock_claims(n, + /*num_polynomials*/ 5, + /*num_to_be_shifted*/ 2, + mle_opening_point, + ck, + /*num_to_be_right_shifted_by_k*/ 2); + auto prover_transcript = NativeTranscript::test_prover_init_empty(); + + auto prover_opening_claims = + GeminiProver::prove(n, mock_claims.polynomial_batcher, mle_opening_point, ck, prover_transcript); + const auto opening_claim = ShplonkProver::prove(ck, prover_opening_claims, prover_transcript); + PCS::compute_opening_proof(ck, opening_claim, prover_transcript); + + auto verifier_transcript = NativeTranscript::test_verifier_init_empty(prover_transcript); + + const auto batch_opening_claim = + ShpleminiVerifier::compute_batch_opening_claim( + mock_claims.claim_batcher, mle_opening_point, vk.get_g1_identity(), verifier_transcript) + .batch_opening_claim; + + auto result = PCS::reduce_verify_batch_opening_claim(batch_opening_claim, vk, verifier_transcript); + + EXPECT_EQ(result, true); +} // Test `ShpleminiVerifier::remove_shifted_commitments`. Four polynomials, two of which are shifted. TEST_F(IPATest, ShpleminiIPAShiftsRemoval) { diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp index 53518cd4a51c..98ca678ce4ef 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp @@ -225,6 +225,40 @@ TEST_F(KZGTest, ShpleminiKzgWithShift) EXPECT_EQ(pairing_points.check(), true); } +// Verifies a Shplemini → KZG flow with both shift-by-1 and right-shift-by-k claims. +TEST_F(KZGTest, ShpleminiKzgWithShiftAndKShift) +{ + std::vector mle_opening_point = random_evaluation_point(log_n); + + MockClaimGenerator mock_claims(n, + /*num_polynomials*/ 5, + /*num_to_be_shifted*/ 2, + mle_opening_point, + ck, + /*num_to_be_right_shifted_by_k*/ 2); + + auto prover_transcript = NativeTranscript::test_prover_init_empty(); + + auto prover_opening_claims = + GeminiProver::prove(n, mock_claims.polynomial_batcher, mle_opening_point, ck, prover_transcript); + + const auto opening_claim = ShplonkProver::prove(ck, prover_opening_claims, prover_transcript); + + PCS::compute_opening_proof(ck, opening_claim, prover_transcript); + + auto verifier_transcript = NativeTranscript::test_verifier_init_empty(prover_transcript); + + auto batch_opening_claim = + ShpleminiVerifier::compute_batch_opening_claim( + mock_claims.claim_batcher, mle_opening_point, vk.get_g1_identity(), verifier_transcript) + .batch_opening_claim; + + const auto pairing_points = + PCS::reduce_verify_batch_opening_claim(std::move(batch_opening_claim), verifier_transcript); + + EXPECT_EQ(pairing_points.check(), true); +} + TEST_F(KZGTest, ShpleminiKzgWithShiftAndInterleaving) { std::vector mle_opening_point = random_evaluation_point(log_n); // sometimes denoted 'u' diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/mock_witness_generator.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/mock_witness_generator.hpp index 37101b69e0ce..3918f165d0dd 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/mock_witness_generator.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/mock_witness_generator.hpp @@ -34,12 +34,16 @@ template struct MockClaimGenerator { ClaimData unshifted; ClaimData to_be_shifted; + ClaimData to_be_right_shifted_by_k; std::vector const_size_mle_opening_point; PolynomialBatcher polynomial_batcher; ClaimBatcher claim_batcher; + // Mock right-shift magnitude (must be even). + static constexpr size_t k_magnitude = 6; + // Containers for mock Sumcheck data std::vector> round_univariates; std::vector sumcheck_commitments; @@ -60,7 +64,8 @@ template struct MockClaimGenerator { const size_t num_polynomials, const size_t num_to_be_shifted, const std::vector& mle_opening_point, - const CommitmentKey& commitment_key) + const CommitmentKey& commitment_key, + const size_t num_to_be_right_shifted_by_k = 0) : ck(commitment_key) // Initialize the commitment key , polynomial_batcher(poly_size) @@ -81,8 +86,9 @@ template struct MockClaimGenerator { challenge = std::span(mle_opening_point); } - BB_ASSERT_GTE(num_polynomials, num_to_be_shifted); - const size_t num_not_to_be_shifted = num_polynomials - num_to_be_shifted; + const size_t total_num_to_be_shifted = num_to_be_shifted + num_to_be_right_shifted_by_k; + BB_ASSERT_GTE(num_polynomials, total_num_to_be_shifted); + const size_t num_not_to_be_shifted = num_polynomials - total_num_to_be_shifted; Fr ebz_factor = 1; @@ -111,13 +117,36 @@ template struct MockClaimGenerator { unshifted.polys.push_back(std::move(poly)); } + // Construct claim data for polynomials that are to-be-right-shifted-by-k. The base poly's data range is + // [0, poly_size - k_magnitude); its right-shift-by-k therefore lives on [k_magnitude, poly_size). + for (size_t idx = 0; idx < num_to_be_right_shifted_by_k; idx++) { + Polynomial poly = Polynomial::random(poly_size - k_magnitude, poly_size, 0); + Commitment commitment = ck.commit(poly); + to_be_right_shifted_by_k.commitments.push_back(commitment); + to_be_right_shifted_by_k.evals.push_back(poly.right_shifted(k_magnitude).evaluate_mle(challenge) * + ebz_factor); + to_be_right_shifted_by_k.polys.push_back(poly.share()); + // Populate the unshifted counterpart in the unshifted claims + unshifted.commitments.push_back(commitment); + unshifted.evals.push_back(poly.evaluate_mle(challenge) * ebz_factor); + unshifted.polys.push_back(std::move(poly)); + } + polynomial_batcher.set_unshifted(RefVector(unshifted.polys)); polynomial_batcher.set_to_be_shifted_by_one(RefVector(to_be_shifted.polys)); + if (num_to_be_right_shifted_by_k > 0) { + polynomial_batcher.set_to_be_shifted_by_k(RefVector(to_be_right_shifted_by_k.polys), k_magnitude); + } claim_batcher = ClaimBatcher{ .unshifted = ClaimBatch{ RefVector(unshifted.commitments), RefVector(unshifted.evals) }, .shifted = ClaimBatch{ RefVector(to_be_shifted.commitments), RefVector(to_be_shifted.evals) } }; + if (num_to_be_right_shifted_by_k > 0) { + claim_batcher.right_shifted_by_k = ClaimBatch{ RefVector(to_be_right_shifted_by_k.commitments), + RefVector(to_be_right_shifted_by_k.evals) }; + claim_batcher.k_shift_magnitude = k_magnitude; + } } // Generate zero polynomials to test edge cases in PCS diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp index edb44a863612..9e17e3c4a42c 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp @@ -332,6 +332,17 @@ template Polynomial Polynomial::shifted() const return result; } +template Polynomial Polynomial::right_shifted(const size_t magnitude) const +{ + // The last `magnitude` virtual coefficients must be zero so the shift fits within `virtual_size()`. + BB_ASSERT_LTE(coefficients_.end_ + magnitude, virtual_size()); + Polynomial result; + result.coefficients_ = coefficients_; + result.coefficients_.start_ += magnitude; + result.coefficients_.end_ += magnitude; + return result; +} + template Polynomial Polynomial::reverse() const { const size_t end_index = this->end_index(); diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp index 7ac33b963303..119c4b5050a5 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp @@ -195,6 +195,12 @@ template class Polynomial { */ Polynomial shifted() const; + /** + * @brief Returns a Polynomial equal to the right-shift-by-magnitude of self. + * @note Resulting Polynomial shares the memory of that used to generate it. + */ + Polynomial right_shifted(size_t magnitude) const; + /** * @brief Returns the polynomial equal to the reverse of self * diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.test.cpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.test.cpp index 2f2e45eba77e..527e85966ad1 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.test.cpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.test.cpp @@ -29,6 +29,34 @@ TEST(Polynomial, Shifted) } } +// Simple test/demonstration of right_shifted functionality +TEST(Polynomial, RightShifted) +{ + using FF = bb::fr; + using Polynomial = bb::Polynomial; + const size_t SIZE = 10; + const size_t VIRTUAL_SIZE = 20; + const size_t START_IDX = 2; + const size_t SHIFT_MAGNITUDE = 5; + auto poly = Polynomial::random(SIZE, VIRTUAL_SIZE, START_IDX); + + auto poly_shifted = poly.right_shifted(SHIFT_MAGNITUDE); + + EXPECT_EQ(poly_shifted.size(), poly.size()); + EXPECT_EQ(poly_shifted.virtual_size(), poly.virtual_size()); + + // The shift is indeed the shift + for (size_t i = 0; i < SIZE; ++i) { + EXPECT_EQ(poly_shifted.get(i + SHIFT_MAGNITUDE), poly.get(i)); + } + + // The shifted view shares memory with the original + poly.at(3) = 25; + for (size_t i = 0; i < SIZE; ++i) { + EXPECT_EQ(poly_shifted.get(i + SHIFT_MAGNITUDE), poly.get(i)); + } +} + // Simple test/demonstration of reverse functionality TEST(Polynomial, Reversed) { From 3186d3866a6802457085d326167669a8289f783f Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Wed, 6 May 2026 10:07:58 +0000 Subject: [PATCH 02/17] add md --- .../FINAL_APPEND_MERGE_IN_TRANSLATOR.md | 265 ++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 barretenberg/cpp/src/barretenberg/goblin/FINAL_APPEND_MERGE_IN_TRANSLATOR.md diff --git a/barretenberg/cpp/src/barretenberg/goblin/FINAL_APPEND_MERGE_IN_TRANSLATOR.md b/barretenberg/cpp/src/barretenberg/goblin/FINAL_APPEND_MERGE_IN_TRANSLATOR.md new file mode 100644 index 000000000000..6cb382bd762f --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/goblin/FINAL_APPEND_MERGE_IN_TRANSLATOR.md @@ -0,0 +1,265 @@ +# Final APPEND Merge in Translator + +## Status + +**Explored, not pursued.** The cost/benefit ratio is unfavourable; the +final APPEND merge in [`MERGE_PROTOCOL.md`](./MERGE_PROTOCOL.md) is kept. +The analysis below is preserved as a record of the design space. + +### Why we stopped + +The headline savings — drop the final merge, save 4 $[M_j]$ commitments, +$[G]$, the Shplonk merge batch, and ~13 field-element evaluations — are +real but modest. The integration cost grows in three places that did not +appear in the initial sketch: + +1. **Two k values in the PCS, not one.** Translator's decomposition + relation reads each op-queue wire both unshifted and shift-by-1 in the + same row (the `(x_lo, y_hi)` / `(x_hi, z_1)` / `(y_lo, z_2)` pair + pattern). Extending that pattern to a right-shifted hiding wire + requires opening $t_{\text{hiding}}$ at *two* k values, $\ell$ and + $\ell-1$. The Shplemini k-shift batch we restored on the parent branch + supports a single $k$; multi-k is a non-trivial extension. Sidestepping + it requires either (a) a separate `[t_hiding-shift-ℓ]` commitment plus + a univariate consistency check (4 extra commitments per merge), or + (b) reshuffling Translator's witness layout so the shift-by-1 pattern + on op-queue wires goes away — a cross-cutting change to limb storage + and range-constraint pairings. + +2. **ZK-budget bleed-through onto Mega.** Each new opening of + $[t_{\text{hiding}}]$ at the Translator-side multilinear point + $u'$ is an extra observable that Mega's ecc_op_wire masking has to + absorb (the polynomial is committed once by the hiding kernel and + serves both protocols). With multi-k the hiding kernel needs ~2 more + masked rows than today and the soundness analysis in + `MERGE_PROTOCOL.md` (full-rank over + $\{\kappa, \kappa^{-1}, u, u', z\}$) has to be redone over a + different observable set. The "consistency check" alternative + separates the budgets but at the cost of those 4 extra commitments. + +3. **Translator becomes mode-dependent.** Two Translator VKs are needed, + distinguished by the precomputed `lagrange_prev` polynomial (Chonk vs + AVM modes). On its own this is clean — `lagrange_prev` already encodes + the mode without an additional selector — but it adds a generation + path, a VK pinning entry, and a verifier dispatch surface to maintain. + +Each item is tractable in isolation. The aggregate, against the modest +end-state savings, is not worth it. + +### What we'd resurrect this for + +The conclusion is sensitive to assumptions. Reasons to revisit: + +- The k-shift Shplemini PCS gains a multi-k extension for an unrelated + reason, eliminating cost item (1). +- Mega's ecc_op masking is restructured for an unrelated reason such + that extra observables on $[t_{\text{hiding}}]$ are free, eliminating + cost item (2). +- Final-merge cost grows materially (e.g. larger op-queue, more wires), + shifting the savings side of the ledger. + +Until then, intermediate PREPEND merges and the final APPEND merge in +`MERGE_PROTOCOL.md` are the canonical design. + +--- + +The remainder of this document captures the proposal as it stood when +the cost/benefit analysis was made, for reference. + +## Original Status + +Proposal. Supersedes the final-step APPEND merge in +[`MERGE_PROTOCOL.md`](./MERGE_PROTOCOL.md). Intermediate PREPEND merges are +untouched. + +## Motivation + +At the final Goblin step, Merge (APPEND) proves +$M_j = T_{\text{prev},j} + X^{\ell} \cdot t_{\text{hiding},j}$ with +$\deg(T_{\text{prev},j}) < \ell$, then hands Translator a commitment +$[X^t M_j]$. + +Two observations make the final merge unnecessary: + +1. **Translator already does the work.** Contribs 65/66/67 of + `TranslatorZeroConstraintsRelationImpl` pin $w_j$ to zero outside the + minicircuit. That bounds $\deg(w_j)$ (Thakur's $G$ is redundant). A + symmetric low-side zero-pin replaces the merge's leading-zero check on + $[L']$ and closes the MegaZK rows-0..3 gap in one relation. +2. **Concatenation is addition when supports are disjoint.** If + $T_{\text{prev},j}$ and $X^\ell t_{\text{hiding},j}$ have disjoint + non-zero supports, then $M_j = T_{\text{prev},j} + X^\ell t_{\text{hiding},j}$ + holds as a polynomial identity — and $[M_j]$ as a plain EC addition. + +Removing the final merge saves 4 $[M_j]$ commitments, $[G]$, the Shplonk +batch for merge, and ~13 field-element evaluations. + +## Design + +### Option A (MegaZK trace reshuffle) — too invasive + +Reshuffle the hiding kernel's Mega trace so `ecc_op` starts at row $\ell+s$ +instead of $s$. Supports become disjoint at the commitment level: +$[X^s M_j] = [X^s T_{\text{prev},j}] + [t_{\text{hiding},j}]$, a plain EC +add. Requires unifying Translator to `RANDOMNESS_START = 5`, a +hiding-kernel trace-layout variant, and touches databus / public-input +positions / trace-overflow logic. + +### Option B (Shplemini k-shifts) + +Keep all Mega trace layouts. $T_{\text{prev},j}$ and $t_{\text{hiding},j}$ +both have data starting at row $s=5$, supports overlap. Resolve via: + +**Shplemini right-shift-by-$k$ opening.** The PCS batcher produces an +opening claim for $X^k \cdot P$ from a single commitment $[P]$ via a +claim-side adjustment — no second commitment. Originally PR #11663, removed +unused by PR #18741, resurrected here. With $k = \ell$, Shplemini delivers +$(X^\ell \cdot t_{\text{hiding},j})(u')$ from $[t_{\text{hiding},j}]$. + +**Split flavor entities.** Translator's op-queue wires become two families: + +``` +OpQueueWires_prev: op_prev, x_lo_y_hi_prev, x_hi_z_1_prev, y_lo_z_2_prev +OpQueueWires_hiding: op_hiding, x_lo_y_hi_hiding, x_hi_z_1_hiding, y_lo_z_2_hiding +``` + +The three to-be-shifted wires in each family have shift-by-1 mirrors (6 +`_shift` entities total); `op` stays non-shiftable in both (2 entities +total). `_prev` commitments come from tail-kernel public inputs, `_hiding` +from hiding-kernel witness commitments. + +**Virtual op-queue wire.** Translator's existing relations are rewritten +wherever they read `x_lo_y_hi / x_hi_z_1 / y_lo_z_2 / op`: + +$$\text{op}_j \;\longrightarrow\; T_{\text{prev},j} + t_{\text{hiding},j\text{-shift-}\ell}$$ + +$$\text{op}_j^{\text{-shift-1}} \;\longrightarrow\; T_{\text{prev},j\text{-shift-1}} + t_{\text{hiding},j\text{-shift-}(\ell+1)}$$ + +Because $T_{\text{prev},j}$ has data only on $[s, s+\ell)$ and +$X^\ell t_{\text{hiding},j}$ only on $[s+\ell, \ldots)$, supports are +disjoint — no selector, no relation-degree increase. + +### Complete relation-side change list + +1. Substitute op-queue reads per the formulas above. Mechanical. +2. Contribs 65/66/67 continue to enforce $w_j = 0$ outside the minicircuit, + evaluated on the sum. Subsumes the degree bound on $T_{\text{prev}}$. +3. **New subrelation:** + `lagrange_prev · t_{hiding,j-shift-ℓ} = 0`, pinning the shifted + $t_{\text{hiding}}$ to zero on $[s, s+\ell)$. Simultaneously: + - enforces the disjoint-support requirement directly in Translator; + - pins rows 0..4 of unshifted $t_{\text{hiding}}$ (which land at rows + $[\ell, \ell+4]$ of the shifted poly, inside $[s, s+\ell)$ since $s=5$), + closing the MegaZK rows-0..3 gap and replacing `lagrange_hiding_boundary`; + - reuses the already-emitted shifted opening — no unshifted + $t_{\text{hiding}}$ opening is required anywhere. + +$T_{\text{prev}}$'s leading zeros need no extra relation: non-ZK Mega's +`EccOpQueueRelation` pins them, carried structurally through the +intermediate-merge chain. + +### Precise value of $\ell$ + +$\ell$ is a single compile-time constant = **max cumulative prev-table +size** across every Chonk configuration. It plays three roles: k-shift +amount, upper bound of $T_{\text{prev}}$'s data range, and width of +`lagrange_prev`. The only coupling to the MegaZK fix is $\ell \geq s = 5$, +trivially satisfied. + +Row accounting: after right-shift-by-$\ell$, `lagrange_prev` ∩ (non-zero +region of shifted poly) = $[\ell, s+\ell)$, corresponding to rows $[0, s)$ +of unshifted $t_{\text{hiding}}$. That pins rows 0..4. Row 4 is already +zero (pinned by `EccOpQueueRelation` since it's in the main sumcheck loop +and `lagrange_ecc_op(4) = 0`); the extra pin is a harmless no-op. Rows +0..3 are the MegaZK-unconstrained ones that actually need it. + +### Defensive $T_{\text{prev}}$ subrelation + +$$T_{\text{prev},j} \cdot (1 - \texttt{lagrange\_prev}) = 0$$ + +Degree-1, negligible cost, reuses the already-emitted $T_{\text{prev},j}(u')$ +opening. Redundant — cumulative $\deg(T_{\text{prev}}) \leq \ell$ follows +by induction on the PREPEND chain from each intermediate merge's Thakur $G$ +check — but cheap enough to include so the bound is a direct Translator +subrelation rather than an inductive argument over the intermediate merges. + +## Cost + +- Sumcheck relation degree: unchanged. +- 4 extra openings per op-queue wire at $u'$. +- Resurrect Shplemini k-shift batcher support (#11663). +- One flavor file, one relations file, one PCS batcher file. Mega untouched. + +## Interaction with AVM + +Chonk and AVM share one Translator implementation; the two modes differ +only in the precomputed `lagrange_prev` polynomial (and therefore in the +Translator VK). + +- **Chonk mode.** Both incoming subtables are non-empty. `lagrange_prev` + is the indicator of $[s, s+\ell)$ — the rows where $T_{\text{prev}}$ + carries data and where the right-shifted $t_{\text{hiding}}$ must vanish. +- **AVM mode.** `_prev` carries AVM's full ecc_op; the `_hiding` family is + absent (no commitments and no openings are sent for it). `lagrange_prev` + is the indicator of AVM's full ecc_op data range, so + $(1-\texttt{lagrange\_prev})$ vanishes there and the defensive + $T_{\text{prev}} \cdot (1-\texttt{lagrange\_prev}) = 0$ subrelation is + trivially satisfied. The new + $\texttt{lagrange\_prev} \cdot t_{\text{hiding-shift-}\ell} = 0$ + subrelation is vacuous because $t_{\text{hiding}} \equiv 0$. + +The op-queue substitution +$\text{op}_j \to T_{\text{prev},j} + t_{\text{hiding},j\text{-shift-}\ell}$ +is structural — no extra mode selector is needed. In AVM mode the second +summand drops out automatically. + +Consequence: `MegaAvmFlavor` keeps `TRACE_OFFSET = 1`. The +`APPEND_OUTPUT_SHIFT` and the $\kappa^{s-t}$ correction disappear entirely. + +## Implementation Plan + +1. **Resurrect Shplemini k-shifts.** Restore right-shift-by-$k$ support in + `PolynomialBatcher` / `ClaimBatcher` removed by PR #18741. +2. **Translator flavor.** Split op-queue entities into `_prev` / `_hiding` + families with shift-by-1 mirrors on the three shiftable wires. Flag + `_hiding` entities as requiring a k-shift claim with $k = \ell$. Update + entity counts / getters. +3. **Translator relations.** Substitute op-queue reads (item 1 in the + relation list). Add `lagrange_prev · t_{hiding-shift-ℓ} = 0` and (optional) + $T_{\text{prev}} \cdot (1-\texttt{lagrange\_prev}) = 0$. +4. **Translator prover/verifier.** Populate `_prev` and `_hiding` + polynomials from the op queue's two sub-tables; emit/consume both sets of + evaluations at $u'$; register the k-shift claim against Shplemini. +5. **Goblin.** Drop `prove_merge(APPEND)` and the corresponding + `recursively_verify_merge(APPEND)` in the hiding kernel. +6. **Chonk plumbing.** Hand $[X^s T_{\text{prev},j}]$ and + $[t_{\text{hiding},j}]$ to Translator directly; remove the $[X^t M_j]$ + consumer. +7. **AVM Translator VK.** Generate a separate Translator VK whose + `lagrange_prev` is the indicator of AVM's ecc_op data range; in this + mode the verifier consumes only the `_prev` family. +8. **Soundness.** Rewrite the "Hiding kernel ZK soundness" and ZK-budget + sections of `MERGE_PROTOCOL.md` for the new observable set. + + +## Open Questions + +- **$\ell$ constexpr.** Confirm $\ell_{\max}$ is compile-time fixed across + every Chonk configuration (init/intermediate/tail + all app counts). + Load-bearing. +- **ZK argument rewrite.** `MERGE_PROTOCOL.md`'s full-rank analysis is over + $\{\kappa, \kappa^{-1}, u, u', z\}$; the new observable set drops + $\kappa, \kappa^{-1}, [G]$, merge Shplonk quotient, and adds one Shplemini + k-shift opening per op-queue wire. Likely strictly easier (fewer + observables over the same 40 randomness coefficients), but needs a redo. +- **Effect on ECCVM.** None expected. The ECCVM–Translator consistency + check reconciles ECCVM's op-queue view with Translator's via openings at + a shared challenge, not via a shared commitment. In the new design + Translator's op-queue-wire opening at $z$ is + $T_{\text{prev}}(z) + z^\ell \cdot t_{\text{hiding}}(z)$, computed from + openings already in the transcript. ECCVM sees no change. +- **Translator ZK masking.** Contribs 65/66/67 multiply by + `in_minicircuit_or_masked`. The new `lagrange_prev · t_{hiding-shift-ℓ}` + subrelation must use the same factor (or be provably unaffected by + masked-row values), otherwise a masked row at $i \in [\ell, \ell+4)$ could + satisfy it with garbage. From 5ac2a27496e3c89287412c487127cff3517062a1 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Wed, 6 May 2026 10:16:15 +0000 Subject: [PATCH 03/17] rm md --- .../FINAL_APPEND_MERGE_IN_TRANSLATOR.md | 265 ------------------ 1 file changed, 265 deletions(-) delete mode 100644 barretenberg/cpp/src/barretenberg/goblin/FINAL_APPEND_MERGE_IN_TRANSLATOR.md diff --git a/barretenberg/cpp/src/barretenberg/goblin/FINAL_APPEND_MERGE_IN_TRANSLATOR.md b/barretenberg/cpp/src/barretenberg/goblin/FINAL_APPEND_MERGE_IN_TRANSLATOR.md deleted file mode 100644 index 6cb382bd762f..000000000000 --- a/barretenberg/cpp/src/barretenberg/goblin/FINAL_APPEND_MERGE_IN_TRANSLATOR.md +++ /dev/null @@ -1,265 +0,0 @@ -# Final APPEND Merge in Translator - -## Status - -**Explored, not pursued.** The cost/benefit ratio is unfavourable; the -final APPEND merge in [`MERGE_PROTOCOL.md`](./MERGE_PROTOCOL.md) is kept. -The analysis below is preserved as a record of the design space. - -### Why we stopped - -The headline savings — drop the final merge, save 4 $[M_j]$ commitments, -$[G]$, the Shplonk merge batch, and ~13 field-element evaluations — are -real but modest. The integration cost grows in three places that did not -appear in the initial sketch: - -1. **Two k values in the PCS, not one.** Translator's decomposition - relation reads each op-queue wire both unshifted and shift-by-1 in the - same row (the `(x_lo, y_hi)` / `(x_hi, z_1)` / `(y_lo, z_2)` pair - pattern). Extending that pattern to a right-shifted hiding wire - requires opening $t_{\text{hiding}}$ at *two* k values, $\ell$ and - $\ell-1$. The Shplemini k-shift batch we restored on the parent branch - supports a single $k$; multi-k is a non-trivial extension. Sidestepping - it requires either (a) a separate `[t_hiding-shift-ℓ]` commitment plus - a univariate consistency check (4 extra commitments per merge), or - (b) reshuffling Translator's witness layout so the shift-by-1 pattern - on op-queue wires goes away — a cross-cutting change to limb storage - and range-constraint pairings. - -2. **ZK-budget bleed-through onto Mega.** Each new opening of - $[t_{\text{hiding}}]$ at the Translator-side multilinear point - $u'$ is an extra observable that Mega's ecc_op_wire masking has to - absorb (the polynomial is committed once by the hiding kernel and - serves both protocols). With multi-k the hiding kernel needs ~2 more - masked rows than today and the soundness analysis in - `MERGE_PROTOCOL.md` (full-rank over - $\{\kappa, \kappa^{-1}, u, u', z\}$) has to be redone over a - different observable set. The "consistency check" alternative - separates the budgets but at the cost of those 4 extra commitments. - -3. **Translator becomes mode-dependent.** Two Translator VKs are needed, - distinguished by the precomputed `lagrange_prev` polynomial (Chonk vs - AVM modes). On its own this is clean — `lagrange_prev` already encodes - the mode without an additional selector — but it adds a generation - path, a VK pinning entry, and a verifier dispatch surface to maintain. - -Each item is tractable in isolation. The aggregate, against the modest -end-state savings, is not worth it. - -### What we'd resurrect this for - -The conclusion is sensitive to assumptions. Reasons to revisit: - -- The k-shift Shplemini PCS gains a multi-k extension for an unrelated - reason, eliminating cost item (1). -- Mega's ecc_op masking is restructured for an unrelated reason such - that extra observables on $[t_{\text{hiding}}]$ are free, eliminating - cost item (2). -- Final-merge cost grows materially (e.g. larger op-queue, more wires), - shifting the savings side of the ledger. - -Until then, intermediate PREPEND merges and the final APPEND merge in -`MERGE_PROTOCOL.md` are the canonical design. - ---- - -The remainder of this document captures the proposal as it stood when -the cost/benefit analysis was made, for reference. - -## Original Status - -Proposal. Supersedes the final-step APPEND merge in -[`MERGE_PROTOCOL.md`](./MERGE_PROTOCOL.md). Intermediate PREPEND merges are -untouched. - -## Motivation - -At the final Goblin step, Merge (APPEND) proves -$M_j = T_{\text{prev},j} + X^{\ell} \cdot t_{\text{hiding},j}$ with -$\deg(T_{\text{prev},j}) < \ell$, then hands Translator a commitment -$[X^t M_j]$. - -Two observations make the final merge unnecessary: - -1. **Translator already does the work.** Contribs 65/66/67 of - `TranslatorZeroConstraintsRelationImpl` pin $w_j$ to zero outside the - minicircuit. That bounds $\deg(w_j)$ (Thakur's $G$ is redundant). A - symmetric low-side zero-pin replaces the merge's leading-zero check on - $[L']$ and closes the MegaZK rows-0..3 gap in one relation. -2. **Concatenation is addition when supports are disjoint.** If - $T_{\text{prev},j}$ and $X^\ell t_{\text{hiding},j}$ have disjoint - non-zero supports, then $M_j = T_{\text{prev},j} + X^\ell t_{\text{hiding},j}$ - holds as a polynomial identity — and $[M_j]$ as a plain EC addition. - -Removing the final merge saves 4 $[M_j]$ commitments, $[G]$, the Shplonk -batch for merge, and ~13 field-element evaluations. - -## Design - -### Option A (MegaZK trace reshuffle) — too invasive - -Reshuffle the hiding kernel's Mega trace so `ecc_op` starts at row $\ell+s$ -instead of $s$. Supports become disjoint at the commitment level: -$[X^s M_j] = [X^s T_{\text{prev},j}] + [t_{\text{hiding},j}]$, a plain EC -add. Requires unifying Translator to `RANDOMNESS_START = 5`, a -hiding-kernel trace-layout variant, and touches databus / public-input -positions / trace-overflow logic. - -### Option B (Shplemini k-shifts) - -Keep all Mega trace layouts. $T_{\text{prev},j}$ and $t_{\text{hiding},j}$ -both have data starting at row $s=5$, supports overlap. Resolve via: - -**Shplemini right-shift-by-$k$ opening.** The PCS batcher produces an -opening claim for $X^k \cdot P$ from a single commitment $[P]$ via a -claim-side adjustment — no second commitment. Originally PR #11663, removed -unused by PR #18741, resurrected here. With $k = \ell$, Shplemini delivers -$(X^\ell \cdot t_{\text{hiding},j})(u')$ from $[t_{\text{hiding},j}]$. - -**Split flavor entities.** Translator's op-queue wires become two families: - -``` -OpQueueWires_prev: op_prev, x_lo_y_hi_prev, x_hi_z_1_prev, y_lo_z_2_prev -OpQueueWires_hiding: op_hiding, x_lo_y_hi_hiding, x_hi_z_1_hiding, y_lo_z_2_hiding -``` - -The three to-be-shifted wires in each family have shift-by-1 mirrors (6 -`_shift` entities total); `op` stays non-shiftable in both (2 entities -total). `_prev` commitments come from tail-kernel public inputs, `_hiding` -from hiding-kernel witness commitments. - -**Virtual op-queue wire.** Translator's existing relations are rewritten -wherever they read `x_lo_y_hi / x_hi_z_1 / y_lo_z_2 / op`: - -$$\text{op}_j \;\longrightarrow\; T_{\text{prev},j} + t_{\text{hiding},j\text{-shift-}\ell}$$ - -$$\text{op}_j^{\text{-shift-1}} \;\longrightarrow\; T_{\text{prev},j\text{-shift-1}} + t_{\text{hiding},j\text{-shift-}(\ell+1)}$$ - -Because $T_{\text{prev},j}$ has data only on $[s, s+\ell)$ and -$X^\ell t_{\text{hiding},j}$ only on $[s+\ell, \ldots)$, supports are -disjoint — no selector, no relation-degree increase. - -### Complete relation-side change list - -1. Substitute op-queue reads per the formulas above. Mechanical. -2. Contribs 65/66/67 continue to enforce $w_j = 0$ outside the minicircuit, - evaluated on the sum. Subsumes the degree bound on $T_{\text{prev}}$. -3. **New subrelation:** - `lagrange_prev · t_{hiding,j-shift-ℓ} = 0`, pinning the shifted - $t_{\text{hiding}}$ to zero on $[s, s+\ell)$. Simultaneously: - - enforces the disjoint-support requirement directly in Translator; - - pins rows 0..4 of unshifted $t_{\text{hiding}}$ (which land at rows - $[\ell, \ell+4]$ of the shifted poly, inside $[s, s+\ell)$ since $s=5$), - closing the MegaZK rows-0..3 gap and replacing `lagrange_hiding_boundary`; - - reuses the already-emitted shifted opening — no unshifted - $t_{\text{hiding}}$ opening is required anywhere. - -$T_{\text{prev}}$'s leading zeros need no extra relation: non-ZK Mega's -`EccOpQueueRelation` pins them, carried structurally through the -intermediate-merge chain. - -### Precise value of $\ell$ - -$\ell$ is a single compile-time constant = **max cumulative prev-table -size** across every Chonk configuration. It plays three roles: k-shift -amount, upper bound of $T_{\text{prev}}$'s data range, and width of -`lagrange_prev`. The only coupling to the MegaZK fix is $\ell \geq s = 5$, -trivially satisfied. - -Row accounting: after right-shift-by-$\ell$, `lagrange_prev` ∩ (non-zero -region of shifted poly) = $[\ell, s+\ell)$, corresponding to rows $[0, s)$ -of unshifted $t_{\text{hiding}}$. That pins rows 0..4. Row 4 is already -zero (pinned by `EccOpQueueRelation` since it's in the main sumcheck loop -and `lagrange_ecc_op(4) = 0`); the extra pin is a harmless no-op. Rows -0..3 are the MegaZK-unconstrained ones that actually need it. - -### Defensive $T_{\text{prev}}$ subrelation - -$$T_{\text{prev},j} \cdot (1 - \texttt{lagrange\_prev}) = 0$$ - -Degree-1, negligible cost, reuses the already-emitted $T_{\text{prev},j}(u')$ -opening. Redundant — cumulative $\deg(T_{\text{prev}}) \leq \ell$ follows -by induction on the PREPEND chain from each intermediate merge's Thakur $G$ -check — but cheap enough to include so the bound is a direct Translator -subrelation rather than an inductive argument over the intermediate merges. - -## Cost - -- Sumcheck relation degree: unchanged. -- 4 extra openings per op-queue wire at $u'$. -- Resurrect Shplemini k-shift batcher support (#11663). -- One flavor file, one relations file, one PCS batcher file. Mega untouched. - -## Interaction with AVM - -Chonk and AVM share one Translator implementation; the two modes differ -only in the precomputed `lagrange_prev` polynomial (and therefore in the -Translator VK). - -- **Chonk mode.** Both incoming subtables are non-empty. `lagrange_prev` - is the indicator of $[s, s+\ell)$ — the rows where $T_{\text{prev}}$ - carries data and where the right-shifted $t_{\text{hiding}}$ must vanish. -- **AVM mode.** `_prev` carries AVM's full ecc_op; the `_hiding` family is - absent (no commitments and no openings are sent for it). `lagrange_prev` - is the indicator of AVM's full ecc_op data range, so - $(1-\texttt{lagrange\_prev})$ vanishes there and the defensive - $T_{\text{prev}} \cdot (1-\texttt{lagrange\_prev}) = 0$ subrelation is - trivially satisfied. The new - $\texttt{lagrange\_prev} \cdot t_{\text{hiding-shift-}\ell} = 0$ - subrelation is vacuous because $t_{\text{hiding}} \equiv 0$. - -The op-queue substitution -$\text{op}_j \to T_{\text{prev},j} + t_{\text{hiding},j\text{-shift-}\ell}$ -is structural — no extra mode selector is needed. In AVM mode the second -summand drops out automatically. - -Consequence: `MegaAvmFlavor` keeps `TRACE_OFFSET = 1`. The -`APPEND_OUTPUT_SHIFT` and the $\kappa^{s-t}$ correction disappear entirely. - -## Implementation Plan - -1. **Resurrect Shplemini k-shifts.** Restore right-shift-by-$k$ support in - `PolynomialBatcher` / `ClaimBatcher` removed by PR #18741. -2. **Translator flavor.** Split op-queue entities into `_prev` / `_hiding` - families with shift-by-1 mirrors on the three shiftable wires. Flag - `_hiding` entities as requiring a k-shift claim with $k = \ell$. Update - entity counts / getters. -3. **Translator relations.** Substitute op-queue reads (item 1 in the - relation list). Add `lagrange_prev · t_{hiding-shift-ℓ} = 0` and (optional) - $T_{\text{prev}} \cdot (1-\texttt{lagrange\_prev}) = 0$. -4. **Translator prover/verifier.** Populate `_prev` and `_hiding` - polynomials from the op queue's two sub-tables; emit/consume both sets of - evaluations at $u'$; register the k-shift claim against Shplemini. -5. **Goblin.** Drop `prove_merge(APPEND)` and the corresponding - `recursively_verify_merge(APPEND)` in the hiding kernel. -6. **Chonk plumbing.** Hand $[X^s T_{\text{prev},j}]$ and - $[t_{\text{hiding},j}]$ to Translator directly; remove the $[X^t M_j]$ - consumer. -7. **AVM Translator VK.** Generate a separate Translator VK whose - `lagrange_prev` is the indicator of AVM's ecc_op data range; in this - mode the verifier consumes only the `_prev` family. -8. **Soundness.** Rewrite the "Hiding kernel ZK soundness" and ZK-budget - sections of `MERGE_PROTOCOL.md` for the new observable set. - - -## Open Questions - -- **$\ell$ constexpr.** Confirm $\ell_{\max}$ is compile-time fixed across - every Chonk configuration (init/intermediate/tail + all app counts). - Load-bearing. -- **ZK argument rewrite.** `MERGE_PROTOCOL.md`'s full-rank analysis is over - $\{\kappa, \kappa^{-1}, u, u', z\}$; the new observable set drops - $\kappa, \kappa^{-1}, [G]$, merge Shplonk quotient, and adds one Shplemini - k-shift opening per op-queue wire. Likely strictly easier (fewer - observables over the same 40 randomness coefficients), but needs a redo. -- **Effect on ECCVM.** None expected. The ECCVM–Translator consistency - check reconciles ECCVM's op-queue view with Translator's via openings at - a shared challenge, not via a shared commitment. In the new design - Translator's op-queue-wire opening at $z$ is - $T_{\text{prev}}(z) + z^\ell \cdot t_{\text{hiding}}(z)$, computed from - openings already in the transcript. ECCVM sees no change. -- **Translator ZK masking.** Contribs 65/66/67 multiply by - `in_minicircuit_or_masked`. The new `lagrange_prev · t_{hiding-shift-ℓ}` - subrelation must use the same factor (or be provably unaffected by - masked-row values), otherwise a masked row at $i \in [\ell, \ell+4)$ could - satisfy it with garbage. From 6e416dcdea9b1a35957442d6658e0381f65969c4 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Wed, 6 May 2026 10:17:33 +0000 Subject: [PATCH 04/17] Revert "k shifts resurrected" This reverts commit 337d3bb97e4d044b42f953c23b8ba63c30ff09e6. --- .../commitment_schemes/claim_batcher.hpp | 23 ++---------- .../commitment_schemes/gemini/gemini.hpp | 36 ------------------- .../commitment_schemes/ipa/ipa.test.cpp | 28 --------------- .../commitment_schemes/kzg/kzg.test.cpp | 34 ------------------ .../utils/mock_witness_generator.hpp | 35 ++---------------- .../barretenberg/polynomials/polynomial.cpp | 11 ------ .../barretenberg/polynomials/polynomial.hpp | 6 ---- .../polynomials/polynomial.test.cpp | 28 --------------- 8 files changed, 6 insertions(+), 195 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp index b724ceeb81b7..4f7e3370c2da 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp @@ -34,26 +34,20 @@ template struct ClaimBatcher_ { Fr scalar = 0; }; - std::optional unshifted; // commitments and evaluations of unshifted polynomials - std::optional shifted; // commitments of to-be-shifted-by-1 polys, evals of their shifts - std::optional right_shifted_by_k; // commitments of to-be-right-shifted-by-k polys, evals of their shifts - - // Magnitude of the right-shift-by-k applied to `right_shifted_by_k` polys (assumed even). - size_t k_shift_magnitude = 0; + std::optional unshifted; // commitments and evaluations of unshifted polynomials + std::optional shifted; // commitments of to-be-shifted-by-1 polys, evals of their shifts Batch get_unshifted() { return (unshifted) ? *unshifted : Batch{}; } Batch get_shifted() { return (shifted) ? *shifted : Batch{}; } - Batch get_right_shifted_by_k() { return (right_shifted_by_k) ? *right_shifted_by_k : Batch{}; } Fr get_unshifted_batch_scalar() const { return unshifted ? unshifted->scalar : Fr{ 0 }; } /** * @brief Compute scalars used to batch each set of claims, excluding contribution from batching challenge \rho - * @details Computes scalars s_0, s_1, s_2 given by + * @details Computes scalars s_0, s_1 given by * \f[ * - s_0 = \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right) \f], * - s_1 = \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right) - * - s_2 = r^{k} \times \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right) * \f] * where the scalars used to batch the claims are given by * \f[ @@ -87,11 +81,6 @@ template struct ClaimBatcher_ { shifted->scalar = r_challenge.invert() * (inverse_vanishing_eval_pos - nu_challenge * inverse_vanishing_eval_neg); } - if (right_shifted_by_k) { - // r^k ⋅ (1/(z−r) + ν/(z+r)) - right_shifted_by_k->scalar = r_challenge.pow(static_cast(k_shift_magnitude)) * - (inverse_vanishing_eval_pos + nu_challenge * inverse_vanishing_eval_neg); - } } /** * @brief Append the commitments and scalars from each batch of claims to the Shplemini vectors which subsequently @@ -111,7 +100,6 @@ template struct ClaimBatcher_ { size_t num_powers = 0; num_powers += unshifted.has_value() ? unshifted->commitments.size() : 0; num_powers += shifted.has_value() ? shifted->commitments.size() : 0; - num_powers += right_shifted_by_k.has_value() ? right_shifted_by_k->commitments.size() : 0; Fr rho_power = Fr(1); size_t power_idx = 0; @@ -140,11 +128,6 @@ template struct ClaimBatcher_ { // i-th shifted commitments will be multiplied by ρ^{num_unshifted + i} and r⁻¹ ⋅ (1/(z−r) − ν/(z+r)) aggregate_claim_data_and_update_batched_evaluation(*shifted); } - if (right_shifted_by_k) { - // i-th right-shifted-by-k commitment will be multiplied by ρ^{num_unshifted + num_shifted + i} and - // r^k ⋅ (1/(z−r) + ν/(z+r)) - aggregate_claim_data_and_update_batched_evaluation(*right_shifted_by_k); - } BB_ASSERT_EQ(power_idx, num_powers); } diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp index d0436f4f15b3..0cc9972c85c2 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp @@ -132,19 +132,15 @@ template class GeminiProver_ { Polynomial batched_unshifted; // linear combination of unshifted polynomials Polynomial batched_to_be_shifted_by_one; // linear combination of to-be-shifted polynomials - Polynomial batched_to_be_shifted_by_k; // linear combination of to-be-right-shifted-by-k polynomials // Batched tails: small polynomials covering only the tail region (e.g. last NUM_MASKED_ROWS positions). // Populated during compute_batched if tails are registered. Polynomial batched_unshifted_tail_; Polynomial batched_shifted_tail_; - size_t k_shift_magnitude = 0; // magnitude of right-shift-by-k (assumed even) - public: RefVector unshifted; // set of unshifted polynomials RefVector to_be_shifted_by_one; // set of polynomials to be left shifted by 1 - RefVector to_be_shifted_by_k; // set of polynomials to be right shifted by k // Tails: small polynomials (e.g. masking values) to be batched with the same rho scalar // as their corresponding base polynomial. Pairs of (index in unshifted/shifted list, tail poly). @@ -160,18 +156,10 @@ template class GeminiProver_ { bool has_unshifted() const { return unshifted.size() > 0; } bool has_to_be_shifted_by_one() const { return to_be_shifted_by_one.size() > 0; } - bool has_to_be_shifted_by_k() const { return to_be_shifted_by_k.size() > 0; } // Set references to the polynomials to be batched void set_unshifted(RefVector polynomials) { unshifted = polynomials; } void set_to_be_shifted_by_one(RefVector polynomials) { to_be_shifted_by_one = polynomials; } - void set_to_be_shifted_by_k(RefVector polynomials, const size_t shift_magnitude) - { - // k must be even for the +/- evaluation formulas to share a sign convention - BB_ASSERT_EQ(shift_magnitude % 2, static_cast(0)); - to_be_shifted_by_k = polynomials; - k_shift_magnitude = shift_magnitude; - } void add_unshifted_tail(size_t batcher_index, Polynomial&& tail) { @@ -248,13 +236,6 @@ template class GeminiProver_ { full_batched += batched_shifted_tail_.shifted(); } - if (has_to_be_shifted_by_k()) { - batched_to_be_shifted_by_k = - Polynomial(full_batched_size - k_shift_magnitude, full_batched_size, /*start_index=*/0); - batch(batched_to_be_shifted_by_k, to_be_shifted_by_k); - full_batched += batched_to_be_shifted_by_k.right_shifted(k_shift_magnitude); // A₀ += X^k * H - } - return full_batched; } @@ -277,25 +258,8 @@ template class GeminiProver_ { A_0_pos = std::move(A_0_extended); } - // The k-shift contribution lives on [0, full_batched_size - k); ensure the running - // accumulators cover that range before adding (mirrors the tail-extension pattern above). - if (has_to_be_shifted_by_k()) { - const size_t needed_end = full_batched_size - k_shift_magnitude; - if (A_0_pos.end_index() < needed_end) { - Polynomial A_0_extended(A_0_pos, needed_end - A_0_pos.start_index()); - A_0_pos = std::move(A_0_extended); - } - } - Polynomial A_0_neg = A_0_pos; - if (has_to_be_shifted_by_k()) { - const Fr r_pow_k = - r_challenge.pow(static_cast(k_shift_magnitude)); // r^k (k even ⇒ same sign at ±r) - A_0_pos.add_scaled(batched_to_be_shifted_by_k, r_pow_k); - A_0_neg.add_scaled(batched_to_be_shifted_by_k, r_pow_k); - } - Fr r_inv = r_challenge.invert(); if (has_to_be_shifted_by_one()) { A_0_pos.add_scaled(batched_to_be_shifted_by_one, r_inv); diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp index de4d6da57d90..987bed0343c8 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -309,34 +309,6 @@ TEST_F(IPATest, ShpleminiIPAWithShift) EXPECT_EQ(result, true); } -// Shplemini + IPA. Verifies a flow with both shift-by-1 and right-shift-by-k claims. -TEST_F(IPATest, ShpleminiIPAWithShiftAndKShift) -{ - auto mle_opening_point = this->random_evaluation_point(log_n); - MockClaimGenerator mock_claims(n, - /*num_polynomials*/ 5, - /*num_to_be_shifted*/ 2, - mle_opening_point, - ck, - /*num_to_be_right_shifted_by_k*/ 2); - auto prover_transcript = NativeTranscript::test_prover_init_empty(); - - auto prover_opening_claims = - GeminiProver::prove(n, mock_claims.polynomial_batcher, mle_opening_point, ck, prover_transcript); - const auto opening_claim = ShplonkProver::prove(ck, prover_opening_claims, prover_transcript); - PCS::compute_opening_proof(ck, opening_claim, prover_transcript); - - auto verifier_transcript = NativeTranscript::test_verifier_init_empty(prover_transcript); - - const auto batch_opening_claim = - ShpleminiVerifier::compute_batch_opening_claim( - mock_claims.claim_batcher, mle_opening_point, vk.get_g1_identity(), verifier_transcript) - .batch_opening_claim; - - auto result = PCS::reduce_verify_batch_opening_claim(batch_opening_claim, vk, verifier_transcript); - - EXPECT_EQ(result, true); -} // Test `ShpleminiVerifier::remove_shifted_commitments`. Four polynomials, two of which are shifted. TEST_F(IPATest, ShpleminiIPAShiftsRemoval) { diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp index 98ca678ce4ef..53518cd4a51c 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp @@ -225,40 +225,6 @@ TEST_F(KZGTest, ShpleminiKzgWithShift) EXPECT_EQ(pairing_points.check(), true); } -// Verifies a Shplemini → KZG flow with both shift-by-1 and right-shift-by-k claims. -TEST_F(KZGTest, ShpleminiKzgWithShiftAndKShift) -{ - std::vector mle_opening_point = random_evaluation_point(log_n); - - MockClaimGenerator mock_claims(n, - /*num_polynomials*/ 5, - /*num_to_be_shifted*/ 2, - mle_opening_point, - ck, - /*num_to_be_right_shifted_by_k*/ 2); - - auto prover_transcript = NativeTranscript::test_prover_init_empty(); - - auto prover_opening_claims = - GeminiProver::prove(n, mock_claims.polynomial_batcher, mle_opening_point, ck, prover_transcript); - - const auto opening_claim = ShplonkProver::prove(ck, prover_opening_claims, prover_transcript); - - PCS::compute_opening_proof(ck, opening_claim, prover_transcript); - - auto verifier_transcript = NativeTranscript::test_verifier_init_empty(prover_transcript); - - auto batch_opening_claim = - ShpleminiVerifier::compute_batch_opening_claim( - mock_claims.claim_batcher, mle_opening_point, vk.get_g1_identity(), verifier_transcript) - .batch_opening_claim; - - const auto pairing_points = - PCS::reduce_verify_batch_opening_claim(std::move(batch_opening_claim), verifier_transcript); - - EXPECT_EQ(pairing_points.check(), true); -} - TEST_F(KZGTest, ShpleminiKzgWithShiftAndInterleaving) { std::vector mle_opening_point = random_evaluation_point(log_n); // sometimes denoted 'u' diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/mock_witness_generator.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/mock_witness_generator.hpp index 3918f165d0dd..37101b69e0ce 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/mock_witness_generator.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/mock_witness_generator.hpp @@ -34,16 +34,12 @@ template struct MockClaimGenerator { ClaimData unshifted; ClaimData to_be_shifted; - ClaimData to_be_right_shifted_by_k; std::vector const_size_mle_opening_point; PolynomialBatcher polynomial_batcher; ClaimBatcher claim_batcher; - // Mock right-shift magnitude (must be even). - static constexpr size_t k_magnitude = 6; - // Containers for mock Sumcheck data std::vector> round_univariates; std::vector sumcheck_commitments; @@ -64,8 +60,7 @@ template struct MockClaimGenerator { const size_t num_polynomials, const size_t num_to_be_shifted, const std::vector& mle_opening_point, - const CommitmentKey& commitment_key, - const size_t num_to_be_right_shifted_by_k = 0) + const CommitmentKey& commitment_key) : ck(commitment_key) // Initialize the commitment key , polynomial_batcher(poly_size) @@ -86,9 +81,8 @@ template struct MockClaimGenerator { challenge = std::span(mle_opening_point); } - const size_t total_num_to_be_shifted = num_to_be_shifted + num_to_be_right_shifted_by_k; - BB_ASSERT_GTE(num_polynomials, total_num_to_be_shifted); - const size_t num_not_to_be_shifted = num_polynomials - total_num_to_be_shifted; + BB_ASSERT_GTE(num_polynomials, num_to_be_shifted); + const size_t num_not_to_be_shifted = num_polynomials - num_to_be_shifted; Fr ebz_factor = 1; @@ -117,36 +111,13 @@ template struct MockClaimGenerator { unshifted.polys.push_back(std::move(poly)); } - // Construct claim data for polynomials that are to-be-right-shifted-by-k. The base poly's data range is - // [0, poly_size - k_magnitude); its right-shift-by-k therefore lives on [k_magnitude, poly_size). - for (size_t idx = 0; idx < num_to_be_right_shifted_by_k; idx++) { - Polynomial poly = Polynomial::random(poly_size - k_magnitude, poly_size, 0); - Commitment commitment = ck.commit(poly); - to_be_right_shifted_by_k.commitments.push_back(commitment); - to_be_right_shifted_by_k.evals.push_back(poly.right_shifted(k_magnitude).evaluate_mle(challenge) * - ebz_factor); - to_be_right_shifted_by_k.polys.push_back(poly.share()); - // Populate the unshifted counterpart in the unshifted claims - unshifted.commitments.push_back(commitment); - unshifted.evals.push_back(poly.evaluate_mle(challenge) * ebz_factor); - unshifted.polys.push_back(std::move(poly)); - } - polynomial_batcher.set_unshifted(RefVector(unshifted.polys)); polynomial_batcher.set_to_be_shifted_by_one(RefVector(to_be_shifted.polys)); - if (num_to_be_right_shifted_by_k > 0) { - polynomial_batcher.set_to_be_shifted_by_k(RefVector(to_be_right_shifted_by_k.polys), k_magnitude); - } claim_batcher = ClaimBatcher{ .unshifted = ClaimBatch{ RefVector(unshifted.commitments), RefVector(unshifted.evals) }, .shifted = ClaimBatch{ RefVector(to_be_shifted.commitments), RefVector(to_be_shifted.evals) } }; - if (num_to_be_right_shifted_by_k > 0) { - claim_batcher.right_shifted_by_k = ClaimBatch{ RefVector(to_be_right_shifted_by_k.commitments), - RefVector(to_be_right_shifted_by_k.evals) }; - claim_batcher.k_shift_magnitude = k_magnitude; - } } // Generate zero polynomials to test edge cases in PCS diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp index 9e17e3c4a42c..edb44a863612 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp @@ -332,17 +332,6 @@ template Polynomial Polynomial::shifted() const return result; } -template Polynomial Polynomial::right_shifted(const size_t magnitude) const -{ - // The last `magnitude` virtual coefficients must be zero so the shift fits within `virtual_size()`. - BB_ASSERT_LTE(coefficients_.end_ + magnitude, virtual_size()); - Polynomial result; - result.coefficients_ = coefficients_; - result.coefficients_.start_ += magnitude; - result.coefficients_.end_ += magnitude; - return result; -} - template Polynomial Polynomial::reverse() const { const size_t end_index = this->end_index(); diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp index 119c4b5050a5..7ac33b963303 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp @@ -195,12 +195,6 @@ template class Polynomial { */ Polynomial shifted() const; - /** - * @brief Returns a Polynomial equal to the right-shift-by-magnitude of self. - * @note Resulting Polynomial shares the memory of that used to generate it. - */ - Polynomial right_shifted(size_t magnitude) const; - /** * @brief Returns the polynomial equal to the reverse of self * diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.test.cpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.test.cpp index 527e85966ad1..2f2e45eba77e 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.test.cpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.test.cpp @@ -29,34 +29,6 @@ TEST(Polynomial, Shifted) } } -// Simple test/demonstration of right_shifted functionality -TEST(Polynomial, RightShifted) -{ - using FF = bb::fr; - using Polynomial = bb::Polynomial; - const size_t SIZE = 10; - const size_t VIRTUAL_SIZE = 20; - const size_t START_IDX = 2; - const size_t SHIFT_MAGNITUDE = 5; - auto poly = Polynomial::random(SIZE, VIRTUAL_SIZE, START_IDX); - - auto poly_shifted = poly.right_shifted(SHIFT_MAGNITUDE); - - EXPECT_EQ(poly_shifted.size(), poly.size()); - EXPECT_EQ(poly_shifted.virtual_size(), poly.virtual_size()); - - // The shift is indeed the shift - for (size_t i = 0; i < SIZE; ++i) { - EXPECT_EQ(poly_shifted.get(i + SHIFT_MAGNITUDE), poly.get(i)); - } - - // The shifted view shares memory with the original - poly.at(3) = 25; - for (size_t i = 0; i < SIZE; ++i) { - EXPECT_EQ(poly_shifted.get(i + SHIFT_MAGNITUDE), poly.get(i)); - } -} - // Simple test/demonstration of reverse functionality TEST(Polynomial, Reversed) { From b6c0534b480124be6162a29f8c36080ac0059109 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Wed, 6 May 2026 10:33:17 +0000 Subject: [PATCH 05/17] add missing wires --- .../dsl/acir_format/gate_count_constants.hpp | 2 +- .../translator_vm/translator_flavor.hpp | 37 ++++++++++++------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp index 2544496e44f1..4fecdbb5ecdc 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp @@ -113,7 +113,7 @@ constexpr std::tuple HONK_RECURSION_CONSTANTS( // ======================================== // Gate count for Chonk recursive verification (Ultra with RollupIO) -inline constexpr size_t CHONK_RECURSION_GATES = 1474677; +inline constexpr size_t CHONK_RECURSION_GATES = 1474692; // ======================================== // Hypernova Recursion Constants diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp index 1511e72ba132..19859cd96f8d 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp @@ -586,7 +586,12 @@ class TranslatorFlavor { /** * @brief All unshifted polynomials for PCS (excludes computable precomputed, includes concatenated). - * @details masking(1) + ordered_extra(1) + op(1) + ordered(5) + z_perm(1) + concat(5) = 14 + * @details masking(1) + ordered_extra(1) + op(1) + op_queue_tbs(3) + ordered(5) + z_perm(1) + concat(5) = 17 + * + * The op-queue to-be-shifted wires (x_lo_y_hi, x_hi_z_1, y_lo_z_2) appear here in addition to + * get_pcs_to_be_shifted because the decomposition relation reads them both unshifted (e.g. `x_lo`) + * and shift-by-1 (e.g. `y_hi`) at the same row. Without registering the unshifted opening, the + * unshifted MLE evaluations at the sumcheck point would be unconstrained. */ auto get_pcs_unshifted() { @@ -594,9 +599,10 @@ class TranslatorFlavor { MaskingEntities::get_all(), // gemini_masking_poly RefArray{ this->ordered_extra_range_constraints_numerator }, // non-computable precomputed WireNonshiftedEntities::get_all(), // op (from merge protocol) - OrderedRangeConstraints::get_all(), // ordered_0..4 - DerivedWitnessEntities::get_all(), // z_perm - ConcatenatedPolynomials::get_all()); // concat_0..4 + OpQueueWiresToBeShiftedEntities::get_all(), // x_lo_y_hi, x_hi_z_1, y_lo_z_2 + OrderedRangeConstraints::get_all(), // ordered_0..4 + DerivedWitnessEntities::get_all(), // z_perm + ConcatenatedPolynomials::get_all()); // concat_0..4 } /** @@ -711,9 +717,11 @@ class TranslatorFlavor { "Range constraint wires must fill exactly 4 concatenation groups"); // PCS batch sizes - static constexpr size_t NUM_UNSHIFTED_WITNESSES_WITHOUT_CONCATENATED = WireNonshiftedEntities::_members_size + - OrderedRangeConstraints::_members_size + - DerivedWitnessEntities::_members_size; + // Note: op-queue to-be-shifted wires (x_lo_y_hi, x_hi_z_1, y_lo_z_2) are registered in BOTH the + // unshifted and shifted PCS batches because the decomposition relation reads them in both forms. + static constexpr size_t NUM_UNSHIFTED_WITNESSES_WITHOUT_CONCATENATED = + WireNonshiftedEntities::_members_size + OpQueueWiresToBeShiftedEntities::_members_size + + OrderedRangeConstraints::_members_size + DerivedWitnessEntities::_members_size; static constexpr size_t NUM_TO_BE_SHIFTED = OpQueueWiresToBeShiftedEntities::_members_size + OrderedRangeConstraints::_members_size + DerivedWitnessEntities::_members_size; @@ -731,17 +739,20 @@ class TranslatorFlavor { // A container to be fed to ShpleminiVerifier to avoid redundant scalar muls. // Identifies commitments that appear in both the unshifted and shifted batches: - // Unshifted batch: masking(1) + ordered_extra(1) + op(1) + ordered(5) + z_perm(1) + concat(5) = 14 + // Unshifted batch: masking(1) + ordered_extra(1) + op(1) + op_queue_tbs(3) + ordered(5) + z_perm(1) + concat(5) + // = 17 // Shifted batch: op_queue(3) + ordered(5) + z_perm(1) + concat(5) = 14 - // Range 1: ordered(5) + z_perm(1) — stored indices 2..7 (unshifted) ↔ 16..21 (shifted) - // Range 2: concatenated(5) — stored indices 8..12 (unshifted) ↔ 22..26 (shifted) + // Range 1: op_queue_tbs(3) + ordered(5) + z_perm(1) = 9 (contiguous in both batches) + // stored indices 2..10 (unshifted) ↔ 16..24 (shifted) + // Range 2: concatenated(5) — stored indices 11..15 (unshifted) ↔ 25..29 (shifted) // (Stored indices are 0-based after ZK offset; offset=2 accounts for Q_commitment + gemini_masking_poly) + static constexpr size_t NUM_OP_QUEUE_TO_BE_SHIFTED = OpQueueWiresToBeShiftedEntities::_members_size; static constexpr RepeatedCommitmentsData REPEATED_COMMITMENTS = RepeatedCommitmentsData(2, 2 + NUM_PCS_TO_BE_SHIFTED, - NUM_ORDERED_RANGE + 1, - 2 + NUM_ORDERED_RANGE + 1, - 2 + NUM_PCS_TO_BE_SHIFTED + NUM_ORDERED_RANGE + 1, + NUM_OP_QUEUE_TO_BE_SHIFTED + NUM_ORDERED_RANGE + 1, + 2 + NUM_OP_QUEUE_TO_BE_SHIFTED + NUM_ORDERED_RANGE + 1, + 2 + NUM_PCS_TO_BE_SHIFTED + NUM_OP_QUEUE_TO_BE_SHIFTED + NUM_ORDERED_RANGE + 1, NUM_CONCATENATED_POLYS); static constexpr size_t PROOF_LENGTH = From 274155af365084f64b83cf8113a1bd8b3ab589cc Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Wed, 6 May 2026 15:06:44 +0000 Subject: [PATCH 06/17] macros for translator entities --- .../batched_honk_translator_verifier.cpp | 4 +- .../translator_circuit_checker.cpp | 5 +- .../cpp/src/barretenberg/flavor/flavor.hpp | 35 +- .../translator_recursive_flavor.hpp | 12 +- .../translator_vm/translator.test.cpp | 42 +- .../translator_vm/translator_fixed_vk.hpp | 19 + .../translator_vm/translator_flavor.cpp | 284 ++++ .../translator_vm/translator_flavor.hpp | 1214 ++++++----------- .../translator_vm/translator_selectors.hpp | 4 +- .../translator_vm/translator_verifier.cpp | 9 +- 10 files changed, 794 insertions(+), 834 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.cpp diff --git a/barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_verifier.cpp b/barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_verifier.cpp index cc727a5d0aae..dcae727d639a 100644 --- a/barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_verifier.cpp @@ -243,8 +243,8 @@ typename BatchedHonkTranslatorVerifier_::ReductionResult BatchedHonkTrans }(); // Translator claim components (translator first: its masking poly must be at position 0 for Shplemini offset=2). - auto concat_shift_evals = TranslatorFlavor::reconstruct_concatenated_evaluations( - trans_evals.get_groups_to_be_concatenated_shifted(), std::span(joint_challenge)); + auto concat_shift_evals = TransFlavor::template reconstruct_concatenated_evaluations( + trans_evals, std::span(joint_challenge)); RefVector joint_unshifted_comms = trans_commitments.get_pcs_unshifted(); RefVector joint_unshifted_evals = trans_evals.get_pcs_unshifted(); diff --git a/barretenberg/cpp/src/barretenberg/circuit_checker/translator_circuit_checker.cpp b/barretenberg/cpp/src/barretenberg/circuit_checker/translator_circuit_checker.cpp index e6433f598aec..6790c6f45c34 100644 --- a/barretenberg/cpp/src/barretenberg/circuit_checker/translator_circuit_checker.cpp +++ b/barretenberg/cpp/src/barretenberg/circuit_checker/translator_circuit_checker.cpp @@ -65,12 +65,11 @@ void TranslatorCircuitChecker::populate_values(const Builder& circuit, AllValues // ----------------------------------------------------------------------- // Shifted wire values (= values at next row, or zero at boundary). - // ShiftedEntities::get_all() returns 86 refs: + // get_shifted() returns 86 refs: // [0..79] : shifts of circuit.wires[1..80] (x_lo_y_hi through last range constraint) // [80..85] : shifts of ordered range constraints (0..4) and z_perm (not from circuit wires) // ----------------------------------------------------------------------- - auto& shifted_base = static_cast&>(values); - auto shifted_refs = shifted_base.get_all(); + auto shifted_refs = values.get_shifted(); if (row_idx + 1 < num_gates) { // circuit.wires[j+1] holds the wire that shifts to shifted_refs[j] for (size_t j = 0; j < 80; j++) { diff --git a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp index c8fa282fe79f..7545f8cbe23f 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp @@ -95,7 +95,7 @@ template struct Precomput * * @tparam PrecomputedCommitments The precomputed entities containing VK commitments * @tparam HashType The field type for the precomputed hash (e.g., fr for both ECCVM and Translator) - * @tparam HardcodedVKAndHash Class containing static vk_hash() and get_all() methods with hardcoded values + * @tparam HardcodedVKAndHash Class containing static vk_hash() and either populate() or get_all() with hardcoded values */ template class FixedVKAndHash_ : public PrecomputedCommitments { @@ -104,8 +104,17 @@ class FixedVKAndHash_ : public PrecomputedCommitments { bool operator==(const FixedVKAndHash_&) const = default; - // Default construct the fixed VK from hardcoded commitments and precomputed hash + // Default construct the fixed VK from a hardcoded commitment populator and precomputed hash. FixedVKAndHash_() + requires requires(PrecomputedCommitments& commitments) { HardcodedVKAndHash::populate(commitments); } + : hash(HardcodedVKAndHash::vk_hash()) + { + HardcodedVKAndHash::populate(static_cast(*this)); + } + + // Default construct the fixed VK from hardcoded commitment vectors and precomputed hash. + FixedVKAndHash_() + requires(!requires(PrecomputedCommitments& commitments) { HardcodedVKAndHash::populate(commitments); }) : hash(HardcodedVKAndHash::vk_hash()) { for (auto [vk_commitment, fixed_commitment] : zip_view(this->get_all(), HardcodedVKAndHash::get_all())) { @@ -113,6 +122,15 @@ class FixedVKAndHash_ : public PrecomputedCommitments { } } + template + void populate_stdlib(Builder* builder, StdlibPrecomputedCommitments& commitments) const + requires requires(const PrecomputedCommitments& native_commitments) { + HardcodedVKAndHash::populate_stdlib(builder, commitments, native_commitments); + } + { + HardcodedVKAndHash::populate_stdlib(builder, commitments, static_cast(*this)); + } + HashType get_hash() const { return hash; } private: @@ -340,6 +358,19 @@ class FixedStdlibVKAndHash_ : public PrecomputedCommitments { * @brief Construct from native verification key and fix all witnesses (VK is constant for fixed circuits) */ FixedStdlibVKAndHash_(Builder* builder, const std::shared_ptr& native_key) + requires requires(const NativeVerificationKey& native_key, PrecomputedCommitments& commitments) { + native_key.populate_stdlib(builder, commitments); + } + : hash(FF::from_witness(builder, native_key->get_hash())) + { + native_key->populate_stdlib(builder, static_cast(*this)); + hash.fix_witness(); + } + + FixedStdlibVKAndHash_(Builder* builder, const std::shared_ptr& native_key) + requires(!requires(const NativeVerificationKey& native_key, PrecomputedCommitments& commitments) { + native_key.populate_stdlib(builder, commitments); + }) : hash(FF::from_witness(builder, native_key->get_hash())) { for (auto [native_comm, comm] : zip_view(native_key->get_all(), this->get_all())) { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp index d567dab9e988..e83d18bfa1b5 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp @@ -75,6 +75,13 @@ class TranslatorRecursiveFlavor { NativeFlavor::complete_full_circuit_evaluations(evals, full_circuit, challenge); } + template + static std::array reconstruct_concatenated_evaluations( + NativeFlavor::AllEntities& evals, std::span challenge) + { + return NativeFlavor::template reconstruct_concatenated_evaluations(evals, challenge); + } + using Relations = TranslatorFlavor::Relations_; static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); @@ -104,11 +111,10 @@ class TranslatorRecursiveFlavor { /** * @brief A container for the witness commitments. */ - using WitnessCommitments = TranslatorFlavor::WitnessEntities; + using WitnessCommitments = TranslatorFlavor::AllEntities; using CommitmentLabels = TranslatorFlavor::CommitmentLabels; - // Reuse the VerifierCommitments from Translator - using VerifierCommitments = TranslatorFlavor::VerifierCommitments_; + using VerifierCommitments = TranslatorFlavor::AllEntities; using Transcript = UltraStdlibTranscript; using VKAndHash = VKAndHash_; diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator.test.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator.test.cpp index 6187dd7706b1..b0e1b25cf330 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator.test.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator.test.cpp @@ -352,18 +352,15 @@ TEST_F(TranslatorTests, FixedVK) auto proving_key = std::make_shared(circuit_builder); TranslatorProver prover{ proving_key, prover_transcript }; TranslatorFlavor::VerificationKey computed_vk = create_vk_from_proving_key(proving_key->proving_key); - auto labels = TranslatorFlavor::VerificationKey::get_labels(); - - size_t index = 0; - for (auto [vk_commitment, fixed_commitment] : zip_view(computed_vk.get_all(), fixed_vk.get_all())) { - if (vk_commitment != fixed_commitment) { - info("// ", labels[index]); - info("Commitment(uint256_t(\"0x", vk_commitment.x, "\"),"); - info(" uint256_t(\"0x", vk_commitment.y, "\")),"); - } - EXPECT_EQ(vk_commitment, fixed_commitment) << "Mismatch at label: " << labels[index]; - ++index; + + const auto& vk_commitment = computed_vk.ordered_extra_range_constraints_numerator; + const auto& fixed_commitment = fixed_vk.ordered_extra_range_constraints_numerator; + if (vk_commitment != fixed_commitment) { + info("// ordered_extra_range_constraints_numerator"); + info("Commitment(uint256_t(\"0x", vk_commitment.x, "\"),"); + info(" uint256_t(\"0x", vk_commitment.y, "\")),"); } + EXPECT_EQ(vk_commitment, fixed_commitment) << "Mismatch at label: ordered_extra_range_constraints_numerator"; EXPECT_EQ(computed_vk, fixed_vk); }; @@ -512,6 +509,29 @@ TEST_F(TranslatorTests, EvaluationPartition) EXPECT_EQ(remaining, 0UL); } +TEST_F(TranslatorTests, OpQueueSplitWiresAreOpenedUnshiftedAndShifted) +{ + using Flavor = TranslatorFlavor; + using FF = Flavor::FF; + + Flavor::AllEntities evals; + + std::set pcs_unshifted; + for (auto& entity : evals.get_pcs_unshifted()) { + pcs_unshifted.insert(&entity); + } + + std::set pcs_shift_sources; + for (auto& entity : evals.get_pcs_to_be_shifted()) { + pcs_shift_sources.insert(&entity); + } + + for (auto* op_queue_split_wire : { &evals.x_lo_y_hi, &evals.x_hi_z_1, &evals.y_lo_z_2 }) { + EXPECT_TRUE(pcs_unshifted.contains(op_queue_split_wire)); + EXPECT_TRUE(pcs_shift_sources.contains(op_queue_split_wire)); + } +} + /** * @brief Verify that the verifier-side methods populate every entity in AllEntities. * @details Start from all-zeros, call set_minicircuit_evaluations + complete_full_circuit_evaluations diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_fixed_vk.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_fixed_vk.hpp index fa0aec7aea66..ee07330a9c35 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_fixed_vk.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_fixed_vk.hpp @@ -6,6 +6,7 @@ #pragma once #include "barretenberg/common/ref_vector.hpp" +#include "barretenberg/common/zip_view.hpp" #include "barretenberg/ecc/curves/bn254/bn254.hpp" namespace bb { @@ -34,6 +35,24 @@ struct TranslatorHardcodedVKAndHash { uint256_t("0x14149055853422bf016065386e8ea0ffb9425b454048e1cd14cfdca457aa7e17")), }; } + + template static void populate(VKEntities& vk) + { + const auto values = get_all(); + size_t i = 0; + for (auto& field : vk.get_all()) { + field = values[i++]; + } + } + + template + static void populate_stdlib(Builder* builder, StdlibVKEntities& stdlib_vk, const NativeVKEntities& native_vk) + { + for (auto [stdlib_field, native_field] : zip_view(stdlib_vk.get_all(), native_vk.get_all())) { + stdlib_field = StdlibVKEntities::DataType::from_witness(builder, native_field); + stdlib_field.fix_witness(); + } + } }; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.cpp new file mode 100644 index 000000000000..4f349cf16094 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.cpp @@ -0,0 +1,284 @@ +#include "barretenberg/translator_vm/translator_flavor.hpp" + +#include "barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp" + +namespace bb { + +template +void TranslatorFlavor::compute_computable_precomputed(AllEntities& evals, std::span challenge) +{ + TranslatorSelectorEvaluations::compute(challenge).populate(evals); +} + +template +void TranslatorFlavor::set_minicircuit_evaluations(AllEntities& evals, + const std::array& mid) +{ + size_t src = 0; + for (auto& wire : evals.get_minicircuit_wires()) { + wire = mid[src++]; + } + for (auto& wire : evals.get_minicircuit_wires_shifted()) { + wire = mid[src++]; + } +} + +template +void TranslatorFlavor::complete_claimed_evaluations(AllEntities& evals, std::span challenge) +{ + compute_computable_precomputed(evals, challenge); + + FFType l0 = FFType(1); + for (size_t i = 0; i < CONST_TRANSLATOR_LOG_N - LOG_MINI_CIRCUIT_SIZE; i++) { + l0 *= (FFType(1) - challenge[LOG_MINI_CIRCUIT_SIZE + i]); + } + for (auto& wire : evals.get_minicircuit_wires()) { + wire *= l0; + } + for (auto& wire : evals.get_minicircuit_wires_shifted()) { + wire *= l0; + } +} + +template +void TranslatorFlavor::complete_full_circuit_evaluations( + AllEntities& evals, + const std::array& full_circuit, + std::span challenge) +{ + set_full_circuit_evaluations(evals, full_circuit); + complete_claimed_evaluations(evals, challenge); + + auto concat_evals = reconstruct_concatenated_evaluations(evals, challenge); + for (auto [ref, eval] : zip_view(evals.get_concatenated(), concat_evals)) { + ref = eval; + } +} + +template +std::array TranslatorFlavor::reconstruct_concatenated_evaluations( + AllEntities& evals, std::span challenge) +{ + static constexpr size_t NUM_TOP_BITS = numeric::get_msb(CONCATENATION_GROUP_SIZE); + + std::array lagrange_basis; + for (size_t j = 0; j < CONCATENATION_GROUP_SIZE; j++) { + lagrange_basis[j] = FFType(1); + for (size_t bit = 0; bit < NUM_TOP_BITS; bit++) { + const FFType& u = challenge[CONST_TRANSLATOR_LOG_N - NUM_TOP_BITS + bit]; + lagrange_basis[j] *= ((j >> bit) & 1) ? u : (FFType(1) - u); + } + } + FFType padding_inv = lagrange_basis[0].invert(); + + // Walk CONCAT_MAP: each row produces one concat-poly evaluation by accumulating its chunk + // wires against the lagrange basis. Zero-padded slots (non-range chunk has 13 wires < CONCATENATION_GROUP_SIZE) + // contribute zero and are simply not emitted. + std::array result; + size_t group_idx = 0; +#define ACCUMULATE_REF(name) \ + if constexpr (Shifted) { \ + acc += lagrange_basis[j] * evals.name##_shift; \ + } else { \ + acc += lagrange_basis[j] * evals.name; \ + } \ + ++j; +#define RECONSTRUCT_GROUP(concat_name, group_macro) \ + { \ + FFType acc(0); \ + size_t j = 0; \ + group_macro(ACCUMULATE_REF) result[group_idx++] = acc * padding_inv; \ + } + CONCAT_MAP(RECONSTRUCT_GROUP) +#undef ACCUMULATE_REF +#undef RECONSTRUCT_GROUP + return result; +} + +template +std::array TranslatorFlavor::get_full_circuit_evaluations( + AllEntities& evals) +{ + std::array result; + size_t dst = 0; + for (auto& entity : evals.get_full_circuit_entities()) { + result[dst++] = entity; + } + return result; +} + +template +void TranslatorFlavor::set_full_circuit_evaluations( + AllEntities& evals, const std::array& full_circuit) +{ + size_t src = 0; + for (auto& entity : evals.get_full_circuit_entities()) { + entity = full_circuit[src++]; + } +} + +template void TranslatorFlavor::compute_computable_precomputed( + AllEntities&, std::span); +template void TranslatorFlavor::set_minicircuit_evaluations( + AllEntities&, const std::array&); +template void TranslatorFlavor::complete_claimed_evaluations( + AllEntities&, std::span); +template void TranslatorFlavor::complete_full_circuit_evaluations( + AllEntities&, + const std::array&, + std::span); +template std::array TranslatorFlavor:: + reconstruct_concatenated_evaluations(AllEntities&, + std::span); +template std::array TranslatorFlavor:: + reconstruct_concatenated_evaluations(AllEntities&, + std::span); +template std::array TranslatorFlavor:: + get_full_circuit_evaluations(AllEntities&); +template void TranslatorFlavor::set_full_circuit_evaluations( + AllEntities&, const std::array&); + +template void TranslatorFlavor::compute_computable_precomputed( + AllEntities&, std::span); +template void TranslatorFlavor::set_minicircuit_evaluations( + AllEntities&, + const std::array&); +template void TranslatorFlavor::complete_claimed_evaluations( + AllEntities&, std::span); +template void TranslatorFlavor::complete_full_circuit_evaluations( + AllEntities&, + const std::array&, + std::span); +template std::array TranslatorFlavor:: + reconstruct_concatenated_evaluations( + AllEntities&, std::span); +template std::array TranslatorFlavor:: + reconstruct_concatenated_evaluations( + AllEntities&, std::span); +template void TranslatorFlavor::set_full_circuit_evaluations( + AllEntities&, + const std::array&); + +TranslatorFlavor::ProverPolynomials::ProverPolynomials() +{ + const size_t circuit_size = 1 << CONST_TRANSLATOR_LOG_N; + for (auto& ordered_range_constraint : get_ordered_range_constraints()) { + ordered_range_constraint = Polynomial{ /*size*/ circuit_size - 1, + /*largest possible index*/ circuit_size, + 1 }; + } + + // Initialize 5 concatenated polynomials (full circuit_size, shiftable with start_index=1) + // Row 0 of block 0 is the no-op row where all values are zero. + for (auto& concat_poly : get_concatenated()) { + concat_poly = Polynomial{ /*size*/ circuit_size - 1, + /*virtual_size*/ circuit_size, + /*start_index*/ 1 }; + } + z_perm = Polynomial{ /*size*/ circuit_size - 1, + /*virtual_size*/ circuit_size, + /*start_index*/ 1 }; + + op = Polynomial{ MINI_CIRCUIT_SIZE, circuit_size }; + + // All minicircuit wires (non-op-queue) are only non-zero in [1, MINI_CIRCUIT_SIZE) + for (auto& poly : this->get_minicircuit_wires()) { + if (poly.is_empty()) { + poly = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - 1, + /*virtual_size*/ circuit_size, + /*start_index*/ 1 }; + } + } + + // Op queue wires to be shifted + for (auto& poly : this->get_op_queue_split_wires()) { + if (poly.is_empty()) { + poly = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - 1, + /*virtual_size*/ circuit_size, + /*start_index*/ 1 }; + } + } + + // Initialize lagrange polynomials and the ordered extra range constraints numerator (the precomputed + // polynomials) within the appropriate range they operate on + lagrange_first = Polynomial{ /*size*/ 1, /*virtual_size*/ circuit_size }; + lagrange_result_row = Polynomial{ /*size*/ 1, /*virtual_size*/ circuit_size, /*start_index*/ RESULT_ROW }; + lagrange_even_in_minicircuit = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - RESULT_ROW - NUM_MASKED_ROWS_END, + /*virtual_size*/ circuit_size, + /*start_index=*/RESULT_ROW }; + lagrange_odd_in_minicircuit = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - RESULT_ROW - NUM_MASKED_ROWS_END - 1, + /*virtual_size*/ circuit_size, + /*start_index=*/RESULT_ROW + 1 }; + lagrange_last_in_minicircuit = Polynomial{ /*size*/ 1, + /*virtual_size*/ circuit_size, + /*start_index=*/MINI_CIRCUIT_SIZE - NUM_MASKED_ROWS_END - 1 }; + lagrange_mini_masking = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - RANDOMNESS_START, + /*virtual_size*/ circuit_size, + /*start_index=*/RANDOMNESS_START }; + // With concatenation, masking rows are scattered in concatenated polys: end of each of the 16 blocks + // Must span full circuit since values go up to position 15*MINI+(MINI-1) + lagrange_masking = Polynomial{ circuit_size, circuit_size }; + // Ordered masking: contiguous at the end (marks masking positions in ordered polynomials) + lagrange_ordered_masking = Polynomial{ /*size*/ MAX_RANDOM_VALUES_PER_ORDERED, + /*virtual_size*/ circuit_size, + /*start_index*/ circuit_size - MAX_RANDOM_VALUES_PER_ORDERED }; + lagrange_last = Polynomial{ /*size*/ 1, + /*virtual_size*/ circuit_size, + /*start_index*/ circuit_size - 1 }; + // lagrange_real_last marks the last position with sorted values in ordered polynomials + // (where we check maximum value = 2^14 - 1). With contiguous masking at the end, + // this is at position circuit_size - MAX_RANDOM_VALUES_PER_ORDERED - 1. + lagrange_real_last = Polynomial{ /*size*/ 1, + /*virtual_size*/ circuit_size, + /*start_index*/ circuit_size - MAX_RANDOM_VALUES_PER_ORDERED - 1 }; + ordered_extra_range_constraints_numerator = + Polynomial{ /*size*/ SORTED_STEPS_COUNT * NUM_CONCATENATED_POLYS + MASKING_OVERFLOW_COLUMN, + /*virtual_size*/ circuit_size, + /*start_index*/ 0 }; + + set_shifted(); +} + +TranslatorFlavor::AllValues TranslatorFlavor::ProverPolynomials::get_row(size_t row_idx) const +{ + AllValues result; + for (auto [result_field, polynomial] : zip_view(result.get_all(), this->get_all())) { + // Translator polynomials have different support regions (start_index/end_index) + // Return 0 for out-of-bounds access (which is the correct value outside support) + if (row_idx >= polynomial.start_index() && row_idx < polynomial.end_index()) { + result_field = polynomial[row_idx]; + } else { + result_field = FF(0); + } + } + return result; +} + +void TranslatorFlavor::ProverPolynomials::set_shifted() +{ + for (auto [shifted, to_be_shifted] : zip_view(get_shifted(), get_all_to_be_shifted())) { + shifted = to_be_shifted.shifted(); + } +} + +TranslatorFlavor::CommitmentLabels::CommitmentLabels() +{ + // Concatenated polynomials (sent via get_non_opqueue_wires_and_ordered_range_constraints) + this->concatenated_range_constraints_0 = "CONCATENATED_RANGE_CONSTRAINTS_0"; + this->concatenated_range_constraints_1 = "CONCATENATED_RANGE_CONSTRAINTS_1"; + this->concatenated_range_constraints_2 = "CONCATENATED_RANGE_CONSTRAINTS_2"; + this->concatenated_range_constraints_3 = "CONCATENATED_RANGE_CONSTRAINTS_3"; + this->concatenated_non_range = "CONCATENATED_NON_RANGE"; + + // Ordered range constraints (sent via get_non_opqueue_wires_and_ordered_range_constraints) + this->ordered_range_constraints_0 = "ORDERED_RANGE_CONSTRAINTS_0"; + this->ordered_range_constraints_1 = "ORDERED_RANGE_CONSTRAINTS_1"; + this->ordered_range_constraints_2 = "ORDERED_RANGE_CONSTRAINTS_2"; + this->ordered_range_constraints_3 = "ORDERED_RANGE_CONSTRAINTS_3"; + this->ordered_range_constraints_4 = "ORDERED_RANGE_CONSTRAINTS_4"; + + // Grand product (committed separately) + this->z_perm = "Z_PERM"; +} + +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp index 19859cd96f8d..b4a45d5a7893 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp @@ -30,6 +30,283 @@ namespace bb { +#define DEFINE_ENTITY_DECL(name) DataType name; +#define DEFINE_ENTITY_REF(name) name, +#define DEFINE_ENTITY_LABEL(name) #name, +#define DEFINE_ENTITY_COUNT(name) +1 + +#define DEFINE_SHIFTED_ENTITY_DECL(name) DataType name##_shift; +#define DEFINE_SHIFTED_ENTITY_REF(name) name##_shift, +#define DEFINE_SHIFTED_ENTITY_LABEL(name) #name "_shift", + +#define DEFINE_VIEW_FROM_LIST(method, LIST) \ + [[nodiscard]] auto method() \ + { \ + return RefArray, 0 LIST(DEFINE_ENTITY_COUNT)>{ LIST(DEFINE_ENTITY_REF) }; \ + } \ + [[nodiscard]] auto method() const \ + { \ + return RefArray, 0 LIST(DEFINE_ENTITY_COUNT)>{ LIST( \ + DEFINE_ENTITY_REF) }; \ + } + +#define DEFINE_SHIFTED_VIEW_FROM_LIST(method, LIST) \ + [[nodiscard]] auto method() \ + { \ + return RefArray, 0 LIST(DEFINE_ENTITY_COUNT)>{ LIST( \ + DEFINE_SHIFTED_ENTITY_REF) }; \ + } \ + [[nodiscard]] auto method() const \ + { \ + return RefArray, 0 LIST(DEFINE_ENTITY_COUNT)>{ LIST( \ + DEFINE_SHIFTED_ENTITY_REF) }; \ + } + +// Generate a flat RefArray view that mixes entities named directly (from UNSHIFTED_LIST) with entities +// named with the `_shift` suffix (from SHIFTED_LIST). Used for views like get_all / get_full_circuit_entities +// that span both unshifted and shifted entities. +#define DEFINE_MIXED_VIEW_FROM_LIST(method, UNSHIFTED_LIST, SHIFTED_LIST) \ + [[nodiscard]] auto method() \ + { \ + return RefArray, \ + 0 UNSHIFTED_LIST(DEFINE_ENTITY_COUNT) SHIFTED_LIST(DEFINE_ENTITY_COUNT)>{ UNSHIFTED_LIST( \ + DEFINE_ENTITY_REF) SHIFTED_LIST(DEFINE_SHIFTED_ENTITY_REF) }; \ + } \ + [[nodiscard]] auto method() const \ + { \ + return RefArray, \ + 0 UNSHIFTED_LIST(DEFINE_ENTITY_COUNT) SHIFTED_LIST(DEFINE_ENTITY_COUNT)>{ UNSHIFTED_LIST( \ + DEFINE_ENTITY_REF) SHIFTED_LIST(DEFINE_SHIFTED_ENTITY_REF) }; \ + } + +#define DEFINE_FIELDS_FROM_LIST(DataType, LIST) LIST(DEFINE_ENTITY_DECL) +#define DEFINE_SHIFTED_FIELDS_FROM_LIST(DataType, LIST) LIST(DEFINE_SHIFTED_ENTITY_DECL) + +#define LIST_SIZE(LIST) (0 LIST(DEFINE_ENTITY_COUNT)) + +#define PRECOMPUTED_COLUMNS(M) \ + M(ordered_extra_range_constraints_numerator) \ + M(lagrange_first) \ + M(lagrange_last) \ + M(lagrange_odd_in_minicircuit) \ + M(lagrange_even_in_minicircuit) \ + M(lagrange_result_row) \ + M(lagrange_last_in_minicircuit) \ + M(lagrange_masking) \ + M(lagrange_mini_masking) \ + M(lagrange_real_last) \ + M(lagrange_ordered_masking) + +#define VK_COLUMNS(M) M(ordered_extra_range_constraints_numerator) + +#define CONCATENATED_COLUMNS(M) \ + M(concatenated_range_constraints_0) \ + M(concatenated_range_constraints_1) \ + M(concatenated_range_constraints_2) \ + M(concatenated_range_constraints_3) \ + M(concatenated_non_range) + +#define OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ + M(x_lo_y_hi) \ + M(x_hi_z_1) \ + M(y_lo_z_2) + +#define ORDERED_RANGE_COLUMNS(M) \ + M(ordered_range_constraints_0) \ + M(ordered_range_constraints_1) \ + M(ordered_range_constraints_2) \ + M(ordered_range_constraints_3) \ + M(ordered_range_constraints_4) + +#define OP_QUEUE_WIRE_COLUMNS(M) M(op) + +#define GRAND_PRODUCT_COLUMNS(M) M(z_perm) + +// PCS column lists — each derived view textually contains the shared PCS_SHIFT_SOURCE_COLUMNS +// (and CONCATENATED_COLUMNS where applicable). The "every shift source is also opened +// unshifted" invariant is therefore structural: any entity added to PCS_SHIFT_SOURCE_COLUMNS +// flows automatically into both PCS_UNSHIFTED_COLUMNS and PCS_REPEATED_COLUMNS. + +// Polynomials whose unshifted commitment is registered in BOTH PCS batches because their relations read +// them at row i AND row i+1 (op-queue split wires via the decomposition relation; ordered/grand-product +// via their permutation/grand-product structure). These do NOT include concatenated polys (whose +// evaluations are reconstructed from minicircuit-wire evals rather than committed and sent directly). +#define PCS_SHIFT_SOURCE_COLUMNS(M) \ + OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ + ORDERED_RANGE_COLUMNS(M) \ + GRAND_PRODUCT_COLUMNS(M) + +// All polynomials registered in the shifted PCS batch (= every shift source plus the concatenated polys). +// 14 entries: op_queue_split(3) + ordered(5) + z_perm(1) + concat(5). +#define PCS_REPEATED_COLUMNS(M) \ + PCS_SHIFT_SOURCE_COLUMNS(M) \ + CONCATENATED_COLUMNS(M) + +// All unshifted PCS entities except the concatenated polys (whose evaluations the verifier reconstructs +// from minicircuit-wire evals rather than reading from the proof). Used as the unshifted-named prefix of +// get_full_circuit_entities. 12 entries: masking(1) + vk(1) + op(1) + shift_source(9). +#define PCS_UNSHIFTED_EXCLUDING_CONCAT_COLUMNS(M) \ + MASKING_COLUMNS(M) \ + VK_COLUMNS(M) \ + OP_QUEUE_WIRE_COLUMNS(M) \ + PCS_SHIFT_SOURCE_COLUMNS(M) + +// All unshifted PCS entities (excludes computable precomputed selectors). 17 entries: the prefix above +// plus concatenated(5). Contains PCS_REPEATED_COLUMNS as a contiguous suffix, structurally +// enforcing "to-be-shifted ⊆ unshifted". +#define PCS_UNSHIFTED_COLUMNS(M) \ + PCS_UNSHIFTED_EXCLUDING_CONCAT_COLUMNS(M) \ + CONCATENATED_COLUMNS(M) + +// All polynomials whose shift-by-1 enters Sumcheck. Layout: +// op_queue_split(3) + minicircuit(77) + ordered(5) + grand_product(1) = 86. +#define ALL_TO_BE_SHIFTED_COLUMNS(M) \ + OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ + NON_OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ + ORDERED_RANGE_COLUMNS(M) \ + GRAND_PRODUCT_COLUMNS(M) + +// Unshifted half of get_all: masking + precomputed + witness, contiguous prefix in the AllEntities layout. +#define NON_SHIFTED_COLUMNS(M) \ + MASKING_COLUMNS(M) \ + PRECOMPUTED_COLUMNS(M) \ + WITNESS_COLUMNS(M) + +// All wire columns (op-queue + minicircuit). Layout: op(1) + op_queue_split(3) + minicircuit(77) = 81. +#define WIRES_COLUMNS(M) \ + OP_QUEUE_WIRE_COLUMNS(M) \ + OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ + NON_OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) + +// Concatenated + ordered range (committed in the wire-round). Layout: concat(5) + ordered(5) = 10. +#define NON_OPQUEUE_WIRES_AND_ORDERED_RANGE_COLUMNS(M) \ + CONCATENATED_COLUMNS(M) \ + ORDERED_RANGE_COLUMNS(M) + +#define MASKING_COLUMNS(M) M(gemini_masking_poly) + +// ---- Concat-group chunks ---- +// Each chunk lists the minicircuit wires that fold into one concatenated polynomial. The "non_range" +// chunk lists 13 real wires; the runtime padder zeros it out to CONCATENATION_GROUP_SIZE. +#define CONCAT_GROUP_NON_RANGE(M) \ + M(p_x_low_limbs) \ + M(p_x_high_limbs) \ + M(p_y_low_limbs) \ + M(p_y_high_limbs) \ + M(z_low_limbs) \ + M(z_high_limbs) \ + M(accumulators_binary_limbs_0) \ + M(accumulators_binary_limbs_1) \ + M(accumulators_binary_limbs_2) \ + M(accumulators_binary_limbs_3) \ + M(quotient_low_binary_limbs) \ + M(quotient_high_binary_limbs) \ + M(relation_wide_limbs) + +#define CONCAT_GROUP_RANGE_0(M) \ + M(p_x_low_limbs_range_constraint_0) \ + M(p_x_low_limbs_range_constraint_1) \ + M(p_x_low_limbs_range_constraint_2) \ + M(p_x_low_limbs_range_constraint_3) \ + M(p_x_low_limbs_range_constraint_4) \ + M(p_x_low_limbs_range_constraint_tail) \ + M(p_x_high_limbs_range_constraint_0) \ + M(p_x_high_limbs_range_constraint_1) \ + M(p_x_high_limbs_range_constraint_2) \ + M(p_x_high_limbs_range_constraint_3) \ + M(p_x_high_limbs_range_constraint_4) \ + M(p_x_high_limbs_range_constraint_tail) \ + M(p_y_low_limbs_range_constraint_0) \ + M(p_y_low_limbs_range_constraint_1) \ + M(p_y_low_limbs_range_constraint_2) \ + M(p_y_low_limbs_range_constraint_3) + +#define CONCAT_GROUP_RANGE_1(M) \ + M(p_y_low_limbs_range_constraint_4) \ + M(p_y_low_limbs_range_constraint_tail) \ + M(p_y_high_limbs_range_constraint_0) \ + M(p_y_high_limbs_range_constraint_1) \ + M(p_y_high_limbs_range_constraint_2) \ + M(p_y_high_limbs_range_constraint_3) \ + M(p_y_high_limbs_range_constraint_4) \ + M(p_y_high_limbs_range_constraint_tail) \ + M(z_low_limbs_range_constraint_0) \ + M(z_low_limbs_range_constraint_1) \ + M(z_low_limbs_range_constraint_2) \ + M(z_low_limbs_range_constraint_3) \ + M(z_low_limbs_range_constraint_4) \ + M(z_low_limbs_range_constraint_tail) \ + M(z_high_limbs_range_constraint_0) \ + M(z_high_limbs_range_constraint_1) + +#define CONCAT_GROUP_RANGE_2(M) \ + M(z_high_limbs_range_constraint_2) \ + M(z_high_limbs_range_constraint_3) \ + M(z_high_limbs_range_constraint_4) \ + M(z_high_limbs_range_constraint_tail) \ + M(accumulator_low_limbs_range_constraint_0) \ + M(accumulator_low_limbs_range_constraint_1) \ + M(accumulator_low_limbs_range_constraint_2) \ + M(accumulator_low_limbs_range_constraint_3) \ + M(accumulator_low_limbs_range_constraint_4) \ + M(accumulator_low_limbs_range_constraint_tail) \ + M(accumulator_high_limbs_range_constraint_0) \ + M(accumulator_high_limbs_range_constraint_1) \ + M(accumulator_high_limbs_range_constraint_2) \ + M(accumulator_high_limbs_range_constraint_3) \ + M(accumulator_high_limbs_range_constraint_4) \ + M(accumulator_high_limbs_range_constraint_tail) + +#define CONCAT_GROUP_RANGE_3(M) \ + M(quotient_low_limbs_range_constraint_0) \ + M(quotient_low_limbs_range_constraint_1) \ + M(quotient_low_limbs_range_constraint_2) \ + M(quotient_low_limbs_range_constraint_3) \ + M(quotient_low_limbs_range_constraint_4) \ + M(quotient_low_limbs_range_constraint_tail) \ + M(quotient_high_limbs_range_constraint_0) \ + M(quotient_high_limbs_range_constraint_1) \ + M(quotient_high_limbs_range_constraint_2) \ + M(quotient_high_limbs_range_constraint_3) \ + M(quotient_high_limbs_range_constraint_4) \ + M(quotient_high_limbs_range_constraint_tail) \ + M(relation_wide_limbs_range_constraint_0) \ + M(relation_wide_limbs_range_constraint_1) \ + M(relation_wide_limbs_range_constraint_2) \ + M(relation_wide_limbs_range_constraint_3) + +// Single source of truth: each row pairs a concatenated polynomial name with its chunk macro. +// Iteration order matches the output order of get_groups_to_be_concatenated(). +#define CONCAT_MAP(ROW) \ + ROW(concatenated_range_constraints_0, CONCAT_GROUP_RANGE_0) \ + ROW(concatenated_range_constraints_1, CONCAT_GROUP_RANGE_1) \ + ROW(concatenated_range_constraints_2, CONCAT_GROUP_RANGE_2) \ + ROW(concatenated_range_constraints_3, CONCAT_GROUP_RANGE_3) \ + ROW(concatenated_non_range, CONCAT_GROUP_NON_RANGE) + +// Flat list of all 77 minicircuit wires. Order: non-range first, then range groups in order — matches +// the historical AllEntities field layout. +#define NON_OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ + CONCAT_GROUP_NON_RANGE(M) \ + CONCAT_GROUP_RANGE_0(M) \ + CONCAT_GROUP_RANGE_1(M) \ + CONCAT_GROUP_RANGE_2(M) \ + CONCAT_GROUP_RANGE_3(M) + +#define WITNESS_COLUMNS(M) \ + OP_QUEUE_WIRE_COLUMNS(M) \ + OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ + NON_OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ + ORDERED_RANGE_COLUMNS(M) \ + GRAND_PRODUCT_COLUMNS(M) \ + CONCATENATED_COLUMNS(M) + +#define SHIFTED_COLUMNS(M) \ + OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ + NON_OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ + ORDERED_RANGE_COLUMNS(M) \ + GRAND_PRODUCT_COLUMNS(M) + class TranslatorFlavor { public: @@ -146,519 +423,98 @@ class TranslatorFlavor { static constexpr size_t num_frs_fr = FrCodec::calc_num_fields(); static constexpr size_t num_frs_fq = FrCodec::calc_num_fields(); - /** - * @brief A base class labelling precomputed entities and (ordered) subsets of interest. - * @details Used to build the proving key and verification key. - */ - template class PrecomputedEntities { + template class AllEntities { public: - bool operator==(const PrecomputedEntities& other) const = default; using DataType = DataType_; - DEFINE_FLAVOR_MEMBERS(DataType, - ordered_extra_range_constraints_numerator, // column 0 - lagrange_first, // column 1 - lagrange_last, // column 2 - lagrange_odd_in_minicircuit, // column 3 - lagrange_even_in_minicircuit, // column 4 - lagrange_result_row, // column 5 - lagrange_last_in_minicircuit, // column 6 - lagrange_masking, // column 7 - lagrange_mini_masking, // column 8 - lagrange_real_last, // column 9 - lagrange_ordered_masking); // column 10 - }; - - template class ConcatenatedPolynomials { - public: - DEFINE_FLAVOR_MEMBERS(DataType, - concatenated_range_constraints_0, // column 0 - concatenated_range_constraints_1, // column 1 - concatenated_range_constraints_2, // column 2 - concatenated_range_constraints_3, // column 3 - concatenated_non_range) // column 4 - }; - /** - * @brief Non-range main wires (13 wires that go into concatenated group 4) - */ - template class NonRangeMainWires { - public: - DEFINE_FLAVOR_MEMBERS(DataType, - p_x_low_limbs, // column 0 - p_x_high_limbs, // column 1 - p_y_low_limbs, // column 2 - p_y_high_limbs, // column 3 - z_low_limbs, // column 4 - z_high_limbs, // column 5 - accumulators_binary_limbs_0, // column 6 - accumulators_binary_limbs_1, // column 7 - accumulators_binary_limbs_2, // column 8 - accumulators_binary_limbs_3, // column 9 - quotient_low_binary_limbs, // column 10 - quotient_high_binary_limbs, // column 11 - relation_wide_limbs) // column 12 - }; - - /** - * @brief Range constraint wires (64 wires that go into concatenated groups 0-3) - */ - template class RangeConstraintWires { - public: - DEFINE_FLAVOR_MEMBERS(DataType, - p_x_low_limbs_range_constraint_0, // column 0 - p_x_low_limbs_range_constraint_1, // column 17 - p_x_low_limbs_range_constraint_2, // column 18 - p_x_low_limbs_range_constraint_3, // column 19 - p_x_low_limbs_range_constraint_4, // column 20 - p_x_low_limbs_range_constraint_tail, // column 21 - p_x_high_limbs_range_constraint_0, // column 22 - p_x_high_limbs_range_constraint_1, // column 23 - p_x_high_limbs_range_constraint_2, // column 24 - p_x_high_limbs_range_constraint_3, // column 25 - p_x_high_limbs_range_constraint_4, // column 26 - p_x_high_limbs_range_constraint_tail, // column 27 - p_y_low_limbs_range_constraint_0, // column 28 - p_y_low_limbs_range_constraint_1, // column 29 - p_y_low_limbs_range_constraint_2, // column 30 - p_y_low_limbs_range_constraint_3, // column 31 - p_y_low_limbs_range_constraint_4, // column 32 - p_y_low_limbs_range_constraint_tail, // column 33 - p_y_high_limbs_range_constraint_0, // column 34 - p_y_high_limbs_range_constraint_1, // column 35 - p_y_high_limbs_range_constraint_2, // column 36 - p_y_high_limbs_range_constraint_3, // column 37 - p_y_high_limbs_range_constraint_4, // column 38 - p_y_high_limbs_range_constraint_tail, // column 39 - z_low_limbs_range_constraint_0, // column 40 - z_low_limbs_range_constraint_1, // column 41 - z_low_limbs_range_constraint_2, // column 42 - z_low_limbs_range_constraint_3, // column 43 - z_low_limbs_range_constraint_4, // column 44 - z_low_limbs_range_constraint_tail, // column 45 - z_high_limbs_range_constraint_0, // column 46 - z_high_limbs_range_constraint_1, // column 47 - z_high_limbs_range_constraint_2, // column 48 - z_high_limbs_range_constraint_3, // column 49 - z_high_limbs_range_constraint_4, // column 50 - z_high_limbs_range_constraint_tail, // column 51 - accumulator_low_limbs_range_constraint_0, // column 52 - accumulator_low_limbs_range_constraint_1, // column 53 - accumulator_low_limbs_range_constraint_2, // column 54 - accumulator_low_limbs_range_constraint_3, // column 55 - accumulator_low_limbs_range_constraint_4, // column 56 - accumulator_low_limbs_range_constraint_tail, // column 57 - accumulator_high_limbs_range_constraint_0, // column 58 - accumulator_high_limbs_range_constraint_1, // column 59 - accumulator_high_limbs_range_constraint_2, // column 60 - accumulator_high_limbs_range_constraint_3, // column 61 - accumulator_high_limbs_range_constraint_4, // column 62 - accumulator_high_limbs_range_constraint_tail, // column 63 - quotient_low_limbs_range_constraint_0, // column 64 - quotient_low_limbs_range_constraint_1, // column 65 - quotient_low_limbs_range_constraint_2, // column 66 - quotient_low_limbs_range_constraint_3, // column 67 - quotient_low_limbs_range_constraint_4, // column 68 - quotient_low_limbs_range_constraint_tail, // column 69 - quotient_high_limbs_range_constraint_0, // column 70 - quotient_high_limbs_range_constraint_1, // column 71 - quotient_high_limbs_range_constraint_2, // column 72 - quotient_high_limbs_range_constraint_3, // column 73 - quotient_high_limbs_range_constraint_4, // column 74 - quotient_high_limbs_range_constraint_tail, // column 75 - relation_wide_limbs_range_constraint_0, // column 76 - relation_wide_limbs_range_constraint_1, // column 77 - relation_wide_limbs_range_constraint_2, // column 62 - relation_wide_limbs_range_constraint_3); // column 63 - }; - - /** - * @brief All non-op-queue wires that need to be shifted (composed of non-range main + range constraint) - */ - template - class NonOpQueueWiresToBeShiftedEntities : public NonRangeMainWires, - public RangeConstraintWires { - public: - DEFINE_COMPOUND_GET_ALL(NonRangeMainWires, RangeConstraintWires) - }; - - /** - * @brief Op queue wires (to be shifted): first 3 wires of the to-be-shifted group - */ - template class OpQueueWiresToBeShiftedEntities { - public: - DEFINE_FLAVOR_MEMBERS(DataType, - x_lo_y_hi, // column 0 - x_hi_z_1, // column 1 - y_lo_z_2) // column 2 - }; - - /** - * @brief All wires to be shifted (op queue + non-op-queue) - */ - template - class WireToBeShiftedEntities : public OpQueueWiresToBeShiftedEntities, - public NonOpQueueWiresToBeShiftedEntities { - public: - DEFINE_COMPOUND_GET_ALL(OpQueueWiresToBeShiftedEntities, NonOpQueueWiresToBeShiftedEntities) - }; - - // Note: These are technically derived from wires but do not depend on challenges (like z_perm). They are committed - // to in the wires commitment round. - template class OrderedRangeConstraints { - public: - DEFINE_FLAVOR_MEMBERS(DataType, - ordered_range_constraints_0, // column 0 - ordered_range_constraints_1, // column 1 - ordered_range_constraints_2, // column 2 - ordered_range_constraints_3, // column 3 - ordered_range_constraints_4); // column 4 - }; - - /** - * @brief Op queue wires (non-shifted): these represent the op queue and are provided by the merge protocol - */ - template class OpQueueWireNonshiftedEntities { - public: - DEFINE_FLAVOR_MEMBERS(DataType, - op // column 0 - ); - }; - - /** - * @brief All wire entities that are not shifted (currently just the op queue wire) - */ - template class WireNonshiftedEntities : public OpQueueWireNonshiftedEntities { - public: - DEFINE_COMPOUND_GET_ALL(OpQueueWireNonshiftedEntities) - }; - - template class DerivedWitnessEntities { - public: - DEFINE_FLAVOR_MEMBERS(DataType, - z_perm); // column 0 - }; - /** - * @brief Container for all witness polynomials used/constructed by the prover. - */ - template - class WitnessEntities : public WireNonshiftedEntities, - public WireToBeShiftedEntities, - public OrderedRangeConstraints, - public DerivedWitnessEntities, - public ConcatenatedPolynomials { - public: - DEFINE_COMPOUND_GET_ALL(WireNonshiftedEntities, - WireToBeShiftedEntities, - OrderedRangeConstraints, - DerivedWitnessEntities, - ConcatenatedPolynomials) + DEFINE_FIELDS_FROM_LIST(DataType, MASKING_COLUMNS) + DEFINE_FIELDS_FROM_LIST(DataType, PRECOMPUTED_COLUMNS) + DEFINE_FIELDS_FROM_LIST(DataType, WITNESS_COLUMNS) + DEFINE_SHIFTED_FIELDS_FROM_LIST(DataType, SHIFTED_COLUMNS) + + // ---- Public views consumed by the prover/verifier and external flavor framework ---- + DEFINE_VIEW_FROM_LIST(get_precomputed, PRECOMPUTED_COLUMNS) + DEFINE_VIEW_FROM_LIST(get_witness, WITNESS_COLUMNS) + DEFINE_VIEW_FROM_LIST(get_concatenated, CONCATENATED_COLUMNS) + DEFINE_VIEW_FROM_LIST(get_op_queue_split_wires, OP_QUEUE_SHIFT_SOURCE_COLUMNS) + DEFINE_VIEW_FROM_LIST(get_minicircuit_wires, NON_OP_QUEUE_SHIFT_SOURCE_COLUMNS) + DEFINE_VIEW_FROM_LIST(get_ordered_range_constraints, ORDERED_RANGE_COLUMNS) + DEFINE_VIEW_FROM_LIST(get_wires, WIRES_COLUMNS) + DEFINE_VIEW_FROM_LIST(get_all_to_be_shifted, ALL_TO_BE_SHIFTED_COLUMNS) + DEFINE_VIEW_FROM_LIST(get_non_opqueue_wires_and_ordered_range_constraints, + NON_OPQUEUE_WIRES_AND_ORDERED_RANGE_COLUMNS) + + DEFINE_SHIFTED_VIEW_FROM_LIST(get_shifted, SHIFTED_COLUMNS) + DEFINE_SHIFTED_VIEW_FROM_LIST(get_minicircuit_wires_shifted, NON_OP_QUEUE_SHIFT_SOURCE_COLUMNS) + DEFINE_SHIFTED_VIEW_FROM_LIST(get_pcs_shifted, PCS_SHIFT_SOURCE_COLUMNS) /** - * @brief Entities constructed from circuit data. - * + * @brief All unshifted polynomials for PCS (excludes computable precomputed). + * @details masking(1) + vk_precomputed(1) + op_queue_wire(1) + op_queue_split(3) + ordered(5) + * + grand_product(1) + concat(5) = 17. The trailing 14 entries (PCS_REPEATED_COLUMNS) + * are also registered as shift sources — see get_pcs_to_be_shifted. */ - auto get_wires() - { - return concatenate(WireNonshiftedEntities::get_all(), - WireToBeShiftedEntities::get_all()); - }; + DEFINE_VIEW_FROM_LIST(get_pcs_unshifted, PCS_UNSHIFTED_COLUMNS) /** - * @brief Concatenated polynomials and ordered range constraints (committed to by translator prover). - * @details 5 concatenated + 5 ordered = 10 commitments. + * @brief All to-be-shifted polynomials for PCS. Each entity here also lives in get_pcs_unshifted + * (PCS_REPEATED_COLUMNS is a contiguous suffix of PCS_UNSHIFTED_COLUMNS). */ - auto get_non_opqueue_wires_and_ordered_range_constraints() - { - return concatenate(ConcatenatedPolynomials::get_all(), - OrderedRangeConstraints::get_all()); - }; + DEFINE_VIEW_FROM_LIST(get_pcs_to_be_shifted, PCS_REPEATED_COLUMNS) - /** - * @brief All polys that need shifted views for Sumcheck (corresponds 1:1 with ShiftedEntities). - * @details WireToBeShifted(80) + OrderedRangeConstraints(5) + DerivedWitness(1) = 86 - */ - auto get_all_to_be_shifted() + DEFINE_MIXED_VIEW_FROM_LIST(get_all, NON_SHIFTED_COLUMNS, SHIFTED_COLUMNS) + constexpr std::size_t size() const { return get_all().size(); } + static const std::vector& get_labels() { - return concatenate(WireToBeShiftedEntities::get_all(), - OrderedRangeConstraints::get_all(), - DerivedWitnessEntities::get_all()); - }; + static const auto labels = + concatenate(std::vector{ NON_SHIFTED_COLUMNS(DEFINE_ENTITY_LABEL) }, + std::vector{ SHIFTED_COLUMNS(DEFINE_SHIFTED_ENTITY_LABEL) }); + return labels; + } /** - * @brief Get the concatenated polynomials. + * @brief Full-circuit entities sent in the proof (excludes computable precomputed, minicircuit wires, + * and concatenated polys whose evals are reconstructed from wire evals). + * @details PCS_UNSHIFTED_EXCLUDING_CONCAT_COLUMNS (12, unshifted-named) + PCS_SHIFT_SOURCE_COLUMNS + * (9, `_shift`-suffixed) = 21. */ - auto get_concatenated() { return ConcatenatedPolynomials::get_all(); } + DEFINE_MIXED_VIEW_FROM_LIST(get_full_circuit_entities, + PCS_UNSHIFTED_EXCLUDING_CONCAT_COLUMNS, + PCS_SHIFT_SOURCE_COLUMNS) - /** - * @brief Get all minicircuit wire polynomials that are concatenated into the 5 concatenated polys. - * @details Returns 5 groups of 16 wires each. Groups 0-3 are range constraint wires; group 4 is - * 13 non-range main wires + 3 null padding slots. - */ + // Build the partition driven by CONCAT_MAP. Each row of the map declares a concatenated + // polynomial together with the chunk macro listing its constituent minicircuit wires. The chunks + // are emitted directly into a RefVector and zero-padded to CONCATENATION_GROUP_SIZE. std::vector> get_groups_to_be_concatenated() { static DataType zero_value = DataType(0); - return partition_minicircuit_wires_into_groups( - NonOpQueueWiresToBeShiftedEntities::get_all(), zero_value); - }; - }; - - /** - * @brief Op queue shifted entities (mirrors OpQueueWiresToBeShiftedEntities) - */ - template class OpQueueShiftedEntities { - public: - DEFINE_FLAVOR_MEMBERS(DataType, - x_lo_y_hi_shift, // column 0 - x_hi_z_1_shift, // column 1 - y_lo_z_2_shift) // column 2 - }; - - /** - * @brief Non-op-queue minicircuit wire shifted entities (mirrors NonOpQueueWiresToBeShiftedEntities) - */ - template class NonOpQueueShiftedEntities { - public: - DEFINE_FLAVOR_MEMBERS(DataType, - p_x_low_limbs_shift, // column 3 - p_x_high_limbs_shift, // column 10 - p_y_low_limbs_shift, // column 17 - p_y_high_limbs_shift, // column 24 - z_low_limbs_shift, // column 31 - z_high_limbs_shift, // column 38 - accumulators_binary_limbs_0_shift, // column 45 - accumulators_binary_limbs_1_shift, // column 46 - accumulators_binary_limbs_2_shift, // column 47 - accumulators_binary_limbs_3_shift, // column 48 - quotient_low_binary_limbs_shift, // column 61 - quotient_high_binary_limbs_shift, // column 62 - relation_wide_limbs_shift, // column 75 - p_x_low_limbs_range_constraint_0_shift, // column 4 - p_x_low_limbs_range_constraint_1_shift, // column 5 - p_x_low_limbs_range_constraint_2_shift, // column 6 - p_x_low_limbs_range_constraint_3_shift, // column 7 - p_x_low_limbs_range_constraint_4_shift, // column 8 - p_x_low_limbs_range_constraint_tail_shift, // column 9 - p_x_high_limbs_range_constraint_0_shift, // column 11 - p_x_high_limbs_range_constraint_1_shift, // column 12 - p_x_high_limbs_range_constraint_2_shift, // column 13 - p_x_high_limbs_range_constraint_3_shift, // column 14 - p_x_high_limbs_range_constraint_4_shift, // column 15 - p_x_high_limbs_range_constraint_tail_shift, // column 16 - p_y_low_limbs_range_constraint_0_shift, // column 18 - p_y_low_limbs_range_constraint_1_shift, // column 19 - p_y_low_limbs_range_constraint_2_shift, // column 20 - p_y_low_limbs_range_constraint_3_shift, // column 21 - p_y_low_limbs_range_constraint_4_shift, // column 22 - p_y_low_limbs_range_constraint_tail_shift, // column 23 - p_y_high_limbs_range_constraint_0_shift, // column 25 - p_y_high_limbs_range_constraint_1_shift, // column 26 - p_y_high_limbs_range_constraint_2_shift, // column 27 - p_y_high_limbs_range_constraint_3_shift, // column 28 - p_y_high_limbs_range_constraint_4_shift, // column 29 - p_y_high_limbs_range_constraint_tail_shift, // column 30 - z_low_limbs_range_constraint_0_shift, // column 32 - z_low_limbs_range_constraint_1_shift, // column 33 - z_low_limbs_range_constraint_2_shift, // column 34 - z_low_limbs_range_constraint_3_shift, // column 35 - z_low_limbs_range_constraint_4_shift, // column 36 - z_low_limbs_range_constraint_tail_shift, // column 37 - z_high_limbs_range_constraint_0_shift, // column 39 - z_high_limbs_range_constraint_1_shift, // column 40 - z_high_limbs_range_constraint_2_shift, // column 41 - z_high_limbs_range_constraint_3_shift, // column 42 - z_high_limbs_range_constraint_4_shift, // column 43 - z_high_limbs_range_constraint_tail_shift, // column 44 - accumulator_low_limbs_range_constraint_0_shift, // column 49 - accumulator_low_limbs_range_constraint_1_shift, // column 50 - accumulator_low_limbs_range_constraint_2_shift, // column 51 - accumulator_low_limbs_range_constraint_3_shift, // column 52 - accumulator_low_limbs_range_constraint_4_shift, // column 53 - accumulator_low_limbs_range_constraint_tail_shift, // column 54 - accumulator_high_limbs_range_constraint_0_shift, // column 55 - accumulator_high_limbs_range_constraint_1_shift, // column 56 - accumulator_high_limbs_range_constraint_2_shift, // column 57 - accumulator_high_limbs_range_constraint_3_shift, // column 58 - accumulator_high_limbs_range_constraint_4_shift, // column 59 - accumulator_high_limbs_range_constraint_tail_shift, // column 60 - quotient_low_limbs_range_constraint_0_shift, // column 63 - quotient_low_limbs_range_constraint_1_shift, // column 64 - quotient_low_limbs_range_constraint_2_shift, // column 65 - quotient_low_limbs_range_constraint_3_shift, // column 66 - quotient_low_limbs_range_constraint_4_shift, // column 67 - quotient_low_limbs_range_constraint_tail_shift, // column 68 - quotient_high_limbs_range_constraint_0_shift, // column 69 - quotient_high_limbs_range_constraint_1_shift, // column 70 - quotient_high_limbs_range_constraint_2_shift, // column 71 - quotient_high_limbs_range_constraint_3_shift, // column 72 - quotient_high_limbs_range_constraint_4_shift, // column 73 - quotient_high_limbs_range_constraint_tail_shift, // column 74 - relation_wide_limbs_range_constraint_0_shift, // column 76 - relation_wide_limbs_range_constraint_1_shift, // column 77 - relation_wide_limbs_range_constraint_2_shift, // column 78 - relation_wide_limbs_range_constraint_3_shift) // column 79 - }; - - /** - * @brief Ordered range constraint + z_perm shifted entities - */ - template class DerivedShiftedEntities { - public: - DEFINE_FLAVOR_MEMBERS(DataType, - ordered_range_constraints_0_shift, // column 80 - ordered_range_constraints_1_shift, // column 81 - ordered_range_constraints_2_shift, // column 82 - ordered_range_constraints_3_shift, // column 83 - ordered_range_constraints_4_shift, // column 84 - z_perm_shift) // column 85 - }; - - /** - * @brief Represents polynomials shifted by 1 or their evaluations, defined relative to WireToBeShiftedEntities. - */ - template - class ShiftedEntities : public OpQueueShiftedEntities, - public NonOpQueueShiftedEntities, - public DerivedShiftedEntities { - public: - DEFINE_COMPOUND_GET_ALL(OpQueueShiftedEntities, - NonOpQueueShiftedEntities, - DerivedShiftedEntities) - - /** - * @brief PCS-level shifted evaluations matching get_to_be_shifted(): - * op_queue(3) + ordered_range(5) + z_perm(1) = 9 - */ - auto get_pcs_shifted() - { - return concatenate(OpQueueShiftedEntities::get_all(), - DerivedShiftedEntities::get_all()); + std::vector> groups; +#define PUSH_REF(name) group.push_back(name); +#define PUSH_GROUP(concat_name, group_macro) \ + { \ + RefVector group; \ + group_macro(PUSH_REF) while (group.size() < CONCATENATION_GROUP_SIZE) group.push_back(zero_value); \ + groups.emplace_back(std::move(group)); \ + } + CONCAT_MAP(PUSH_GROUP) +#undef PUSH_REF +#undef PUSH_GROUP + return groups; } - - /** - * @brief Get the shifted versions of minicircuit wires organized into 5 concatenation groups. - * @details Mirrors get_groups_to_be_concatenated() but with shifted wire entities. - */ std::vector> get_groups_to_be_concatenated_shifted() { static DataType zero_value = DataType(0); - return partition_minicircuit_wires_into_groups(NonOpQueueShiftedEntities::get_all(), - zero_value); - }; - }; - - /** - * @brief Container for ZK entities (gemini masking polynomial for ZK-PCS) - * @details Translator is always ZK, so this always contains the masking polynomial - */ - template class MaskingEntities { - public: - DEFINE_FLAVOR_MEMBERS(DataType, gemini_masking_poly) - }; - - /** - * @brief A base class labelling all entities (for instance, all of the polynomials used by the prover during - * sumcheck) in this Honk variant along with particular subsets of interest. - * @details Used to build containers for: the prover's polynomial during sumcheck; the sumcheck's folded - * polynomials; the univariates consturcted during during sumcheck; the evaluations produced by sumcheck. - * - * Symbolically we have: AllEntities = PrecomputedEntities + WitnessEntities + ShiftedEntities + MaskingEntities. - */ - template - class AllEntities : public MaskingEntities, - public PrecomputedEntities, - public WitnessEntities, - public ShiftedEntities { - public: - DEFINE_COMPOUND_GET_ALL(MaskingEntities, - PrecomputedEntities, - WitnessEntities, - ShiftedEntities) - - /** - * @brief Getter for concatenated polynomials - */ - auto get_concatenated() { return ConcatenatedPolynomials::get_all(); }; - - /** - * @brief Getter for the ordered entities used in computing the denominator of the grand product in the - * permutation relation. - */ - auto get_ordered_range_constraints() { return OrderedRangeConstraints::get_all(); }; - - /** - * @brief All unshifted polynomials for PCS (excludes computable precomputed, includes concatenated). - * @details masking(1) + ordered_extra(1) + op(1) + op_queue_tbs(3) + ordered(5) + z_perm(1) + concat(5) = 17 - * - * The op-queue to-be-shifted wires (x_lo_y_hi, x_hi_z_1, y_lo_z_2) appear here in addition to - * get_pcs_to_be_shifted because the decomposition relation reads them both unshifted (e.g. `x_lo`) - * and shift-by-1 (e.g. `y_hi`) at the same row. Without registering the unshifted opening, the - * unshifted MLE evaluations at the sumcheck point would be unconstrained. - */ - auto get_pcs_unshifted() - { - return concatenate( - MaskingEntities::get_all(), // gemini_masking_poly - RefArray{ this->ordered_extra_range_constraints_numerator }, // non-computable precomputed - WireNonshiftedEntities::get_all(), // op (from merge protocol) - OpQueueWiresToBeShiftedEntities::get_all(), // x_lo_y_hi, x_hi_z_1, y_lo_z_2 - OrderedRangeConstraints::get_all(), // ordered_0..4 - DerivedWitnessEntities::get_all(), // z_perm - ConcatenatedPolynomials::get_all()); // concat_0..4 - } - - /** - * @brief All to-be-shifted polynomials for PCS (base to-be-shifted + concatenated). - * @details op_queue_shifted(3) + ordered(5) + z_perm(1) + concat(5) = 14 - */ - auto get_pcs_to_be_shifted() - { - return concatenate(OpQueueWiresToBeShiftedEntities::get_all(), // x_lo_y_hi, x_hi_z_1, y_lo_z_2 - OrderedRangeConstraints::get_all(), // ordered_0..4 - DerivedWitnessEntities::get_all(), // z_perm - ConcatenatedPolynomials::get_all()); // concat_0..4 - } - - auto get_shifted() { return ShiftedEntities::get_all(); }; - auto get_pcs_shifted() { return ShiftedEntities::get_pcs_shifted(); }; - - /** - * @brief Full-circuit entities sent in the proof (excludes computable precomputed, minicircuit wires, - * and concatenated polys whose evals are reconstructed from wire evals). - * @details Masking(1) + ordered_extra(1) + op(1) + OpQueueTBS(3) + OrderedRange(5) + z_perm(1) - * + pcs_shifted(9) = 21 - */ - auto get_full_circuit_entities() - { - return concatenate(MaskingEntities::get_all(), - RefArray{ this->ordered_extra_range_constraints_numerator }, - WireNonshiftedEntities::get_all(), - OpQueueWiresToBeShiftedEntities::get_all(), - OrderedRangeConstraints::get_all(), - DerivedWitnessEntities::get_all(), - ShiftedEntities::get_pcs_shifted()); - } - - /** - * @brief The 77 minicircuit wires (unshifted): NonRangeMain(13) + RangeConstraint(64). - */ - auto get_minicircuit_wires() { return NonOpQueueWiresToBeShiftedEntities::get_all(); } - - /** - * @brief The 77 minicircuit wire shifts: corresponds 1:1 with get_minicircuit_wires(). - */ - auto get_minicircuit_wires_shifted() { return NonOpQueueShiftedEntities::get_all(); } - - friend std::ostream& operator<<(std::ostream& os, const AllEntities& a) - { - os << "{ "; - std::ios_base::fmtflags f(os.flags()); - auto entities = a.get_all(); - for (size_t i = 0; i < entities.size() - 1; i++) { - os << "e[" << std::setw(2) << i << "] = " << (entities[i]) << ",\n"; - } - os << "e[" << std::setw(2) << (entities.size() - 1) << "] = " << entities[entities.size() - 1] << " }"; - - os.flags(f); - return os; + std::vector> groups; +#define PUSH_SHIFT_REF(name) group.push_back(name##_shift); +#define PUSH_SHIFT_GROUP(concat_name, group_macro) \ + { \ + RefVector group; \ + group_macro(PUSH_SHIFT_REF) while (group.size() < CONCATENATION_GROUP_SIZE) group.push_back(zero_value); \ + groups.emplace_back(std::move(group)); \ + } + CONCAT_MAP(PUSH_SHIFT_GROUP) +#undef PUSH_SHIFT_REF +#undef PUSH_SHIFT_GROUP + return groups; } }; @@ -673,23 +529,15 @@ class TranslatorFlavor { }; // ======================================== - // Derived entity counts (from entity class sizes) + // Entity counts (from entity class sizes) // ======================================== - static constexpr size_t NUM_PRECOMPUTED_ENTITIES = PrecomputedEntities::_members_size; - static constexpr size_t NUM_WIRES_NON_SHIFTED = WireNonshiftedEntities::_members_size; - static constexpr size_t NUM_ORDERED_RANGE = OrderedRangeConstraints::_members_size; - - // Witness = WireNonshifted + WireToBeShifted + OrderedRange + Derived + Concatenated - static constexpr size_t NUM_WITNESS_ENTITIES = - WireNonshiftedEntities::_members_size + OpQueueWiresToBeShiftedEntities::_members_size + - NonRangeMainWires::_members_size + RangeConstraintWires::_members_size + - OrderedRangeConstraints::_members_size + DerivedWitnessEntities::_members_size + - ConcatenatedPolynomials::_members_size; - - // Shifted = OpQueueShifted + NonOpQueueShifted + DerivedShifted - static constexpr size_t NUM_SHIFTED_ENTITIES = OpQueueShiftedEntities::_members_size + - NonOpQueueShiftedEntities::_members_size + - DerivedShiftedEntities::_members_size; + static constexpr size_t NUM_PRECOMPUTED_ENTITIES = LIST_SIZE(PRECOMPUTED_COLUMNS); + static constexpr size_t NUM_OP_QUEUE_WIRES_NOT_SHIFT_SOURCE = LIST_SIZE(OP_QUEUE_WIRE_COLUMNS); + static constexpr size_t NUM_ORDERED_RANGE = LIST_SIZE(ORDERED_RANGE_COLUMNS); + + static constexpr size_t NUM_WITNESS_ENTITIES = LIST_SIZE(WITNESS_COLUMNS); + + static constexpr size_t NUM_SHIFTED_ENTITIES = LIST_SIZE(SHIFTED_COLUMNS); static constexpr size_t NUM_ALL_ENTITIES = NUM_MASKING_POLYNOMIALS + NUM_PRECOMPUTED_ENTITIES + NUM_WITNESS_ENTITIES + NUM_SHIFTED_ENTITIES; @@ -698,10 +546,7 @@ class TranslatorFlavor { static constexpr size_t NUM_COMPUTABLE_PRECOMPUTED = NUM_PRECOMPUTED_ENTITIES - 1; // Minicircuit wires: NonRangeMain + RangeConstraint (the non-op-queue wires that get shifted) - static constexpr size_t NUM_MINICIRCUIT_WIRES = - NonRangeMainWires::_members_size + RangeConstraintWires::_members_size; - static_assert(NUM_MINICIRCUIT_WIRES == NonOpQueueShiftedEntities::_members_size, - "Shifted minicircuit wires must match unshifted"); + static constexpr size_t NUM_MINICIRCUIT_WIRES = LIST_SIZE(NON_OP_QUEUE_SHIFT_SOURCE_COLUMNS); // 77 unshifted + 77 shifted minicircuit wire evaluations are sent mid-sumcheck static constexpr size_t NUM_MINICIRCUIT_EVALUATIONS = 2 * NUM_MINICIRCUIT_WIRES; @@ -712,26 +557,24 @@ class TranslatorFlavor { // Total number of minicircuit wires across all concatenation groups static constexpr size_t NUM_CONCATENATED_WIRES = NUM_CONCATENATED_POLYS * CONCATENATION_GROUP_SIZE; - static_assert(ConcatenatedPolynomials::_members_size == NUM_CONCATENATED_POLYS); - static_assert(RangeConstraintWires::_members_size == (NUM_CONCATENATED_POLYS - 1) * CONCATENATION_GROUP_SIZE, - "Range constraint wires must fill exactly 4 concatenation groups"); - - // PCS batch sizes - // Note: op-queue to-be-shifted wires (x_lo_y_hi, x_hi_z_1, y_lo_z_2) are registered in BOTH the - // unshifted and shifted PCS batches because the decomposition relation reads them in both forms. - static constexpr size_t NUM_UNSHIFTED_WITNESSES_WITHOUT_CONCATENATED = - WireNonshiftedEntities::_members_size + OpQueueWiresToBeShiftedEntities::_members_size + - OrderedRangeConstraints::_members_size + DerivedWitnessEntities::_members_size; - static constexpr size_t NUM_TO_BE_SHIFTED = OpQueueWiresToBeShiftedEntities::_members_size + - OrderedRangeConstraints::_members_size + - DerivedWitnessEntities::_members_size; - static constexpr size_t NUM_PCS_UNSHIFTED = NUM_MASKING_POLYNOMIALS + - (NUM_PRECOMPUTED_ENTITIES - NUM_COMPUTABLE_PRECOMPUTED) + - NUM_UNSHIFTED_WITNESSES_WITHOUT_CONCATENATED + NUM_CONCATENATED_POLYS; - static constexpr size_t NUM_PCS_TO_BE_SHIFTED = NUM_TO_BE_SHIFTED + NUM_CONCATENATED_POLYS; + static_assert(LIST_SIZE(CONCATENATED_COLUMNS) == NUM_CONCATENATED_POLYS); + static_assert(LIST_SIZE(CONCAT_GROUP_RANGE_0) == CONCATENATION_GROUP_SIZE); + static_assert(LIST_SIZE(CONCAT_GROUP_RANGE_1) == CONCATENATION_GROUP_SIZE); + static_assert(LIST_SIZE(CONCAT_GROUP_RANGE_2) == CONCATENATION_GROUP_SIZE); + static_assert(LIST_SIZE(CONCAT_GROUP_RANGE_3) == CONCATENATION_GROUP_SIZE); + static_assert(LIST_SIZE(CONCAT_GROUP_NON_RANGE) <= CONCATENATION_GROUP_SIZE, + "Non-range concat group must fit in one concatenation group (zero-padded at runtime)"); + + // PCS batch sizes derived directly from the column-list macros. Op-queue to-be-shifted wires + // (x_lo_y_hi, x_hi_z_1, y_lo_z_2) are registered in BOTH the unshifted and shifted PCS batches + // because the decomposition relation reads them in both forms. + static constexpr size_t NUM_TO_BE_SHIFTED = LIST_SIZE(PCS_SHIFT_SOURCE_COLUMNS); + static constexpr size_t NUM_PCS_UNSHIFTED = LIST_SIZE(PCS_UNSHIFTED_COLUMNS); + static constexpr size_t NUM_PCS_TO_BE_SHIFTED = LIST_SIZE(PCS_REPEATED_COLUMNS); // Indices for partitioning AllEntities - static constexpr size_t TO_BE_SHIFTED_WITNESSES_START = NUM_PRECOMPUTED_ENTITIES + NUM_WIRES_NON_SHIFTED; + static constexpr size_t TO_BE_SHIFTED_WITNESSES_START = + NUM_PRECOMPUTED_ENTITIES + NUM_OP_QUEUE_WIRES_NOT_SHIFT_SOURCE; static constexpr size_t SHIFTED_WITNESSES_START = NUM_SHIFTED_ENTITIES + TO_BE_SHIFTED_WITNESSES_START; // Commitments sent in wire round: concatenated + ordered range constraints @@ -746,13 +589,18 @@ class TranslatorFlavor { // stored indices 2..10 (unshifted) ↔ 16..24 (shifted) // Range 2: concatenated(5) — stored indices 11..15 (unshifted) ↔ 25..29 (shifted) // (Stored indices are 0-based after ZK offset; offset=2 accounts for Q_commitment + gemini_masking_poly) - static constexpr size_t NUM_OP_QUEUE_TO_BE_SHIFTED = OpQueueWiresToBeShiftedEntities::_members_size; + static constexpr size_t PCS_REPEATED_NON_CONCAT_START = LIST_SIZE(VK_COLUMNS) + NUM_OP_QUEUE_WIRES_NOT_SHIFT_SOURCE; + static constexpr size_t PCS_REPEATED_CONCATENATED_START = PCS_REPEATED_NON_CONCAT_START + NUM_TO_BE_SHIFTED; + static constexpr size_t PCS_REPEATED_NON_CONCAT_SHIFTED_START = + PCS_REPEATED_NON_CONCAT_START + NUM_PCS_TO_BE_SHIFTED; + static constexpr size_t PCS_REPEATED_CONCATENATED_SHIFTED_START = + PCS_REPEATED_NON_CONCAT_SHIFTED_START + NUM_TO_BE_SHIFTED; static constexpr RepeatedCommitmentsData REPEATED_COMMITMENTS = - RepeatedCommitmentsData(2, - 2 + NUM_PCS_TO_BE_SHIFTED, - NUM_OP_QUEUE_TO_BE_SHIFTED + NUM_ORDERED_RANGE + 1, - 2 + NUM_OP_QUEUE_TO_BE_SHIFTED + NUM_ORDERED_RANGE + 1, - 2 + NUM_PCS_TO_BE_SHIFTED + NUM_OP_QUEUE_TO_BE_SHIFTED + NUM_ORDERED_RANGE + 1, + RepeatedCommitmentsData(PCS_REPEATED_NON_CONCAT_START, + PCS_REPEATED_NON_CONCAT_SHIFTED_START, + NUM_TO_BE_SHIFTED, + PCS_REPEATED_CONCATENATED_START, + PCS_REPEATED_CONCATENATED_SHIFTED_START, NUM_CONCATENATED_POLYS); static constexpr size_t PROOF_LENGTH = @@ -800,51 +648,11 @@ class TranslatorFlavor { // ================================================================ - /** - * @brief Partition minicircuit wire references into concatenation groups. - * @details Takes a flat list of minicircuit wire refs (NonRangeMain followed by RangeConstraint) - * and partitions them: groups 0..3 are sequential chunks of CONCATENATION_GROUP_SIZE range constraint wires, - * group 4 is the non-range main wires with zero-padding. - * Used by both get_groups_to_be_concatenated() and get_groups_to_be_concatenated_shifted(). - */ - template - static std::vector> partition_minicircuit_wires_into_groups(WireRefs wire_refs, - DataType& zero_value) - { - constexpr size_t num_non_range = NonRangeMainWires::_members_size; - constexpr size_t num_range = RangeConstraintWires::_members_size; - static_assert(num_range % CONCATENATION_GROUP_SIZE == 0); - constexpr size_t num_range_groups = num_range / CONCATENATION_GROUP_SIZE; - - std::vector> groups; - // Groups 0..num_range_groups-1: sequential chunks of range constraint wires - for (size_t g = 0; g < num_range_groups; g++) { - RefVector group; - for (size_t j = 0; j < CONCATENATION_GROUP_SIZE; j++) { - group.push_back(wire_refs[num_non_range + g * CONCATENATION_GROUP_SIZE + j]); - } - groups.push_back(std::move(group)); - } - // Last group: non-range main wires + zero padding - RefVector group; - for (size_t j = 0; j < num_non_range; j++) { - group.push_back(wire_refs[j]); - } - for (size_t j = num_non_range; j < CONCATENATION_GROUP_SIZE; j++) { - group.push_back(zero_value); - } - groups.push_back(std::move(group)); - return groups; - } - /** * @brief Compute the computable precomputed selector evaluations and write them into AllEntities. */ template - static void compute_computable_precomputed(AllEntities& evals, std::span challenge) - { - TranslatorSelectorEvaluations::compute(challenge).populate(evals); - } + static void compute_computable_precomputed(AllEntities& evals, std::span challenge); /** * @brief Prover: read the 154 minicircuit wire evaluations from partially-evaluated polynomials. @@ -872,16 +680,7 @@ class TranslatorFlavor { */ template static void set_minicircuit_evaluations(AllEntities& evals, - const std::array& mid) - { - size_t src = 0; - for (auto& wire : evals.get_minicircuit_wires()) { - wire = mid[src++]; - } - for (auto& wire : evals.get_minicircuit_wires_shifted()) { - wire = mid[src++]; - } - } + const std::array& mid); /** * @brief Verifier: complete the claimed evaluations for the sumcheck relation check. @@ -892,23 +691,7 @@ class TranslatorFlavor { * challenges, converting mid-sumcheck values to full evaluations at the sumcheck point. */ template - static void complete_claimed_evaluations(AllEntities& evals, std::span challenge) - { - // 1. Compute the computable precomputed selector evaluations - compute_computable_precomputed(evals, challenge); - - // 2. Scale minicircuit wire evaluations by L_0(u_top) = Π_{i=0}^{3} (1 - u_{LOG_MINI + i}) - FFType l0 = FFType(1); - for (size_t i = 0; i < CONST_TRANSLATOR_LOG_N - LOG_MINI_CIRCUIT_SIZE; i++) { - l0 *= (FFType(1) - challenge[LOG_MINI_CIRCUIT_SIZE + i]); - } - for (auto& wire : evals.get_minicircuit_wires()) { - wire *= l0; - } - for (auto& wire : evals.get_minicircuit_wires_shifted()) { - wire *= l0; - } - } + static void complete_claimed_evaluations(AllEntities& evals, std::span challenge); /** * @brief Verifier: complete full-circuit evaluations from received array and challenge. @@ -921,19 +704,7 @@ class TranslatorFlavor { template static void complete_full_circuit_evaluations(AllEntities& evals, const std::array& full_circuit, - std::span challenge) - { - set_full_circuit_evaluations(evals, full_circuit); - complete_claimed_evaluations(evals, challenge); - - // Reconstruct the 5 concatenated polynomial evaluations from (now L0-scaled) wire evaluations - auto groups = evals.get_groups_to_be_concatenated(); - auto concat_evals = reconstruct_concatenated_evaluations(groups, challenge); - auto concat_refs = evals.get_concatenated(); - for (size_t g = 0; g < NUM_CONCATENATED_POLYS; g++) { - concat_refs[g] = concat_evals[g]; - } - } + std::span challenge); /** * @brief Reconstruct concatenated polynomial evaluations from individual wire evaluations @@ -943,78 +714,31 @@ class TranslatorFlavor { * is reconstructed as: F(u) = [1/L_0(u_top)] * Σ_j L_j(u_top) * f_j(u), where L_j are the Lagrange * basis polynomials over the top challenges and L_0 is the "padding" factor. * - * @param groups The 5 groups of 16 wire evaluations to reconstruct from. + * Wire evaluations are read directly from `evals` via CONCAT_MAP — the chunk per concat + * polynomial is named by the map and accessed by field name (with `_shift` suffix when Shifted=true), + * so no intermediate `std::vector` is materialised. + * + * @tparam Shifted If true, accumulate from `name##_shift` fields instead of `name`. + * @param evals AllEntities holding wire evaluations. * @param challenge The full sumcheck challenge vector. * @return Array of 5 reconstructed concatenated evaluations. */ - template + template static std::array reconstruct_concatenated_evaluations( - const std::vector>& groups, std::span challenge) - { - static constexpr size_t NUM_TOP_BITS = numeric::get_msb(CONCATENATION_GROUP_SIZE); - - // Compute CONCATENATION_GROUP_SIZE-point Lagrange basis over the top challenges - // a = u[N - 4], b = u[N - 3], c = u[N - 2], d = u[N - 1] - // L(0) = (1 - d) * (1 - c) * (1 - b) * (1 - a) - // L(1) = (1 - d) * (1 - c) * (1 - b) * ( a) - // L(2) = (1 - d) * (1 - c) * ( b) * (1 - a) - // L(3) = (1 - d) * (1 - c) * ( b) * ( a) - // ... - std::array lagrange_basis; - for (size_t j = 0; j < CONCATENATION_GROUP_SIZE; j++) { - lagrange_basis[j] = FFType(1); - for (size_t bit = 0; bit < NUM_TOP_BITS; bit++) { - const FFType& u = challenge[CONST_TRANSLATOR_LOG_N - NUM_TOP_BITS + bit]; - lagrange_basis[j] *= ((j >> bit) & 1) ? u : (FFType(1) - u); - } - } - - // L_0 is the "padding" factor from wires having support in [1, MINI) - // The reason we need to divide by L_0 is because L_j(u) already accounts for the challenges a, b, c, d: - // L_j(u) = (1 - d) * (1 - c) * (1 - b) * (1 - a) * L_j(0, u_bottom) - FFType padding_inv = lagrange_basis[0].invert(); - - auto reconstruct = [&](const auto& group) -> FFType { - FFType result = FFType(0); - for (size_t j = 0; j < CONCATENATION_GROUP_SIZE; j++) { - result += lagrange_basis[j] * group[j]; - } - return result * padding_inv; - }; - - std::array result; - for (size_t g = 0; g < NUM_CONCATENATED_POLYS; g++) { - result[g] = reconstruct(groups[g]); - } - return result; - } + AllEntities& evals, std::span challenge); /** * @brief Prover: extract the full-circuit evaluations via get_full_circuit_entities(). */ template - static std::array get_full_circuit_evaluations(AllEntities& evals) - { - std::array result; - size_t dst = 0; - for (auto& entity : evals.get_full_circuit_entities()) { - result[dst++] = entity; - } - return result; - } + static std::array get_full_circuit_evaluations(AllEntities& evals); /** * @brief Verifier: write the full-circuit evaluations back via get_full_circuit_entities(). */ template static void set_full_circuit_evaluations(AllEntities& evals, - const std::array& full_circuit) - { - size_t src = 0; - for (auto& entity : evals.get_full_circuit_entities()) { - entity = full_circuit[src++]; - } - } + const std::array& full_circuit); /** * @brief A container for the prover polynomials handles. @@ -1025,86 +749,7 @@ class TranslatorFlavor { * @brief ProverPolynomials constructor * @details Initializes wire polynomials efficiently to be only minicircuit size.. */ - ProverPolynomials() - { - - const size_t circuit_size = 1 << CONST_TRANSLATOR_LOG_N; - for (auto& ordered_range_constraint : get_ordered_range_constraints()) { - ordered_range_constraint = Polynomial{ /*size*/ circuit_size - 1, - /*largest possible index*/ circuit_size, - 1 }; - } - - // Initialize 5 concatenated polynomials (full circuit_size, shiftable with start_index=1) - // Row 0 of block 0 is the no-op row where all values are zero. - for (auto& concat_poly : get_concatenated()) { - concat_poly = Polynomial{ /*size*/ circuit_size - 1, - /*virtual_size*/ circuit_size, - /*start_index*/ 1 }; - } - z_perm = Polynomial{ /*size*/ circuit_size - 1, - /*virtual_size*/ circuit_size, - /*start_index*/ 1 }; - - op = Polynomial{ MINI_CIRCUIT_SIZE, circuit_size }; - - // All minicircuit wires (non-op-queue) are only non-zero in [1, MINI_CIRCUIT_SIZE) - for (auto& poly : NonOpQueueWiresToBeShiftedEntities::get_all()) { - if (poly.is_empty()) { - poly = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - 1, - /*virtual_size*/ circuit_size, - /*start_index*/ 1 }; - } - } - - // Op queue wires to be shifted - for (auto& poly : OpQueueWiresToBeShiftedEntities::get_all()) { - if (poly.is_empty()) { - poly = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - 1, - /*virtual_size*/ circuit_size, - /*start_index*/ 1 }; - } - } - - // Initialize lagrange polynomials and the ordered extra range constraints numerator (the precomputed - // polynomials) within the appropriate range they operate on - lagrange_first = Polynomial{ /*size*/ 1, /*virtual_size*/ circuit_size }; - lagrange_result_row = Polynomial{ /*size*/ 1, /*virtual_size*/ circuit_size, /*start_index*/ RESULT_ROW }; - lagrange_even_in_minicircuit = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - RESULT_ROW - NUM_MASKED_ROWS_END, - /*virtual_size*/ circuit_size, - /*start_index=*/RESULT_ROW }; - lagrange_odd_in_minicircuit = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - RESULT_ROW - NUM_MASKED_ROWS_END - 1, - /*virtual_size*/ circuit_size, - /*start_index=*/RESULT_ROW + 1 }; - lagrange_last_in_minicircuit = Polynomial{ /*size*/ 1, - /*virtual_size*/ circuit_size, - /*start_index=*/MINI_CIRCUIT_SIZE - NUM_MASKED_ROWS_END - 1 }; - lagrange_mini_masking = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - RANDOMNESS_START, - /*virtual_size*/ circuit_size, - /*start_index=*/RANDOMNESS_START }; - // With concatenation, masking rows are scattered in concatenated polys: end of each of the 16 blocks - // Must span full circuit since values go up to position 15*MINI+(MINI-1) - lagrange_masking = Polynomial{ circuit_size, circuit_size }; - // Ordered masking: contiguous at the end (marks masking positions in ordered polynomials) - lagrange_ordered_masking = Polynomial{ /*size*/ MAX_RANDOM_VALUES_PER_ORDERED, - /*virtual_size*/ circuit_size, - /*start_index*/ circuit_size - MAX_RANDOM_VALUES_PER_ORDERED }; - lagrange_last = Polynomial{ /*size*/ 1, - /*virtual_size*/ circuit_size, - /*start_index*/ circuit_size - 1 }; - // lagrange_real_last marks the last position with sorted values in ordered polynomials - // (where we check maximum value = 2^14 - 1). With contiguous masking at the end, - // this is at position circuit_size - MAX_RANDOM_VALUES_PER_ORDERED - 1. - lagrange_real_last = Polynomial{ /*size*/ 1, - /*virtual_size*/ circuit_size, - /*start_index*/ circuit_size - MAX_RANDOM_VALUES_PER_ORDERED - 1 }; - ordered_extra_range_constraints_numerator = - Polynomial{ /*size*/ SORTED_STEPS_COUNT * NUM_CONCATENATED_POLYS + MASKING_OVERFLOW_COLUMN, - /*virtual_size*/ circuit_size, - /*start_index*/ 0 }; - - set_shifted(); - } + ProverPolynomials(); ProverPolynomials& operator=(const ProverPolynomials&) = delete; ProverPolynomials(const ProverPolynomials& o) = delete; ProverPolynomials(ProverPolynomials&& o) noexcept = default; @@ -1115,28 +760,10 @@ class TranslatorFlavor { * @brief Returns the evaluations of all prover polynomials at one point on the boolean * hypercube, which represents one row in the execution trace. */ - [[nodiscard]] AllValues get_row(size_t row_idx) const - { - AllValues result; - for (auto [result_field, polynomial] : zip_view(result.get_all(), this->get_all())) { - // Translator polynomials have different support regions (start_index/end_index) - // Return 0 for out-of-bounds access (which is the correct value outside support) - if (row_idx >= polynomial.start_index() && row_idx < polynomial.end_index()) { - result_field = polynomial[row_idx]; - } else { - result_field = FF(0); - } - } - return result; - } + [[nodiscard]] AllValues get_row(size_t row_idx) const; // Set all shifted polynomials based on their to-be-shifted counterpart. - // Uses get_all_to_be_shifted() (86 entries for Sumcheck), not get_to_be_shifted() (9 entries for PCS). - void set_shifted() - { - for (auto [shifted, to_be_shifted] : zip_view(get_shifted(), get_all_to_be_shifted())) { - shifted = to_be_shifted.shifted(); - } - } + // Uses get_all_to_be_shifted() (86 entries for Sumcheck), not get_pcs_to_be_shifted() (14 entries for PCS). + void set_shifted(); }; /** @@ -1163,7 +790,8 @@ class TranslatorFlavor { public: bool operator==(const VKEntities& other) const = default; using DataType = DataType_; - DEFINE_FLAVOR_MEMBERS(DataType, ordered_extra_range_constraints_numerator); + DEFINE_FIELDS_FROM_LIST(DataType, VK_COLUMNS) + DEFINE_VIEW_FROM_LIST(get_all, VK_COLUMNS) }; /** @@ -1197,37 +825,7 @@ class TranslatorFlavor { */ class CommitmentLabels : public AllEntities { public: - CommitmentLabels() - { - // Concatenated polynomials (sent via get_non_opqueue_wires_and_ordered_range_constraints) - this->concatenated_range_constraints_0 = "CONCATENATED_RANGE_CONSTRAINTS_0"; - this->concatenated_range_constraints_1 = "CONCATENATED_RANGE_CONSTRAINTS_1"; - this->concatenated_range_constraints_2 = "CONCATENATED_RANGE_CONSTRAINTS_2"; - this->concatenated_range_constraints_3 = "CONCATENATED_RANGE_CONSTRAINTS_3"; - this->concatenated_non_range = "CONCATENATED_NON_RANGE"; - - // Ordered range constraints (sent via get_non_opqueue_wires_and_ordered_range_constraints) - this->ordered_range_constraints_0 = "ORDERED_RANGE_CONSTRAINTS_0"; - this->ordered_range_constraints_1 = "ORDERED_RANGE_CONSTRAINTS_1"; - this->ordered_range_constraints_2 = "ORDERED_RANGE_CONSTRAINTS_2"; - this->ordered_range_constraints_3 = "ORDERED_RANGE_CONSTRAINTS_3"; - this->ordered_range_constraints_4 = "ORDERED_RANGE_CONSTRAINTS_4"; - - // Grand product (committed separately) - this->z_perm = "Z_PERM"; - }; - }; - - template - class VerifierCommitments_ : public AllEntities { - public: - VerifierCommitments_(const std::shared_ptr& verification_key) - { - // Only ordered_extra_range_constraints_numerator needs a VK commitment for PCS. - // All other precomputed selectors are computable (evaluations derived from sumcheck challenge). - this->ordered_extra_range_constraints_numerator = - verification_key->ordered_extra_range_constraints_numerator; - } + CommitmentLabels(); }; /** @@ -1257,7 +855,7 @@ class TranslatorFlavor { (polynomials.z_perm[edge_idx + 1] == polynomials.z_perm_shift[edge_idx + 1]) && polynomials.lagrange_last[edge_idx] == 0 && polynomials.lagrange_last[edge_idx + 1] == 0; } - using VerifierCommitments = VerifierCommitments_; + using VerifierCommitments = AllEntities; }; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_selectors.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_selectors.hpp index 4c0b48c3fb65..14fde3cad3ba 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_selectors.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_selectors.hpp @@ -45,7 +45,7 @@ template struct TranslatorSelectorEv static_assert(LOG_RESULT_ROW < LOG_MINI_CIRCUIT_SIZE); static_assert(LOG_MINI_CIRCUIT_SIZE > LOG_MAX_RANDOM, "Mini circuit must be larger than max random region"); - // The 10 selector evaluations (order matches PrecomputedEntities in translator_flavor.hpp, + // The 10 selector evaluations (order matches PRECOMPUTED_COLUMNS in translator_flavor.hpp, // skipping ordered_extra_range_constraints_numerator which cannot be efficiently computed) FF lagrange_first; FF lagrange_last; @@ -220,7 +220,7 @@ template struct TranslatorSelectorEv /** * @brief Write all 10 computed evaluations into any entity struct with matching named fields. - * @details Works for AllValues, AllEntities, PrecomputedEntities, native or stdlib. + * @details Works for any native or stdlib entity container with matching named selector fields. */ template void populate(Entities& target) const { diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp index ed0711f09689..6b2f0205f8b3 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp @@ -147,7 +147,10 @@ typename TranslatorVerifier_::VerifierCommitments TranslatorVerifier_add_to_hash_buffer("vk_hash", vk_hash); vinfo("Translator vk hash in verifier: ", vk_hash); - VerifierCommitments commitments{ key }; + VerifierCommitments commitments; + // Only ordered_extra_range_constraints_numerator needs a VK commitment for PCS. + // All other precomputed selectors are computable (evaluations derived from sumcheck challenge). + commitments.ordered_extra_range_constraints_numerator = key->ordered_extra_range_constraints_numerator; CommitmentLabels commitment_labels; // For recursive verification, mark the accumulated result's prime basis limb as used @@ -221,8 +224,8 @@ typename TranslatorVerifier_::ReductionResult TranslatorVerifier_(sumcheck_output.challenge)); + auto concat_shift_evals = TranslatorFlavor::reconstruct_concatenated_evaluations( + claimed, std::span(sumcheck_output.challenge)); // --- PCS: build opening claims and verify --- auto combined_unshifted_comms = commitments.get_pcs_unshifted(); From 7f748d2d264f6f239716f4b2d02e7fb1462740da Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Wed, 6 May 2026 15:43:39 +0000 Subject: [PATCH 07/17] gate count fix --- .../src/barretenberg/dsl/acir_format/gate_count_constants.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp index 4fecdbb5ecdc..b97e0aae4a8f 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp @@ -113,7 +113,7 @@ constexpr std::tuple HONK_RECURSION_CONSTANTS( // ======================================== // Gate count for Chonk recursive verification (Ultra with RollupIO) -inline constexpr size_t CHONK_RECURSION_GATES = 1474692; +inline constexpr size_t CHONK_RECURSION_GATES = 1474686; // ======================================== // Hypernova Recursion Constants From 45e8d33fd6d4a5e9e6fdb3e890d50ed4b531bff4 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Wed, 6 May 2026 16:01:04 +0000 Subject: [PATCH 08/17] fix stale comment --- .../batched_honk_translator_verifier.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_verifier.hpp b/barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_verifier.hpp index 51dde12dd1ab..d7ebfbf4178c 100644 --- a/barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_verifier.hpp @@ -63,15 +63,15 @@ template class BatchedHonkTranslatorVerifier_ { // Unshifted: [Trans_rest(TU-1) | MZK_precomputed(P) | MZK_witness(W)] // Shifted: [MZK_shifted(S) | Trans_shifted(TS)] // - // Range 1 (Translator merged): ordered(5)+z_perm(1)+concat(5) in unshifted ↔ same in shifted + // Range 1 (Translator): translator PCS shift sources repeated between the unshifted and shifted sections. // Range 2 (MegaZK): witness[0..S-1] ↔ mega_zk_shifted[0..S-1] static constexpr RepeatedCommitmentsData REPEATED_COMMITMENTS = [] { constexpr size_t TU = TranslatorFlavor::NUM_PCS_UNSHIFTED; // includes masking(1) constexpr size_t P = MegaZKFlavorT::NUM_PRECOMPUTED_ENTITIES; constexpr size_t W = MegaZKFlavorT::NUM_WITNESS_ENTITIES; constexpr size_t S = MegaZKFlavorT::NUM_SHIFTED_ENTITIES; - // Translator repeated: ordered(5)+z_perm(1)+concat(5) in Trans_rest ↔ Trans_shifted - // Trans_rest starts at virtual 0; repeated starts at ordered_extra(1)+op(1)=2 + // Translator repeated range: PCS shift sources in Trans_rest ↔ Trans_shifted. + // Trans_rest starts at virtual 0; repeated starts after ordered_extra and op. constexpr size_t TRANS_REPEAT_START = TranslatorFlavor::REPEATED_COMMITMENTS.first.original_start; constexpr size_t TRANS_REPEAT_COUNT = TranslatorFlavor::REPEATED_COMMITMENTS.first.count + TranslatorFlavor::REPEATED_COMMITMENTS.second.count; From 8b6d18724f9bdfe907c8df79549e00aeb11394e4 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Mon, 11 May 2026 11:23:35 +0000 Subject: [PATCH 09/17] Revert "fix stale comment" This reverts commit 45e8d33fd6d4a5e9e6fdb3e890d50ed4b531bff4. --- .../batched_honk_translator_verifier.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_verifier.hpp b/barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_verifier.hpp index d7ebfbf4178c..51dde12dd1ab 100644 --- a/barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_verifier.hpp @@ -63,15 +63,15 @@ template class BatchedHonkTranslatorVerifier_ { // Unshifted: [Trans_rest(TU-1) | MZK_precomputed(P) | MZK_witness(W)] // Shifted: [MZK_shifted(S) | Trans_shifted(TS)] // - // Range 1 (Translator): translator PCS shift sources repeated between the unshifted and shifted sections. + // Range 1 (Translator merged): ordered(5)+z_perm(1)+concat(5) in unshifted ↔ same in shifted // Range 2 (MegaZK): witness[0..S-1] ↔ mega_zk_shifted[0..S-1] static constexpr RepeatedCommitmentsData REPEATED_COMMITMENTS = [] { constexpr size_t TU = TranslatorFlavor::NUM_PCS_UNSHIFTED; // includes masking(1) constexpr size_t P = MegaZKFlavorT::NUM_PRECOMPUTED_ENTITIES; constexpr size_t W = MegaZKFlavorT::NUM_WITNESS_ENTITIES; constexpr size_t S = MegaZKFlavorT::NUM_SHIFTED_ENTITIES; - // Translator repeated range: PCS shift sources in Trans_rest ↔ Trans_shifted. - // Trans_rest starts at virtual 0; repeated starts after ordered_extra and op. + // Translator repeated: ordered(5)+z_perm(1)+concat(5) in Trans_rest ↔ Trans_shifted + // Trans_rest starts at virtual 0; repeated starts at ordered_extra(1)+op(1)=2 constexpr size_t TRANS_REPEAT_START = TranslatorFlavor::REPEATED_COMMITMENTS.first.original_start; constexpr size_t TRANS_REPEAT_COUNT = TranslatorFlavor::REPEATED_COMMITMENTS.first.count + TranslatorFlavor::REPEATED_COMMITMENTS.second.count; From b06e7ad633f6214761077e9dd11a966080fa310c Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Mon, 11 May 2026 11:23:35 +0000 Subject: [PATCH 10/17] Revert "gate count fix" This reverts commit 7f748d2d264f6f239716f4b2d02e7fb1462740da. --- .../src/barretenberg/dsl/acir_format/gate_count_constants.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp index b97e0aae4a8f..4fecdbb5ecdc 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp @@ -113,7 +113,7 @@ constexpr std::tuple HONK_RECURSION_CONSTANTS( // ======================================== // Gate count for Chonk recursive verification (Ultra with RollupIO) -inline constexpr size_t CHONK_RECURSION_GATES = 1474686; +inline constexpr size_t CHONK_RECURSION_GATES = 1474692; // ======================================== // Hypernova Recursion Constants From 629e6d1826374f5ef4089be36e0a988570cc4131 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Mon, 11 May 2026 11:23:35 +0000 Subject: [PATCH 11/17] Revert "macros for translator entities" This reverts commit 274155af365084f64b83cf8113a1bd8b3ab589cc. --- .../batched_honk_translator_verifier.cpp | 4 +- .../translator_circuit_checker.cpp | 5 +- .../cpp/src/barretenberg/flavor/flavor.hpp | 35 +- .../translator_recursive_flavor.hpp | 12 +- .../translator_vm/translator.test.cpp | 42 +- .../translator_vm/translator_fixed_vk.hpp | 19 - .../translator_vm/translator_flavor.cpp | 284 ---- .../translator_vm/translator_flavor.hpp | 1214 +++++++++++------ .../translator_vm/translator_selectors.hpp | 4 +- .../translator_vm/translator_verifier.cpp | 9 +- 10 files changed, 834 insertions(+), 794 deletions(-) delete mode 100644 barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.cpp diff --git a/barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_verifier.cpp b/barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_verifier.cpp index dcae727d639a..cc727a5d0aae 100644 --- a/barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_verifier.cpp @@ -243,8 +243,8 @@ typename BatchedHonkTranslatorVerifier_::ReductionResult BatchedHonkTrans }(); // Translator claim components (translator first: its masking poly must be at position 0 for Shplemini offset=2). - auto concat_shift_evals = TransFlavor::template reconstruct_concatenated_evaluations( - trans_evals, std::span(joint_challenge)); + auto concat_shift_evals = TranslatorFlavor::reconstruct_concatenated_evaluations( + trans_evals.get_groups_to_be_concatenated_shifted(), std::span(joint_challenge)); RefVector joint_unshifted_comms = trans_commitments.get_pcs_unshifted(); RefVector joint_unshifted_evals = trans_evals.get_pcs_unshifted(); diff --git a/barretenberg/cpp/src/barretenberg/circuit_checker/translator_circuit_checker.cpp b/barretenberg/cpp/src/barretenberg/circuit_checker/translator_circuit_checker.cpp index 6790c6f45c34..e6433f598aec 100644 --- a/barretenberg/cpp/src/barretenberg/circuit_checker/translator_circuit_checker.cpp +++ b/barretenberg/cpp/src/barretenberg/circuit_checker/translator_circuit_checker.cpp @@ -65,11 +65,12 @@ void TranslatorCircuitChecker::populate_values(const Builder& circuit, AllValues // ----------------------------------------------------------------------- // Shifted wire values (= values at next row, or zero at boundary). - // get_shifted() returns 86 refs: + // ShiftedEntities::get_all() returns 86 refs: // [0..79] : shifts of circuit.wires[1..80] (x_lo_y_hi through last range constraint) // [80..85] : shifts of ordered range constraints (0..4) and z_perm (not from circuit wires) // ----------------------------------------------------------------------- - auto shifted_refs = values.get_shifted(); + auto& shifted_base = static_cast&>(values); + auto shifted_refs = shifted_base.get_all(); if (row_idx + 1 < num_gates) { // circuit.wires[j+1] holds the wire that shifts to shifted_refs[j] for (size_t j = 0; j < 80; j++) { diff --git a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp index 7545f8cbe23f..c8fa282fe79f 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp @@ -95,7 +95,7 @@ template struct Precomput * * @tparam PrecomputedCommitments The precomputed entities containing VK commitments * @tparam HashType The field type for the precomputed hash (e.g., fr for both ECCVM and Translator) - * @tparam HardcodedVKAndHash Class containing static vk_hash() and either populate() or get_all() with hardcoded values + * @tparam HardcodedVKAndHash Class containing static vk_hash() and get_all() methods with hardcoded values */ template class FixedVKAndHash_ : public PrecomputedCommitments { @@ -104,17 +104,8 @@ class FixedVKAndHash_ : public PrecomputedCommitments { bool operator==(const FixedVKAndHash_&) const = default; - // Default construct the fixed VK from a hardcoded commitment populator and precomputed hash. + // Default construct the fixed VK from hardcoded commitments and precomputed hash FixedVKAndHash_() - requires requires(PrecomputedCommitments& commitments) { HardcodedVKAndHash::populate(commitments); } - : hash(HardcodedVKAndHash::vk_hash()) - { - HardcodedVKAndHash::populate(static_cast(*this)); - } - - // Default construct the fixed VK from hardcoded commitment vectors and precomputed hash. - FixedVKAndHash_() - requires(!requires(PrecomputedCommitments& commitments) { HardcodedVKAndHash::populate(commitments); }) : hash(HardcodedVKAndHash::vk_hash()) { for (auto [vk_commitment, fixed_commitment] : zip_view(this->get_all(), HardcodedVKAndHash::get_all())) { @@ -122,15 +113,6 @@ class FixedVKAndHash_ : public PrecomputedCommitments { } } - template - void populate_stdlib(Builder* builder, StdlibPrecomputedCommitments& commitments) const - requires requires(const PrecomputedCommitments& native_commitments) { - HardcodedVKAndHash::populate_stdlib(builder, commitments, native_commitments); - } - { - HardcodedVKAndHash::populate_stdlib(builder, commitments, static_cast(*this)); - } - HashType get_hash() const { return hash; } private: @@ -358,19 +340,6 @@ class FixedStdlibVKAndHash_ : public PrecomputedCommitments { * @brief Construct from native verification key and fix all witnesses (VK is constant for fixed circuits) */ FixedStdlibVKAndHash_(Builder* builder, const std::shared_ptr& native_key) - requires requires(const NativeVerificationKey& native_key, PrecomputedCommitments& commitments) { - native_key.populate_stdlib(builder, commitments); - } - : hash(FF::from_witness(builder, native_key->get_hash())) - { - native_key->populate_stdlib(builder, static_cast(*this)); - hash.fix_witness(); - } - - FixedStdlibVKAndHash_(Builder* builder, const std::shared_ptr& native_key) - requires(!requires(const NativeVerificationKey& native_key, PrecomputedCommitments& commitments) { - native_key.populate_stdlib(builder, commitments); - }) : hash(FF::from_witness(builder, native_key->get_hash())) { for (auto [native_comm, comm] : zip_view(native_key->get_all(), this->get_all())) { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp index e83d18bfa1b5..d567dab9e988 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp @@ -75,13 +75,6 @@ class TranslatorRecursiveFlavor { NativeFlavor::complete_full_circuit_evaluations(evals, full_circuit, challenge); } - template - static std::array reconstruct_concatenated_evaluations( - NativeFlavor::AllEntities& evals, std::span challenge) - { - return NativeFlavor::template reconstruct_concatenated_evaluations(evals, challenge); - } - using Relations = TranslatorFlavor::Relations_; static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); @@ -111,10 +104,11 @@ class TranslatorRecursiveFlavor { /** * @brief A container for the witness commitments. */ - using WitnessCommitments = TranslatorFlavor::AllEntities; + using WitnessCommitments = TranslatorFlavor::WitnessEntities; using CommitmentLabels = TranslatorFlavor::CommitmentLabels; - using VerifierCommitments = TranslatorFlavor::AllEntities; + // Reuse the VerifierCommitments from Translator + using VerifierCommitments = TranslatorFlavor::VerifierCommitments_; using Transcript = UltraStdlibTranscript; using VKAndHash = VKAndHash_; diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator.test.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator.test.cpp index b0e1b25cf330..6187dd7706b1 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator.test.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator.test.cpp @@ -352,15 +352,18 @@ TEST_F(TranslatorTests, FixedVK) auto proving_key = std::make_shared(circuit_builder); TranslatorProver prover{ proving_key, prover_transcript }; TranslatorFlavor::VerificationKey computed_vk = create_vk_from_proving_key(proving_key->proving_key); - - const auto& vk_commitment = computed_vk.ordered_extra_range_constraints_numerator; - const auto& fixed_commitment = fixed_vk.ordered_extra_range_constraints_numerator; - if (vk_commitment != fixed_commitment) { - info("// ordered_extra_range_constraints_numerator"); - info("Commitment(uint256_t(\"0x", vk_commitment.x, "\"),"); - info(" uint256_t(\"0x", vk_commitment.y, "\")),"); + auto labels = TranslatorFlavor::VerificationKey::get_labels(); + + size_t index = 0; + for (auto [vk_commitment, fixed_commitment] : zip_view(computed_vk.get_all(), fixed_vk.get_all())) { + if (vk_commitment != fixed_commitment) { + info("// ", labels[index]); + info("Commitment(uint256_t(\"0x", vk_commitment.x, "\"),"); + info(" uint256_t(\"0x", vk_commitment.y, "\")),"); + } + EXPECT_EQ(vk_commitment, fixed_commitment) << "Mismatch at label: " << labels[index]; + ++index; } - EXPECT_EQ(vk_commitment, fixed_commitment) << "Mismatch at label: ordered_extra_range_constraints_numerator"; EXPECT_EQ(computed_vk, fixed_vk); }; @@ -509,29 +512,6 @@ TEST_F(TranslatorTests, EvaluationPartition) EXPECT_EQ(remaining, 0UL); } -TEST_F(TranslatorTests, OpQueueSplitWiresAreOpenedUnshiftedAndShifted) -{ - using Flavor = TranslatorFlavor; - using FF = Flavor::FF; - - Flavor::AllEntities evals; - - std::set pcs_unshifted; - for (auto& entity : evals.get_pcs_unshifted()) { - pcs_unshifted.insert(&entity); - } - - std::set pcs_shift_sources; - for (auto& entity : evals.get_pcs_to_be_shifted()) { - pcs_shift_sources.insert(&entity); - } - - for (auto* op_queue_split_wire : { &evals.x_lo_y_hi, &evals.x_hi_z_1, &evals.y_lo_z_2 }) { - EXPECT_TRUE(pcs_unshifted.contains(op_queue_split_wire)); - EXPECT_TRUE(pcs_shift_sources.contains(op_queue_split_wire)); - } -} - /** * @brief Verify that the verifier-side methods populate every entity in AllEntities. * @details Start from all-zeros, call set_minicircuit_evaluations + complete_full_circuit_evaluations diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_fixed_vk.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_fixed_vk.hpp index ee07330a9c35..fa0aec7aea66 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_fixed_vk.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_fixed_vk.hpp @@ -6,7 +6,6 @@ #pragma once #include "barretenberg/common/ref_vector.hpp" -#include "barretenberg/common/zip_view.hpp" #include "barretenberg/ecc/curves/bn254/bn254.hpp" namespace bb { @@ -35,24 +34,6 @@ struct TranslatorHardcodedVKAndHash { uint256_t("0x14149055853422bf016065386e8ea0ffb9425b454048e1cd14cfdca457aa7e17")), }; } - - template static void populate(VKEntities& vk) - { - const auto values = get_all(); - size_t i = 0; - for (auto& field : vk.get_all()) { - field = values[i++]; - } - } - - template - static void populate_stdlib(Builder* builder, StdlibVKEntities& stdlib_vk, const NativeVKEntities& native_vk) - { - for (auto [stdlib_field, native_field] : zip_view(stdlib_vk.get_all(), native_vk.get_all())) { - stdlib_field = StdlibVKEntities::DataType::from_witness(builder, native_field); - stdlib_field.fix_witness(); - } - } }; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.cpp deleted file mode 100644 index 4f349cf16094..000000000000 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.cpp +++ /dev/null @@ -1,284 +0,0 @@ -#include "barretenberg/translator_vm/translator_flavor.hpp" - -#include "barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp" - -namespace bb { - -template -void TranslatorFlavor::compute_computable_precomputed(AllEntities& evals, std::span challenge) -{ - TranslatorSelectorEvaluations::compute(challenge).populate(evals); -} - -template -void TranslatorFlavor::set_minicircuit_evaluations(AllEntities& evals, - const std::array& mid) -{ - size_t src = 0; - for (auto& wire : evals.get_minicircuit_wires()) { - wire = mid[src++]; - } - for (auto& wire : evals.get_minicircuit_wires_shifted()) { - wire = mid[src++]; - } -} - -template -void TranslatorFlavor::complete_claimed_evaluations(AllEntities& evals, std::span challenge) -{ - compute_computable_precomputed(evals, challenge); - - FFType l0 = FFType(1); - for (size_t i = 0; i < CONST_TRANSLATOR_LOG_N - LOG_MINI_CIRCUIT_SIZE; i++) { - l0 *= (FFType(1) - challenge[LOG_MINI_CIRCUIT_SIZE + i]); - } - for (auto& wire : evals.get_minicircuit_wires()) { - wire *= l0; - } - for (auto& wire : evals.get_minicircuit_wires_shifted()) { - wire *= l0; - } -} - -template -void TranslatorFlavor::complete_full_circuit_evaluations( - AllEntities& evals, - const std::array& full_circuit, - std::span challenge) -{ - set_full_circuit_evaluations(evals, full_circuit); - complete_claimed_evaluations(evals, challenge); - - auto concat_evals = reconstruct_concatenated_evaluations(evals, challenge); - for (auto [ref, eval] : zip_view(evals.get_concatenated(), concat_evals)) { - ref = eval; - } -} - -template -std::array TranslatorFlavor::reconstruct_concatenated_evaluations( - AllEntities& evals, std::span challenge) -{ - static constexpr size_t NUM_TOP_BITS = numeric::get_msb(CONCATENATION_GROUP_SIZE); - - std::array lagrange_basis; - for (size_t j = 0; j < CONCATENATION_GROUP_SIZE; j++) { - lagrange_basis[j] = FFType(1); - for (size_t bit = 0; bit < NUM_TOP_BITS; bit++) { - const FFType& u = challenge[CONST_TRANSLATOR_LOG_N - NUM_TOP_BITS + bit]; - lagrange_basis[j] *= ((j >> bit) & 1) ? u : (FFType(1) - u); - } - } - FFType padding_inv = lagrange_basis[0].invert(); - - // Walk CONCAT_MAP: each row produces one concat-poly evaluation by accumulating its chunk - // wires against the lagrange basis. Zero-padded slots (non-range chunk has 13 wires < CONCATENATION_GROUP_SIZE) - // contribute zero and are simply not emitted. - std::array result; - size_t group_idx = 0; -#define ACCUMULATE_REF(name) \ - if constexpr (Shifted) { \ - acc += lagrange_basis[j] * evals.name##_shift; \ - } else { \ - acc += lagrange_basis[j] * evals.name; \ - } \ - ++j; -#define RECONSTRUCT_GROUP(concat_name, group_macro) \ - { \ - FFType acc(0); \ - size_t j = 0; \ - group_macro(ACCUMULATE_REF) result[group_idx++] = acc * padding_inv; \ - } - CONCAT_MAP(RECONSTRUCT_GROUP) -#undef ACCUMULATE_REF -#undef RECONSTRUCT_GROUP - return result; -} - -template -std::array TranslatorFlavor::get_full_circuit_evaluations( - AllEntities& evals) -{ - std::array result; - size_t dst = 0; - for (auto& entity : evals.get_full_circuit_entities()) { - result[dst++] = entity; - } - return result; -} - -template -void TranslatorFlavor::set_full_circuit_evaluations( - AllEntities& evals, const std::array& full_circuit) -{ - size_t src = 0; - for (auto& entity : evals.get_full_circuit_entities()) { - entity = full_circuit[src++]; - } -} - -template void TranslatorFlavor::compute_computable_precomputed( - AllEntities&, std::span); -template void TranslatorFlavor::set_minicircuit_evaluations( - AllEntities&, const std::array&); -template void TranslatorFlavor::complete_claimed_evaluations( - AllEntities&, std::span); -template void TranslatorFlavor::complete_full_circuit_evaluations( - AllEntities&, - const std::array&, - std::span); -template std::array TranslatorFlavor:: - reconstruct_concatenated_evaluations(AllEntities&, - std::span); -template std::array TranslatorFlavor:: - reconstruct_concatenated_evaluations(AllEntities&, - std::span); -template std::array TranslatorFlavor:: - get_full_circuit_evaluations(AllEntities&); -template void TranslatorFlavor::set_full_circuit_evaluations( - AllEntities&, const std::array&); - -template void TranslatorFlavor::compute_computable_precomputed( - AllEntities&, std::span); -template void TranslatorFlavor::set_minicircuit_evaluations( - AllEntities&, - const std::array&); -template void TranslatorFlavor::complete_claimed_evaluations( - AllEntities&, std::span); -template void TranslatorFlavor::complete_full_circuit_evaluations( - AllEntities&, - const std::array&, - std::span); -template std::array TranslatorFlavor:: - reconstruct_concatenated_evaluations( - AllEntities&, std::span); -template std::array TranslatorFlavor:: - reconstruct_concatenated_evaluations( - AllEntities&, std::span); -template void TranslatorFlavor::set_full_circuit_evaluations( - AllEntities&, - const std::array&); - -TranslatorFlavor::ProverPolynomials::ProverPolynomials() -{ - const size_t circuit_size = 1 << CONST_TRANSLATOR_LOG_N; - for (auto& ordered_range_constraint : get_ordered_range_constraints()) { - ordered_range_constraint = Polynomial{ /*size*/ circuit_size - 1, - /*largest possible index*/ circuit_size, - 1 }; - } - - // Initialize 5 concatenated polynomials (full circuit_size, shiftable with start_index=1) - // Row 0 of block 0 is the no-op row where all values are zero. - for (auto& concat_poly : get_concatenated()) { - concat_poly = Polynomial{ /*size*/ circuit_size - 1, - /*virtual_size*/ circuit_size, - /*start_index*/ 1 }; - } - z_perm = Polynomial{ /*size*/ circuit_size - 1, - /*virtual_size*/ circuit_size, - /*start_index*/ 1 }; - - op = Polynomial{ MINI_CIRCUIT_SIZE, circuit_size }; - - // All minicircuit wires (non-op-queue) are only non-zero in [1, MINI_CIRCUIT_SIZE) - for (auto& poly : this->get_minicircuit_wires()) { - if (poly.is_empty()) { - poly = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - 1, - /*virtual_size*/ circuit_size, - /*start_index*/ 1 }; - } - } - - // Op queue wires to be shifted - for (auto& poly : this->get_op_queue_split_wires()) { - if (poly.is_empty()) { - poly = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - 1, - /*virtual_size*/ circuit_size, - /*start_index*/ 1 }; - } - } - - // Initialize lagrange polynomials and the ordered extra range constraints numerator (the precomputed - // polynomials) within the appropriate range they operate on - lagrange_first = Polynomial{ /*size*/ 1, /*virtual_size*/ circuit_size }; - lagrange_result_row = Polynomial{ /*size*/ 1, /*virtual_size*/ circuit_size, /*start_index*/ RESULT_ROW }; - lagrange_even_in_minicircuit = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - RESULT_ROW - NUM_MASKED_ROWS_END, - /*virtual_size*/ circuit_size, - /*start_index=*/RESULT_ROW }; - lagrange_odd_in_minicircuit = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - RESULT_ROW - NUM_MASKED_ROWS_END - 1, - /*virtual_size*/ circuit_size, - /*start_index=*/RESULT_ROW + 1 }; - lagrange_last_in_minicircuit = Polynomial{ /*size*/ 1, - /*virtual_size*/ circuit_size, - /*start_index=*/MINI_CIRCUIT_SIZE - NUM_MASKED_ROWS_END - 1 }; - lagrange_mini_masking = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - RANDOMNESS_START, - /*virtual_size*/ circuit_size, - /*start_index=*/RANDOMNESS_START }; - // With concatenation, masking rows are scattered in concatenated polys: end of each of the 16 blocks - // Must span full circuit since values go up to position 15*MINI+(MINI-1) - lagrange_masking = Polynomial{ circuit_size, circuit_size }; - // Ordered masking: contiguous at the end (marks masking positions in ordered polynomials) - lagrange_ordered_masking = Polynomial{ /*size*/ MAX_RANDOM_VALUES_PER_ORDERED, - /*virtual_size*/ circuit_size, - /*start_index*/ circuit_size - MAX_RANDOM_VALUES_PER_ORDERED }; - lagrange_last = Polynomial{ /*size*/ 1, - /*virtual_size*/ circuit_size, - /*start_index*/ circuit_size - 1 }; - // lagrange_real_last marks the last position with sorted values in ordered polynomials - // (where we check maximum value = 2^14 - 1). With contiguous masking at the end, - // this is at position circuit_size - MAX_RANDOM_VALUES_PER_ORDERED - 1. - lagrange_real_last = Polynomial{ /*size*/ 1, - /*virtual_size*/ circuit_size, - /*start_index*/ circuit_size - MAX_RANDOM_VALUES_PER_ORDERED - 1 }; - ordered_extra_range_constraints_numerator = - Polynomial{ /*size*/ SORTED_STEPS_COUNT * NUM_CONCATENATED_POLYS + MASKING_OVERFLOW_COLUMN, - /*virtual_size*/ circuit_size, - /*start_index*/ 0 }; - - set_shifted(); -} - -TranslatorFlavor::AllValues TranslatorFlavor::ProverPolynomials::get_row(size_t row_idx) const -{ - AllValues result; - for (auto [result_field, polynomial] : zip_view(result.get_all(), this->get_all())) { - // Translator polynomials have different support regions (start_index/end_index) - // Return 0 for out-of-bounds access (which is the correct value outside support) - if (row_idx >= polynomial.start_index() && row_idx < polynomial.end_index()) { - result_field = polynomial[row_idx]; - } else { - result_field = FF(0); - } - } - return result; -} - -void TranslatorFlavor::ProverPolynomials::set_shifted() -{ - for (auto [shifted, to_be_shifted] : zip_view(get_shifted(), get_all_to_be_shifted())) { - shifted = to_be_shifted.shifted(); - } -} - -TranslatorFlavor::CommitmentLabels::CommitmentLabels() -{ - // Concatenated polynomials (sent via get_non_opqueue_wires_and_ordered_range_constraints) - this->concatenated_range_constraints_0 = "CONCATENATED_RANGE_CONSTRAINTS_0"; - this->concatenated_range_constraints_1 = "CONCATENATED_RANGE_CONSTRAINTS_1"; - this->concatenated_range_constraints_2 = "CONCATENATED_RANGE_CONSTRAINTS_2"; - this->concatenated_range_constraints_3 = "CONCATENATED_RANGE_CONSTRAINTS_3"; - this->concatenated_non_range = "CONCATENATED_NON_RANGE"; - - // Ordered range constraints (sent via get_non_opqueue_wires_and_ordered_range_constraints) - this->ordered_range_constraints_0 = "ORDERED_RANGE_CONSTRAINTS_0"; - this->ordered_range_constraints_1 = "ORDERED_RANGE_CONSTRAINTS_1"; - this->ordered_range_constraints_2 = "ORDERED_RANGE_CONSTRAINTS_2"; - this->ordered_range_constraints_3 = "ORDERED_RANGE_CONSTRAINTS_3"; - this->ordered_range_constraints_4 = "ORDERED_RANGE_CONSTRAINTS_4"; - - // Grand product (committed separately) - this->z_perm = "Z_PERM"; -} - -} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp index b4a45d5a7893..19859cd96f8d 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp @@ -30,283 +30,6 @@ namespace bb { -#define DEFINE_ENTITY_DECL(name) DataType name; -#define DEFINE_ENTITY_REF(name) name, -#define DEFINE_ENTITY_LABEL(name) #name, -#define DEFINE_ENTITY_COUNT(name) +1 - -#define DEFINE_SHIFTED_ENTITY_DECL(name) DataType name##_shift; -#define DEFINE_SHIFTED_ENTITY_REF(name) name##_shift, -#define DEFINE_SHIFTED_ENTITY_LABEL(name) #name "_shift", - -#define DEFINE_VIEW_FROM_LIST(method, LIST) \ - [[nodiscard]] auto method() \ - { \ - return RefArray, 0 LIST(DEFINE_ENTITY_COUNT)>{ LIST(DEFINE_ENTITY_REF) }; \ - } \ - [[nodiscard]] auto method() const \ - { \ - return RefArray, 0 LIST(DEFINE_ENTITY_COUNT)>{ LIST( \ - DEFINE_ENTITY_REF) }; \ - } - -#define DEFINE_SHIFTED_VIEW_FROM_LIST(method, LIST) \ - [[nodiscard]] auto method() \ - { \ - return RefArray, 0 LIST(DEFINE_ENTITY_COUNT)>{ LIST( \ - DEFINE_SHIFTED_ENTITY_REF) }; \ - } \ - [[nodiscard]] auto method() const \ - { \ - return RefArray, 0 LIST(DEFINE_ENTITY_COUNT)>{ LIST( \ - DEFINE_SHIFTED_ENTITY_REF) }; \ - } - -// Generate a flat RefArray view that mixes entities named directly (from UNSHIFTED_LIST) with entities -// named with the `_shift` suffix (from SHIFTED_LIST). Used for views like get_all / get_full_circuit_entities -// that span both unshifted and shifted entities. -#define DEFINE_MIXED_VIEW_FROM_LIST(method, UNSHIFTED_LIST, SHIFTED_LIST) \ - [[nodiscard]] auto method() \ - { \ - return RefArray, \ - 0 UNSHIFTED_LIST(DEFINE_ENTITY_COUNT) SHIFTED_LIST(DEFINE_ENTITY_COUNT)>{ UNSHIFTED_LIST( \ - DEFINE_ENTITY_REF) SHIFTED_LIST(DEFINE_SHIFTED_ENTITY_REF) }; \ - } \ - [[nodiscard]] auto method() const \ - { \ - return RefArray, \ - 0 UNSHIFTED_LIST(DEFINE_ENTITY_COUNT) SHIFTED_LIST(DEFINE_ENTITY_COUNT)>{ UNSHIFTED_LIST( \ - DEFINE_ENTITY_REF) SHIFTED_LIST(DEFINE_SHIFTED_ENTITY_REF) }; \ - } - -#define DEFINE_FIELDS_FROM_LIST(DataType, LIST) LIST(DEFINE_ENTITY_DECL) -#define DEFINE_SHIFTED_FIELDS_FROM_LIST(DataType, LIST) LIST(DEFINE_SHIFTED_ENTITY_DECL) - -#define LIST_SIZE(LIST) (0 LIST(DEFINE_ENTITY_COUNT)) - -#define PRECOMPUTED_COLUMNS(M) \ - M(ordered_extra_range_constraints_numerator) \ - M(lagrange_first) \ - M(lagrange_last) \ - M(lagrange_odd_in_minicircuit) \ - M(lagrange_even_in_minicircuit) \ - M(lagrange_result_row) \ - M(lagrange_last_in_minicircuit) \ - M(lagrange_masking) \ - M(lagrange_mini_masking) \ - M(lagrange_real_last) \ - M(lagrange_ordered_masking) - -#define VK_COLUMNS(M) M(ordered_extra_range_constraints_numerator) - -#define CONCATENATED_COLUMNS(M) \ - M(concatenated_range_constraints_0) \ - M(concatenated_range_constraints_1) \ - M(concatenated_range_constraints_2) \ - M(concatenated_range_constraints_3) \ - M(concatenated_non_range) - -#define OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ - M(x_lo_y_hi) \ - M(x_hi_z_1) \ - M(y_lo_z_2) - -#define ORDERED_RANGE_COLUMNS(M) \ - M(ordered_range_constraints_0) \ - M(ordered_range_constraints_1) \ - M(ordered_range_constraints_2) \ - M(ordered_range_constraints_3) \ - M(ordered_range_constraints_4) - -#define OP_QUEUE_WIRE_COLUMNS(M) M(op) - -#define GRAND_PRODUCT_COLUMNS(M) M(z_perm) - -// PCS column lists — each derived view textually contains the shared PCS_SHIFT_SOURCE_COLUMNS -// (and CONCATENATED_COLUMNS where applicable). The "every shift source is also opened -// unshifted" invariant is therefore structural: any entity added to PCS_SHIFT_SOURCE_COLUMNS -// flows automatically into both PCS_UNSHIFTED_COLUMNS and PCS_REPEATED_COLUMNS. - -// Polynomials whose unshifted commitment is registered in BOTH PCS batches because their relations read -// them at row i AND row i+1 (op-queue split wires via the decomposition relation; ordered/grand-product -// via their permutation/grand-product structure). These do NOT include concatenated polys (whose -// evaluations are reconstructed from minicircuit-wire evals rather than committed and sent directly). -#define PCS_SHIFT_SOURCE_COLUMNS(M) \ - OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ - ORDERED_RANGE_COLUMNS(M) \ - GRAND_PRODUCT_COLUMNS(M) - -// All polynomials registered in the shifted PCS batch (= every shift source plus the concatenated polys). -// 14 entries: op_queue_split(3) + ordered(5) + z_perm(1) + concat(5). -#define PCS_REPEATED_COLUMNS(M) \ - PCS_SHIFT_SOURCE_COLUMNS(M) \ - CONCATENATED_COLUMNS(M) - -// All unshifted PCS entities except the concatenated polys (whose evaluations the verifier reconstructs -// from minicircuit-wire evals rather than reading from the proof). Used as the unshifted-named prefix of -// get_full_circuit_entities. 12 entries: masking(1) + vk(1) + op(1) + shift_source(9). -#define PCS_UNSHIFTED_EXCLUDING_CONCAT_COLUMNS(M) \ - MASKING_COLUMNS(M) \ - VK_COLUMNS(M) \ - OP_QUEUE_WIRE_COLUMNS(M) \ - PCS_SHIFT_SOURCE_COLUMNS(M) - -// All unshifted PCS entities (excludes computable precomputed selectors). 17 entries: the prefix above -// plus concatenated(5). Contains PCS_REPEATED_COLUMNS as a contiguous suffix, structurally -// enforcing "to-be-shifted ⊆ unshifted". -#define PCS_UNSHIFTED_COLUMNS(M) \ - PCS_UNSHIFTED_EXCLUDING_CONCAT_COLUMNS(M) \ - CONCATENATED_COLUMNS(M) - -// All polynomials whose shift-by-1 enters Sumcheck. Layout: -// op_queue_split(3) + minicircuit(77) + ordered(5) + grand_product(1) = 86. -#define ALL_TO_BE_SHIFTED_COLUMNS(M) \ - OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ - NON_OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ - ORDERED_RANGE_COLUMNS(M) \ - GRAND_PRODUCT_COLUMNS(M) - -// Unshifted half of get_all: masking + precomputed + witness, contiguous prefix in the AllEntities layout. -#define NON_SHIFTED_COLUMNS(M) \ - MASKING_COLUMNS(M) \ - PRECOMPUTED_COLUMNS(M) \ - WITNESS_COLUMNS(M) - -// All wire columns (op-queue + minicircuit). Layout: op(1) + op_queue_split(3) + minicircuit(77) = 81. -#define WIRES_COLUMNS(M) \ - OP_QUEUE_WIRE_COLUMNS(M) \ - OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ - NON_OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) - -// Concatenated + ordered range (committed in the wire-round). Layout: concat(5) + ordered(5) = 10. -#define NON_OPQUEUE_WIRES_AND_ORDERED_RANGE_COLUMNS(M) \ - CONCATENATED_COLUMNS(M) \ - ORDERED_RANGE_COLUMNS(M) - -#define MASKING_COLUMNS(M) M(gemini_masking_poly) - -// ---- Concat-group chunks ---- -// Each chunk lists the minicircuit wires that fold into one concatenated polynomial. The "non_range" -// chunk lists 13 real wires; the runtime padder zeros it out to CONCATENATION_GROUP_SIZE. -#define CONCAT_GROUP_NON_RANGE(M) \ - M(p_x_low_limbs) \ - M(p_x_high_limbs) \ - M(p_y_low_limbs) \ - M(p_y_high_limbs) \ - M(z_low_limbs) \ - M(z_high_limbs) \ - M(accumulators_binary_limbs_0) \ - M(accumulators_binary_limbs_1) \ - M(accumulators_binary_limbs_2) \ - M(accumulators_binary_limbs_3) \ - M(quotient_low_binary_limbs) \ - M(quotient_high_binary_limbs) \ - M(relation_wide_limbs) - -#define CONCAT_GROUP_RANGE_0(M) \ - M(p_x_low_limbs_range_constraint_0) \ - M(p_x_low_limbs_range_constraint_1) \ - M(p_x_low_limbs_range_constraint_2) \ - M(p_x_low_limbs_range_constraint_3) \ - M(p_x_low_limbs_range_constraint_4) \ - M(p_x_low_limbs_range_constraint_tail) \ - M(p_x_high_limbs_range_constraint_0) \ - M(p_x_high_limbs_range_constraint_1) \ - M(p_x_high_limbs_range_constraint_2) \ - M(p_x_high_limbs_range_constraint_3) \ - M(p_x_high_limbs_range_constraint_4) \ - M(p_x_high_limbs_range_constraint_tail) \ - M(p_y_low_limbs_range_constraint_0) \ - M(p_y_low_limbs_range_constraint_1) \ - M(p_y_low_limbs_range_constraint_2) \ - M(p_y_low_limbs_range_constraint_3) - -#define CONCAT_GROUP_RANGE_1(M) \ - M(p_y_low_limbs_range_constraint_4) \ - M(p_y_low_limbs_range_constraint_tail) \ - M(p_y_high_limbs_range_constraint_0) \ - M(p_y_high_limbs_range_constraint_1) \ - M(p_y_high_limbs_range_constraint_2) \ - M(p_y_high_limbs_range_constraint_3) \ - M(p_y_high_limbs_range_constraint_4) \ - M(p_y_high_limbs_range_constraint_tail) \ - M(z_low_limbs_range_constraint_0) \ - M(z_low_limbs_range_constraint_1) \ - M(z_low_limbs_range_constraint_2) \ - M(z_low_limbs_range_constraint_3) \ - M(z_low_limbs_range_constraint_4) \ - M(z_low_limbs_range_constraint_tail) \ - M(z_high_limbs_range_constraint_0) \ - M(z_high_limbs_range_constraint_1) - -#define CONCAT_GROUP_RANGE_2(M) \ - M(z_high_limbs_range_constraint_2) \ - M(z_high_limbs_range_constraint_3) \ - M(z_high_limbs_range_constraint_4) \ - M(z_high_limbs_range_constraint_tail) \ - M(accumulator_low_limbs_range_constraint_0) \ - M(accumulator_low_limbs_range_constraint_1) \ - M(accumulator_low_limbs_range_constraint_2) \ - M(accumulator_low_limbs_range_constraint_3) \ - M(accumulator_low_limbs_range_constraint_4) \ - M(accumulator_low_limbs_range_constraint_tail) \ - M(accumulator_high_limbs_range_constraint_0) \ - M(accumulator_high_limbs_range_constraint_1) \ - M(accumulator_high_limbs_range_constraint_2) \ - M(accumulator_high_limbs_range_constraint_3) \ - M(accumulator_high_limbs_range_constraint_4) \ - M(accumulator_high_limbs_range_constraint_tail) - -#define CONCAT_GROUP_RANGE_3(M) \ - M(quotient_low_limbs_range_constraint_0) \ - M(quotient_low_limbs_range_constraint_1) \ - M(quotient_low_limbs_range_constraint_2) \ - M(quotient_low_limbs_range_constraint_3) \ - M(quotient_low_limbs_range_constraint_4) \ - M(quotient_low_limbs_range_constraint_tail) \ - M(quotient_high_limbs_range_constraint_0) \ - M(quotient_high_limbs_range_constraint_1) \ - M(quotient_high_limbs_range_constraint_2) \ - M(quotient_high_limbs_range_constraint_3) \ - M(quotient_high_limbs_range_constraint_4) \ - M(quotient_high_limbs_range_constraint_tail) \ - M(relation_wide_limbs_range_constraint_0) \ - M(relation_wide_limbs_range_constraint_1) \ - M(relation_wide_limbs_range_constraint_2) \ - M(relation_wide_limbs_range_constraint_3) - -// Single source of truth: each row pairs a concatenated polynomial name with its chunk macro. -// Iteration order matches the output order of get_groups_to_be_concatenated(). -#define CONCAT_MAP(ROW) \ - ROW(concatenated_range_constraints_0, CONCAT_GROUP_RANGE_0) \ - ROW(concatenated_range_constraints_1, CONCAT_GROUP_RANGE_1) \ - ROW(concatenated_range_constraints_2, CONCAT_GROUP_RANGE_2) \ - ROW(concatenated_range_constraints_3, CONCAT_GROUP_RANGE_3) \ - ROW(concatenated_non_range, CONCAT_GROUP_NON_RANGE) - -// Flat list of all 77 minicircuit wires. Order: non-range first, then range groups in order — matches -// the historical AllEntities field layout. -#define NON_OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ - CONCAT_GROUP_NON_RANGE(M) \ - CONCAT_GROUP_RANGE_0(M) \ - CONCAT_GROUP_RANGE_1(M) \ - CONCAT_GROUP_RANGE_2(M) \ - CONCAT_GROUP_RANGE_3(M) - -#define WITNESS_COLUMNS(M) \ - OP_QUEUE_WIRE_COLUMNS(M) \ - OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ - NON_OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ - ORDERED_RANGE_COLUMNS(M) \ - GRAND_PRODUCT_COLUMNS(M) \ - CONCATENATED_COLUMNS(M) - -#define SHIFTED_COLUMNS(M) \ - OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ - NON_OP_QUEUE_SHIFT_SOURCE_COLUMNS(M) \ - ORDERED_RANGE_COLUMNS(M) \ - GRAND_PRODUCT_COLUMNS(M) - class TranslatorFlavor { public: @@ -423,98 +146,519 @@ class TranslatorFlavor { static constexpr size_t num_frs_fr = FrCodec::calc_num_fields(); static constexpr size_t num_frs_fq = FrCodec::calc_num_fields(); - template class AllEntities { + /** + * @brief A base class labelling precomputed entities and (ordered) subsets of interest. + * @details Used to build the proving key and verification key. + */ + template class PrecomputedEntities { public: + bool operator==(const PrecomputedEntities& other) const = default; using DataType = DataType_; - DEFINE_FIELDS_FROM_LIST(DataType, MASKING_COLUMNS) - DEFINE_FIELDS_FROM_LIST(DataType, PRECOMPUTED_COLUMNS) - DEFINE_FIELDS_FROM_LIST(DataType, WITNESS_COLUMNS) - DEFINE_SHIFTED_FIELDS_FROM_LIST(DataType, SHIFTED_COLUMNS) - - // ---- Public views consumed by the prover/verifier and external flavor framework ---- - DEFINE_VIEW_FROM_LIST(get_precomputed, PRECOMPUTED_COLUMNS) - DEFINE_VIEW_FROM_LIST(get_witness, WITNESS_COLUMNS) - DEFINE_VIEW_FROM_LIST(get_concatenated, CONCATENATED_COLUMNS) - DEFINE_VIEW_FROM_LIST(get_op_queue_split_wires, OP_QUEUE_SHIFT_SOURCE_COLUMNS) - DEFINE_VIEW_FROM_LIST(get_minicircuit_wires, NON_OP_QUEUE_SHIFT_SOURCE_COLUMNS) - DEFINE_VIEW_FROM_LIST(get_ordered_range_constraints, ORDERED_RANGE_COLUMNS) - DEFINE_VIEW_FROM_LIST(get_wires, WIRES_COLUMNS) - DEFINE_VIEW_FROM_LIST(get_all_to_be_shifted, ALL_TO_BE_SHIFTED_COLUMNS) - DEFINE_VIEW_FROM_LIST(get_non_opqueue_wires_and_ordered_range_constraints, - NON_OPQUEUE_WIRES_AND_ORDERED_RANGE_COLUMNS) - - DEFINE_SHIFTED_VIEW_FROM_LIST(get_shifted, SHIFTED_COLUMNS) - DEFINE_SHIFTED_VIEW_FROM_LIST(get_minicircuit_wires_shifted, NON_OP_QUEUE_SHIFT_SOURCE_COLUMNS) - DEFINE_SHIFTED_VIEW_FROM_LIST(get_pcs_shifted, PCS_SHIFT_SOURCE_COLUMNS) + DEFINE_FLAVOR_MEMBERS(DataType, + ordered_extra_range_constraints_numerator, // column 0 + lagrange_first, // column 1 + lagrange_last, // column 2 + lagrange_odd_in_minicircuit, // column 3 + lagrange_even_in_minicircuit, // column 4 + lagrange_result_row, // column 5 + lagrange_last_in_minicircuit, // column 6 + lagrange_masking, // column 7 + lagrange_mini_masking, // column 8 + lagrange_real_last, // column 9 + lagrange_ordered_masking); // column 10 + }; + + template class ConcatenatedPolynomials { + public: + DEFINE_FLAVOR_MEMBERS(DataType, + concatenated_range_constraints_0, // column 0 + concatenated_range_constraints_1, // column 1 + concatenated_range_constraints_2, // column 2 + concatenated_range_constraints_3, // column 3 + concatenated_non_range) // column 4 + }; + /** + * @brief Non-range main wires (13 wires that go into concatenated group 4) + */ + template class NonRangeMainWires { + public: + DEFINE_FLAVOR_MEMBERS(DataType, + p_x_low_limbs, // column 0 + p_x_high_limbs, // column 1 + p_y_low_limbs, // column 2 + p_y_high_limbs, // column 3 + z_low_limbs, // column 4 + z_high_limbs, // column 5 + accumulators_binary_limbs_0, // column 6 + accumulators_binary_limbs_1, // column 7 + accumulators_binary_limbs_2, // column 8 + accumulators_binary_limbs_3, // column 9 + quotient_low_binary_limbs, // column 10 + quotient_high_binary_limbs, // column 11 + relation_wide_limbs) // column 12 + }; + + /** + * @brief Range constraint wires (64 wires that go into concatenated groups 0-3) + */ + template class RangeConstraintWires { + public: + DEFINE_FLAVOR_MEMBERS(DataType, + p_x_low_limbs_range_constraint_0, // column 0 + p_x_low_limbs_range_constraint_1, // column 17 + p_x_low_limbs_range_constraint_2, // column 18 + p_x_low_limbs_range_constraint_3, // column 19 + p_x_low_limbs_range_constraint_4, // column 20 + p_x_low_limbs_range_constraint_tail, // column 21 + p_x_high_limbs_range_constraint_0, // column 22 + p_x_high_limbs_range_constraint_1, // column 23 + p_x_high_limbs_range_constraint_2, // column 24 + p_x_high_limbs_range_constraint_3, // column 25 + p_x_high_limbs_range_constraint_4, // column 26 + p_x_high_limbs_range_constraint_tail, // column 27 + p_y_low_limbs_range_constraint_0, // column 28 + p_y_low_limbs_range_constraint_1, // column 29 + p_y_low_limbs_range_constraint_2, // column 30 + p_y_low_limbs_range_constraint_3, // column 31 + p_y_low_limbs_range_constraint_4, // column 32 + p_y_low_limbs_range_constraint_tail, // column 33 + p_y_high_limbs_range_constraint_0, // column 34 + p_y_high_limbs_range_constraint_1, // column 35 + p_y_high_limbs_range_constraint_2, // column 36 + p_y_high_limbs_range_constraint_3, // column 37 + p_y_high_limbs_range_constraint_4, // column 38 + p_y_high_limbs_range_constraint_tail, // column 39 + z_low_limbs_range_constraint_0, // column 40 + z_low_limbs_range_constraint_1, // column 41 + z_low_limbs_range_constraint_2, // column 42 + z_low_limbs_range_constraint_3, // column 43 + z_low_limbs_range_constraint_4, // column 44 + z_low_limbs_range_constraint_tail, // column 45 + z_high_limbs_range_constraint_0, // column 46 + z_high_limbs_range_constraint_1, // column 47 + z_high_limbs_range_constraint_2, // column 48 + z_high_limbs_range_constraint_3, // column 49 + z_high_limbs_range_constraint_4, // column 50 + z_high_limbs_range_constraint_tail, // column 51 + accumulator_low_limbs_range_constraint_0, // column 52 + accumulator_low_limbs_range_constraint_1, // column 53 + accumulator_low_limbs_range_constraint_2, // column 54 + accumulator_low_limbs_range_constraint_3, // column 55 + accumulator_low_limbs_range_constraint_4, // column 56 + accumulator_low_limbs_range_constraint_tail, // column 57 + accumulator_high_limbs_range_constraint_0, // column 58 + accumulator_high_limbs_range_constraint_1, // column 59 + accumulator_high_limbs_range_constraint_2, // column 60 + accumulator_high_limbs_range_constraint_3, // column 61 + accumulator_high_limbs_range_constraint_4, // column 62 + accumulator_high_limbs_range_constraint_tail, // column 63 + quotient_low_limbs_range_constraint_0, // column 64 + quotient_low_limbs_range_constraint_1, // column 65 + quotient_low_limbs_range_constraint_2, // column 66 + quotient_low_limbs_range_constraint_3, // column 67 + quotient_low_limbs_range_constraint_4, // column 68 + quotient_low_limbs_range_constraint_tail, // column 69 + quotient_high_limbs_range_constraint_0, // column 70 + quotient_high_limbs_range_constraint_1, // column 71 + quotient_high_limbs_range_constraint_2, // column 72 + quotient_high_limbs_range_constraint_3, // column 73 + quotient_high_limbs_range_constraint_4, // column 74 + quotient_high_limbs_range_constraint_tail, // column 75 + relation_wide_limbs_range_constraint_0, // column 76 + relation_wide_limbs_range_constraint_1, // column 77 + relation_wide_limbs_range_constraint_2, // column 62 + relation_wide_limbs_range_constraint_3); // column 63 + }; + + /** + * @brief All non-op-queue wires that need to be shifted (composed of non-range main + range constraint) + */ + template + class NonOpQueueWiresToBeShiftedEntities : public NonRangeMainWires, + public RangeConstraintWires { + public: + DEFINE_COMPOUND_GET_ALL(NonRangeMainWires, RangeConstraintWires) + }; + + /** + * @brief Op queue wires (to be shifted): first 3 wires of the to-be-shifted group + */ + template class OpQueueWiresToBeShiftedEntities { + public: + DEFINE_FLAVOR_MEMBERS(DataType, + x_lo_y_hi, // column 0 + x_hi_z_1, // column 1 + y_lo_z_2) // column 2 + }; + + /** + * @brief All wires to be shifted (op queue + non-op-queue) + */ + template + class WireToBeShiftedEntities : public OpQueueWiresToBeShiftedEntities, + public NonOpQueueWiresToBeShiftedEntities { + public: + DEFINE_COMPOUND_GET_ALL(OpQueueWiresToBeShiftedEntities, NonOpQueueWiresToBeShiftedEntities) + }; + + // Note: These are technically derived from wires but do not depend on challenges (like z_perm). They are committed + // to in the wires commitment round. + template class OrderedRangeConstraints { + public: + DEFINE_FLAVOR_MEMBERS(DataType, + ordered_range_constraints_0, // column 0 + ordered_range_constraints_1, // column 1 + ordered_range_constraints_2, // column 2 + ordered_range_constraints_3, // column 3 + ordered_range_constraints_4); // column 4 + }; + + /** + * @brief Op queue wires (non-shifted): these represent the op queue and are provided by the merge protocol + */ + template class OpQueueWireNonshiftedEntities { + public: + DEFINE_FLAVOR_MEMBERS(DataType, + op // column 0 + ); + }; + + /** + * @brief All wire entities that are not shifted (currently just the op queue wire) + */ + template class WireNonshiftedEntities : public OpQueueWireNonshiftedEntities { + public: + DEFINE_COMPOUND_GET_ALL(OpQueueWireNonshiftedEntities) + }; + + template class DerivedWitnessEntities { + public: + DEFINE_FLAVOR_MEMBERS(DataType, + z_perm); // column 0 + }; + /** + * @brief Container for all witness polynomials used/constructed by the prover. + */ + template + class WitnessEntities : public WireNonshiftedEntities, + public WireToBeShiftedEntities, + public OrderedRangeConstraints, + public DerivedWitnessEntities, + public ConcatenatedPolynomials { + public: + DEFINE_COMPOUND_GET_ALL(WireNonshiftedEntities, + WireToBeShiftedEntities, + OrderedRangeConstraints, + DerivedWitnessEntities, + ConcatenatedPolynomials) /** - * @brief All unshifted polynomials for PCS (excludes computable precomputed). - * @details masking(1) + vk_precomputed(1) + op_queue_wire(1) + op_queue_split(3) + ordered(5) - * + grand_product(1) + concat(5) = 17. The trailing 14 entries (PCS_REPEATED_COLUMNS) - * are also registered as shift sources — see get_pcs_to_be_shifted. + * @brief Entities constructed from circuit data. + * */ - DEFINE_VIEW_FROM_LIST(get_pcs_unshifted, PCS_UNSHIFTED_COLUMNS) + auto get_wires() + { + return concatenate(WireNonshiftedEntities::get_all(), + WireToBeShiftedEntities::get_all()); + }; /** - * @brief All to-be-shifted polynomials for PCS. Each entity here also lives in get_pcs_unshifted - * (PCS_REPEATED_COLUMNS is a contiguous suffix of PCS_UNSHIFTED_COLUMNS). + * @brief Concatenated polynomials and ordered range constraints (committed to by translator prover). + * @details 5 concatenated + 5 ordered = 10 commitments. */ - DEFINE_VIEW_FROM_LIST(get_pcs_to_be_shifted, PCS_REPEATED_COLUMNS) + auto get_non_opqueue_wires_and_ordered_range_constraints() + { + return concatenate(ConcatenatedPolynomials::get_all(), + OrderedRangeConstraints::get_all()); + }; - DEFINE_MIXED_VIEW_FROM_LIST(get_all, NON_SHIFTED_COLUMNS, SHIFTED_COLUMNS) - constexpr std::size_t size() const { return get_all().size(); } - static const std::vector& get_labels() + /** + * @brief All polys that need shifted views for Sumcheck (corresponds 1:1 with ShiftedEntities). + * @details WireToBeShifted(80) + OrderedRangeConstraints(5) + DerivedWitness(1) = 86 + */ + auto get_all_to_be_shifted() { - static const auto labels = - concatenate(std::vector{ NON_SHIFTED_COLUMNS(DEFINE_ENTITY_LABEL) }, - std::vector{ SHIFTED_COLUMNS(DEFINE_SHIFTED_ENTITY_LABEL) }); - return labels; - } + return concatenate(WireToBeShiftedEntities::get_all(), + OrderedRangeConstraints::get_all(), + DerivedWitnessEntities::get_all()); + }; /** - * @brief Full-circuit entities sent in the proof (excludes computable precomputed, minicircuit wires, - * and concatenated polys whose evals are reconstructed from wire evals). - * @details PCS_UNSHIFTED_EXCLUDING_CONCAT_COLUMNS (12, unshifted-named) + PCS_SHIFT_SOURCE_COLUMNS - * (9, `_shift`-suffixed) = 21. + * @brief Get the concatenated polynomials. */ - DEFINE_MIXED_VIEW_FROM_LIST(get_full_circuit_entities, - PCS_UNSHIFTED_EXCLUDING_CONCAT_COLUMNS, - PCS_SHIFT_SOURCE_COLUMNS) + auto get_concatenated() { return ConcatenatedPolynomials::get_all(); } - // Build the partition driven by CONCAT_MAP. Each row of the map declares a concatenated - // polynomial together with the chunk macro listing its constituent minicircuit wires. The chunks - // are emitted directly into a RefVector and zero-padded to CONCATENATION_GROUP_SIZE. + /** + * @brief Get all minicircuit wire polynomials that are concatenated into the 5 concatenated polys. + * @details Returns 5 groups of 16 wires each. Groups 0-3 are range constraint wires; group 4 is + * 13 non-range main wires + 3 null padding slots. + */ std::vector> get_groups_to_be_concatenated() { static DataType zero_value = DataType(0); - std::vector> groups; -#define PUSH_REF(name) group.push_back(name); -#define PUSH_GROUP(concat_name, group_macro) \ - { \ - RefVector group; \ - group_macro(PUSH_REF) while (group.size() < CONCATENATION_GROUP_SIZE) group.push_back(zero_value); \ - groups.emplace_back(std::move(group)); \ - } - CONCAT_MAP(PUSH_GROUP) -#undef PUSH_REF -#undef PUSH_GROUP - return groups; + return partition_minicircuit_wires_into_groups( + NonOpQueueWiresToBeShiftedEntities::get_all(), zero_value); + }; + }; + + /** + * @brief Op queue shifted entities (mirrors OpQueueWiresToBeShiftedEntities) + */ + template class OpQueueShiftedEntities { + public: + DEFINE_FLAVOR_MEMBERS(DataType, + x_lo_y_hi_shift, // column 0 + x_hi_z_1_shift, // column 1 + y_lo_z_2_shift) // column 2 + }; + + /** + * @brief Non-op-queue minicircuit wire shifted entities (mirrors NonOpQueueWiresToBeShiftedEntities) + */ + template class NonOpQueueShiftedEntities { + public: + DEFINE_FLAVOR_MEMBERS(DataType, + p_x_low_limbs_shift, // column 3 + p_x_high_limbs_shift, // column 10 + p_y_low_limbs_shift, // column 17 + p_y_high_limbs_shift, // column 24 + z_low_limbs_shift, // column 31 + z_high_limbs_shift, // column 38 + accumulators_binary_limbs_0_shift, // column 45 + accumulators_binary_limbs_1_shift, // column 46 + accumulators_binary_limbs_2_shift, // column 47 + accumulators_binary_limbs_3_shift, // column 48 + quotient_low_binary_limbs_shift, // column 61 + quotient_high_binary_limbs_shift, // column 62 + relation_wide_limbs_shift, // column 75 + p_x_low_limbs_range_constraint_0_shift, // column 4 + p_x_low_limbs_range_constraint_1_shift, // column 5 + p_x_low_limbs_range_constraint_2_shift, // column 6 + p_x_low_limbs_range_constraint_3_shift, // column 7 + p_x_low_limbs_range_constraint_4_shift, // column 8 + p_x_low_limbs_range_constraint_tail_shift, // column 9 + p_x_high_limbs_range_constraint_0_shift, // column 11 + p_x_high_limbs_range_constraint_1_shift, // column 12 + p_x_high_limbs_range_constraint_2_shift, // column 13 + p_x_high_limbs_range_constraint_3_shift, // column 14 + p_x_high_limbs_range_constraint_4_shift, // column 15 + p_x_high_limbs_range_constraint_tail_shift, // column 16 + p_y_low_limbs_range_constraint_0_shift, // column 18 + p_y_low_limbs_range_constraint_1_shift, // column 19 + p_y_low_limbs_range_constraint_2_shift, // column 20 + p_y_low_limbs_range_constraint_3_shift, // column 21 + p_y_low_limbs_range_constraint_4_shift, // column 22 + p_y_low_limbs_range_constraint_tail_shift, // column 23 + p_y_high_limbs_range_constraint_0_shift, // column 25 + p_y_high_limbs_range_constraint_1_shift, // column 26 + p_y_high_limbs_range_constraint_2_shift, // column 27 + p_y_high_limbs_range_constraint_3_shift, // column 28 + p_y_high_limbs_range_constraint_4_shift, // column 29 + p_y_high_limbs_range_constraint_tail_shift, // column 30 + z_low_limbs_range_constraint_0_shift, // column 32 + z_low_limbs_range_constraint_1_shift, // column 33 + z_low_limbs_range_constraint_2_shift, // column 34 + z_low_limbs_range_constraint_3_shift, // column 35 + z_low_limbs_range_constraint_4_shift, // column 36 + z_low_limbs_range_constraint_tail_shift, // column 37 + z_high_limbs_range_constraint_0_shift, // column 39 + z_high_limbs_range_constraint_1_shift, // column 40 + z_high_limbs_range_constraint_2_shift, // column 41 + z_high_limbs_range_constraint_3_shift, // column 42 + z_high_limbs_range_constraint_4_shift, // column 43 + z_high_limbs_range_constraint_tail_shift, // column 44 + accumulator_low_limbs_range_constraint_0_shift, // column 49 + accumulator_low_limbs_range_constraint_1_shift, // column 50 + accumulator_low_limbs_range_constraint_2_shift, // column 51 + accumulator_low_limbs_range_constraint_3_shift, // column 52 + accumulator_low_limbs_range_constraint_4_shift, // column 53 + accumulator_low_limbs_range_constraint_tail_shift, // column 54 + accumulator_high_limbs_range_constraint_0_shift, // column 55 + accumulator_high_limbs_range_constraint_1_shift, // column 56 + accumulator_high_limbs_range_constraint_2_shift, // column 57 + accumulator_high_limbs_range_constraint_3_shift, // column 58 + accumulator_high_limbs_range_constraint_4_shift, // column 59 + accumulator_high_limbs_range_constraint_tail_shift, // column 60 + quotient_low_limbs_range_constraint_0_shift, // column 63 + quotient_low_limbs_range_constraint_1_shift, // column 64 + quotient_low_limbs_range_constraint_2_shift, // column 65 + quotient_low_limbs_range_constraint_3_shift, // column 66 + quotient_low_limbs_range_constraint_4_shift, // column 67 + quotient_low_limbs_range_constraint_tail_shift, // column 68 + quotient_high_limbs_range_constraint_0_shift, // column 69 + quotient_high_limbs_range_constraint_1_shift, // column 70 + quotient_high_limbs_range_constraint_2_shift, // column 71 + quotient_high_limbs_range_constraint_3_shift, // column 72 + quotient_high_limbs_range_constraint_4_shift, // column 73 + quotient_high_limbs_range_constraint_tail_shift, // column 74 + relation_wide_limbs_range_constraint_0_shift, // column 76 + relation_wide_limbs_range_constraint_1_shift, // column 77 + relation_wide_limbs_range_constraint_2_shift, // column 78 + relation_wide_limbs_range_constraint_3_shift) // column 79 + }; + + /** + * @brief Ordered range constraint + z_perm shifted entities + */ + template class DerivedShiftedEntities { + public: + DEFINE_FLAVOR_MEMBERS(DataType, + ordered_range_constraints_0_shift, // column 80 + ordered_range_constraints_1_shift, // column 81 + ordered_range_constraints_2_shift, // column 82 + ordered_range_constraints_3_shift, // column 83 + ordered_range_constraints_4_shift, // column 84 + z_perm_shift) // column 85 + }; + + /** + * @brief Represents polynomials shifted by 1 or their evaluations, defined relative to WireToBeShiftedEntities. + */ + template + class ShiftedEntities : public OpQueueShiftedEntities, + public NonOpQueueShiftedEntities, + public DerivedShiftedEntities { + public: + DEFINE_COMPOUND_GET_ALL(OpQueueShiftedEntities, + NonOpQueueShiftedEntities, + DerivedShiftedEntities) + + /** + * @brief PCS-level shifted evaluations matching get_to_be_shifted(): + * op_queue(3) + ordered_range(5) + z_perm(1) = 9 + */ + auto get_pcs_shifted() + { + return concatenate(OpQueueShiftedEntities::get_all(), + DerivedShiftedEntities::get_all()); } + + /** + * @brief Get the shifted versions of minicircuit wires organized into 5 concatenation groups. + * @details Mirrors get_groups_to_be_concatenated() but with shifted wire entities. + */ std::vector> get_groups_to_be_concatenated_shifted() { static DataType zero_value = DataType(0); - std::vector> groups; -#define PUSH_SHIFT_REF(name) group.push_back(name##_shift); -#define PUSH_SHIFT_GROUP(concat_name, group_macro) \ - { \ - RefVector group; \ - group_macro(PUSH_SHIFT_REF) while (group.size() < CONCATENATION_GROUP_SIZE) group.push_back(zero_value); \ - groups.emplace_back(std::move(group)); \ - } - CONCAT_MAP(PUSH_SHIFT_GROUP) -#undef PUSH_SHIFT_REF -#undef PUSH_SHIFT_GROUP - return groups; + return partition_minicircuit_wires_into_groups(NonOpQueueShiftedEntities::get_all(), + zero_value); + }; + }; + + /** + * @brief Container for ZK entities (gemini masking polynomial for ZK-PCS) + * @details Translator is always ZK, so this always contains the masking polynomial + */ + template class MaskingEntities { + public: + DEFINE_FLAVOR_MEMBERS(DataType, gemini_masking_poly) + }; + + /** + * @brief A base class labelling all entities (for instance, all of the polynomials used by the prover during + * sumcheck) in this Honk variant along with particular subsets of interest. + * @details Used to build containers for: the prover's polynomial during sumcheck; the sumcheck's folded + * polynomials; the univariates consturcted during during sumcheck; the evaluations produced by sumcheck. + * + * Symbolically we have: AllEntities = PrecomputedEntities + WitnessEntities + ShiftedEntities + MaskingEntities. + */ + template + class AllEntities : public MaskingEntities, + public PrecomputedEntities, + public WitnessEntities, + public ShiftedEntities { + public: + DEFINE_COMPOUND_GET_ALL(MaskingEntities, + PrecomputedEntities, + WitnessEntities, + ShiftedEntities) + + /** + * @brief Getter for concatenated polynomials + */ + auto get_concatenated() { return ConcatenatedPolynomials::get_all(); }; + + /** + * @brief Getter for the ordered entities used in computing the denominator of the grand product in the + * permutation relation. + */ + auto get_ordered_range_constraints() { return OrderedRangeConstraints::get_all(); }; + + /** + * @brief All unshifted polynomials for PCS (excludes computable precomputed, includes concatenated). + * @details masking(1) + ordered_extra(1) + op(1) + op_queue_tbs(3) + ordered(5) + z_perm(1) + concat(5) = 17 + * + * The op-queue to-be-shifted wires (x_lo_y_hi, x_hi_z_1, y_lo_z_2) appear here in addition to + * get_pcs_to_be_shifted because the decomposition relation reads them both unshifted (e.g. `x_lo`) + * and shift-by-1 (e.g. `y_hi`) at the same row. Without registering the unshifted opening, the + * unshifted MLE evaluations at the sumcheck point would be unconstrained. + */ + auto get_pcs_unshifted() + { + return concatenate( + MaskingEntities::get_all(), // gemini_masking_poly + RefArray{ this->ordered_extra_range_constraints_numerator }, // non-computable precomputed + WireNonshiftedEntities::get_all(), // op (from merge protocol) + OpQueueWiresToBeShiftedEntities::get_all(), // x_lo_y_hi, x_hi_z_1, y_lo_z_2 + OrderedRangeConstraints::get_all(), // ordered_0..4 + DerivedWitnessEntities::get_all(), // z_perm + ConcatenatedPolynomials::get_all()); // concat_0..4 + } + + /** + * @brief All to-be-shifted polynomials for PCS (base to-be-shifted + concatenated). + * @details op_queue_shifted(3) + ordered(5) + z_perm(1) + concat(5) = 14 + */ + auto get_pcs_to_be_shifted() + { + return concatenate(OpQueueWiresToBeShiftedEntities::get_all(), // x_lo_y_hi, x_hi_z_1, y_lo_z_2 + OrderedRangeConstraints::get_all(), // ordered_0..4 + DerivedWitnessEntities::get_all(), // z_perm + ConcatenatedPolynomials::get_all()); // concat_0..4 + } + + auto get_shifted() { return ShiftedEntities::get_all(); }; + auto get_pcs_shifted() { return ShiftedEntities::get_pcs_shifted(); }; + + /** + * @brief Full-circuit entities sent in the proof (excludes computable precomputed, minicircuit wires, + * and concatenated polys whose evals are reconstructed from wire evals). + * @details Masking(1) + ordered_extra(1) + op(1) + OpQueueTBS(3) + OrderedRange(5) + z_perm(1) + * + pcs_shifted(9) = 21 + */ + auto get_full_circuit_entities() + { + return concatenate(MaskingEntities::get_all(), + RefArray{ this->ordered_extra_range_constraints_numerator }, + WireNonshiftedEntities::get_all(), + OpQueueWiresToBeShiftedEntities::get_all(), + OrderedRangeConstraints::get_all(), + DerivedWitnessEntities::get_all(), + ShiftedEntities::get_pcs_shifted()); + } + + /** + * @brief The 77 minicircuit wires (unshifted): NonRangeMain(13) + RangeConstraint(64). + */ + auto get_minicircuit_wires() { return NonOpQueueWiresToBeShiftedEntities::get_all(); } + + /** + * @brief The 77 minicircuit wire shifts: corresponds 1:1 with get_minicircuit_wires(). + */ + auto get_minicircuit_wires_shifted() { return NonOpQueueShiftedEntities::get_all(); } + + friend std::ostream& operator<<(std::ostream& os, const AllEntities& a) + { + os << "{ "; + std::ios_base::fmtflags f(os.flags()); + auto entities = a.get_all(); + for (size_t i = 0; i < entities.size() - 1; i++) { + os << "e[" << std::setw(2) << i << "] = " << (entities[i]) << ",\n"; + } + os << "e[" << std::setw(2) << (entities.size() - 1) << "] = " << entities[entities.size() - 1] << " }"; + + os.flags(f); + return os; } }; @@ -529,15 +673,23 @@ class TranslatorFlavor { }; // ======================================== - // Entity counts (from entity class sizes) + // Derived entity counts (from entity class sizes) // ======================================== - static constexpr size_t NUM_PRECOMPUTED_ENTITIES = LIST_SIZE(PRECOMPUTED_COLUMNS); - static constexpr size_t NUM_OP_QUEUE_WIRES_NOT_SHIFT_SOURCE = LIST_SIZE(OP_QUEUE_WIRE_COLUMNS); - static constexpr size_t NUM_ORDERED_RANGE = LIST_SIZE(ORDERED_RANGE_COLUMNS); - - static constexpr size_t NUM_WITNESS_ENTITIES = LIST_SIZE(WITNESS_COLUMNS); - - static constexpr size_t NUM_SHIFTED_ENTITIES = LIST_SIZE(SHIFTED_COLUMNS); + static constexpr size_t NUM_PRECOMPUTED_ENTITIES = PrecomputedEntities::_members_size; + static constexpr size_t NUM_WIRES_NON_SHIFTED = WireNonshiftedEntities::_members_size; + static constexpr size_t NUM_ORDERED_RANGE = OrderedRangeConstraints::_members_size; + + // Witness = WireNonshifted + WireToBeShifted + OrderedRange + Derived + Concatenated + static constexpr size_t NUM_WITNESS_ENTITIES = + WireNonshiftedEntities::_members_size + OpQueueWiresToBeShiftedEntities::_members_size + + NonRangeMainWires::_members_size + RangeConstraintWires::_members_size + + OrderedRangeConstraints::_members_size + DerivedWitnessEntities::_members_size + + ConcatenatedPolynomials::_members_size; + + // Shifted = OpQueueShifted + NonOpQueueShifted + DerivedShifted + static constexpr size_t NUM_SHIFTED_ENTITIES = OpQueueShiftedEntities::_members_size + + NonOpQueueShiftedEntities::_members_size + + DerivedShiftedEntities::_members_size; static constexpr size_t NUM_ALL_ENTITIES = NUM_MASKING_POLYNOMIALS + NUM_PRECOMPUTED_ENTITIES + NUM_WITNESS_ENTITIES + NUM_SHIFTED_ENTITIES; @@ -546,7 +698,10 @@ class TranslatorFlavor { static constexpr size_t NUM_COMPUTABLE_PRECOMPUTED = NUM_PRECOMPUTED_ENTITIES - 1; // Minicircuit wires: NonRangeMain + RangeConstraint (the non-op-queue wires that get shifted) - static constexpr size_t NUM_MINICIRCUIT_WIRES = LIST_SIZE(NON_OP_QUEUE_SHIFT_SOURCE_COLUMNS); + static constexpr size_t NUM_MINICIRCUIT_WIRES = + NonRangeMainWires::_members_size + RangeConstraintWires::_members_size; + static_assert(NUM_MINICIRCUIT_WIRES == NonOpQueueShiftedEntities::_members_size, + "Shifted minicircuit wires must match unshifted"); // 77 unshifted + 77 shifted minicircuit wire evaluations are sent mid-sumcheck static constexpr size_t NUM_MINICIRCUIT_EVALUATIONS = 2 * NUM_MINICIRCUIT_WIRES; @@ -557,24 +712,26 @@ class TranslatorFlavor { // Total number of minicircuit wires across all concatenation groups static constexpr size_t NUM_CONCATENATED_WIRES = NUM_CONCATENATED_POLYS * CONCATENATION_GROUP_SIZE; - static_assert(LIST_SIZE(CONCATENATED_COLUMNS) == NUM_CONCATENATED_POLYS); - static_assert(LIST_SIZE(CONCAT_GROUP_RANGE_0) == CONCATENATION_GROUP_SIZE); - static_assert(LIST_SIZE(CONCAT_GROUP_RANGE_1) == CONCATENATION_GROUP_SIZE); - static_assert(LIST_SIZE(CONCAT_GROUP_RANGE_2) == CONCATENATION_GROUP_SIZE); - static_assert(LIST_SIZE(CONCAT_GROUP_RANGE_3) == CONCATENATION_GROUP_SIZE); - static_assert(LIST_SIZE(CONCAT_GROUP_NON_RANGE) <= CONCATENATION_GROUP_SIZE, - "Non-range concat group must fit in one concatenation group (zero-padded at runtime)"); - - // PCS batch sizes derived directly from the column-list macros. Op-queue to-be-shifted wires - // (x_lo_y_hi, x_hi_z_1, y_lo_z_2) are registered in BOTH the unshifted and shifted PCS batches - // because the decomposition relation reads them in both forms. - static constexpr size_t NUM_TO_BE_SHIFTED = LIST_SIZE(PCS_SHIFT_SOURCE_COLUMNS); - static constexpr size_t NUM_PCS_UNSHIFTED = LIST_SIZE(PCS_UNSHIFTED_COLUMNS); - static constexpr size_t NUM_PCS_TO_BE_SHIFTED = LIST_SIZE(PCS_REPEATED_COLUMNS); + static_assert(ConcatenatedPolynomials::_members_size == NUM_CONCATENATED_POLYS); + static_assert(RangeConstraintWires::_members_size == (NUM_CONCATENATED_POLYS - 1) * CONCATENATION_GROUP_SIZE, + "Range constraint wires must fill exactly 4 concatenation groups"); + + // PCS batch sizes + // Note: op-queue to-be-shifted wires (x_lo_y_hi, x_hi_z_1, y_lo_z_2) are registered in BOTH the + // unshifted and shifted PCS batches because the decomposition relation reads them in both forms. + static constexpr size_t NUM_UNSHIFTED_WITNESSES_WITHOUT_CONCATENATED = + WireNonshiftedEntities::_members_size + OpQueueWiresToBeShiftedEntities::_members_size + + OrderedRangeConstraints::_members_size + DerivedWitnessEntities::_members_size; + static constexpr size_t NUM_TO_BE_SHIFTED = OpQueueWiresToBeShiftedEntities::_members_size + + OrderedRangeConstraints::_members_size + + DerivedWitnessEntities::_members_size; + static constexpr size_t NUM_PCS_UNSHIFTED = NUM_MASKING_POLYNOMIALS + + (NUM_PRECOMPUTED_ENTITIES - NUM_COMPUTABLE_PRECOMPUTED) + + NUM_UNSHIFTED_WITNESSES_WITHOUT_CONCATENATED + NUM_CONCATENATED_POLYS; + static constexpr size_t NUM_PCS_TO_BE_SHIFTED = NUM_TO_BE_SHIFTED + NUM_CONCATENATED_POLYS; // Indices for partitioning AllEntities - static constexpr size_t TO_BE_SHIFTED_WITNESSES_START = - NUM_PRECOMPUTED_ENTITIES + NUM_OP_QUEUE_WIRES_NOT_SHIFT_SOURCE; + static constexpr size_t TO_BE_SHIFTED_WITNESSES_START = NUM_PRECOMPUTED_ENTITIES + NUM_WIRES_NON_SHIFTED; static constexpr size_t SHIFTED_WITNESSES_START = NUM_SHIFTED_ENTITIES + TO_BE_SHIFTED_WITNESSES_START; // Commitments sent in wire round: concatenated + ordered range constraints @@ -589,18 +746,13 @@ class TranslatorFlavor { // stored indices 2..10 (unshifted) ↔ 16..24 (shifted) // Range 2: concatenated(5) — stored indices 11..15 (unshifted) ↔ 25..29 (shifted) // (Stored indices are 0-based after ZK offset; offset=2 accounts for Q_commitment + gemini_masking_poly) - static constexpr size_t PCS_REPEATED_NON_CONCAT_START = LIST_SIZE(VK_COLUMNS) + NUM_OP_QUEUE_WIRES_NOT_SHIFT_SOURCE; - static constexpr size_t PCS_REPEATED_CONCATENATED_START = PCS_REPEATED_NON_CONCAT_START + NUM_TO_BE_SHIFTED; - static constexpr size_t PCS_REPEATED_NON_CONCAT_SHIFTED_START = - PCS_REPEATED_NON_CONCAT_START + NUM_PCS_TO_BE_SHIFTED; - static constexpr size_t PCS_REPEATED_CONCATENATED_SHIFTED_START = - PCS_REPEATED_NON_CONCAT_SHIFTED_START + NUM_TO_BE_SHIFTED; + static constexpr size_t NUM_OP_QUEUE_TO_BE_SHIFTED = OpQueueWiresToBeShiftedEntities::_members_size; static constexpr RepeatedCommitmentsData REPEATED_COMMITMENTS = - RepeatedCommitmentsData(PCS_REPEATED_NON_CONCAT_START, - PCS_REPEATED_NON_CONCAT_SHIFTED_START, - NUM_TO_BE_SHIFTED, - PCS_REPEATED_CONCATENATED_START, - PCS_REPEATED_CONCATENATED_SHIFTED_START, + RepeatedCommitmentsData(2, + 2 + NUM_PCS_TO_BE_SHIFTED, + NUM_OP_QUEUE_TO_BE_SHIFTED + NUM_ORDERED_RANGE + 1, + 2 + NUM_OP_QUEUE_TO_BE_SHIFTED + NUM_ORDERED_RANGE + 1, + 2 + NUM_PCS_TO_BE_SHIFTED + NUM_OP_QUEUE_TO_BE_SHIFTED + NUM_ORDERED_RANGE + 1, NUM_CONCATENATED_POLYS); static constexpr size_t PROOF_LENGTH = @@ -648,11 +800,51 @@ class TranslatorFlavor { // ================================================================ + /** + * @brief Partition minicircuit wire references into concatenation groups. + * @details Takes a flat list of minicircuit wire refs (NonRangeMain followed by RangeConstraint) + * and partitions them: groups 0..3 are sequential chunks of CONCATENATION_GROUP_SIZE range constraint wires, + * group 4 is the non-range main wires with zero-padding. + * Used by both get_groups_to_be_concatenated() and get_groups_to_be_concatenated_shifted(). + */ + template + static std::vector> partition_minicircuit_wires_into_groups(WireRefs wire_refs, + DataType& zero_value) + { + constexpr size_t num_non_range = NonRangeMainWires::_members_size; + constexpr size_t num_range = RangeConstraintWires::_members_size; + static_assert(num_range % CONCATENATION_GROUP_SIZE == 0); + constexpr size_t num_range_groups = num_range / CONCATENATION_GROUP_SIZE; + + std::vector> groups; + // Groups 0..num_range_groups-1: sequential chunks of range constraint wires + for (size_t g = 0; g < num_range_groups; g++) { + RefVector group; + for (size_t j = 0; j < CONCATENATION_GROUP_SIZE; j++) { + group.push_back(wire_refs[num_non_range + g * CONCATENATION_GROUP_SIZE + j]); + } + groups.push_back(std::move(group)); + } + // Last group: non-range main wires + zero padding + RefVector group; + for (size_t j = 0; j < num_non_range; j++) { + group.push_back(wire_refs[j]); + } + for (size_t j = num_non_range; j < CONCATENATION_GROUP_SIZE; j++) { + group.push_back(zero_value); + } + groups.push_back(std::move(group)); + return groups; + } + /** * @brief Compute the computable precomputed selector evaluations and write them into AllEntities. */ template - static void compute_computable_precomputed(AllEntities& evals, std::span challenge); + static void compute_computable_precomputed(AllEntities& evals, std::span challenge) + { + TranslatorSelectorEvaluations::compute(challenge).populate(evals); + } /** * @brief Prover: read the 154 minicircuit wire evaluations from partially-evaluated polynomials. @@ -680,7 +872,16 @@ class TranslatorFlavor { */ template static void set_minicircuit_evaluations(AllEntities& evals, - const std::array& mid); + const std::array& mid) + { + size_t src = 0; + for (auto& wire : evals.get_minicircuit_wires()) { + wire = mid[src++]; + } + for (auto& wire : evals.get_minicircuit_wires_shifted()) { + wire = mid[src++]; + } + } /** * @brief Verifier: complete the claimed evaluations for the sumcheck relation check. @@ -691,7 +892,23 @@ class TranslatorFlavor { * challenges, converting mid-sumcheck values to full evaluations at the sumcheck point. */ template - static void complete_claimed_evaluations(AllEntities& evals, std::span challenge); + static void complete_claimed_evaluations(AllEntities& evals, std::span challenge) + { + // 1. Compute the computable precomputed selector evaluations + compute_computable_precomputed(evals, challenge); + + // 2. Scale minicircuit wire evaluations by L_0(u_top) = Π_{i=0}^{3} (1 - u_{LOG_MINI + i}) + FFType l0 = FFType(1); + for (size_t i = 0; i < CONST_TRANSLATOR_LOG_N - LOG_MINI_CIRCUIT_SIZE; i++) { + l0 *= (FFType(1) - challenge[LOG_MINI_CIRCUIT_SIZE + i]); + } + for (auto& wire : evals.get_minicircuit_wires()) { + wire *= l0; + } + for (auto& wire : evals.get_minicircuit_wires_shifted()) { + wire *= l0; + } + } /** * @brief Verifier: complete full-circuit evaluations from received array and challenge. @@ -704,7 +921,19 @@ class TranslatorFlavor { template static void complete_full_circuit_evaluations(AllEntities& evals, const std::array& full_circuit, - std::span challenge); + std::span challenge) + { + set_full_circuit_evaluations(evals, full_circuit); + complete_claimed_evaluations(evals, challenge); + + // Reconstruct the 5 concatenated polynomial evaluations from (now L0-scaled) wire evaluations + auto groups = evals.get_groups_to_be_concatenated(); + auto concat_evals = reconstruct_concatenated_evaluations(groups, challenge); + auto concat_refs = evals.get_concatenated(); + for (size_t g = 0; g < NUM_CONCATENATED_POLYS; g++) { + concat_refs[g] = concat_evals[g]; + } + } /** * @brief Reconstruct concatenated polynomial evaluations from individual wire evaluations @@ -714,31 +943,78 @@ class TranslatorFlavor { * is reconstructed as: F(u) = [1/L_0(u_top)] * Σ_j L_j(u_top) * f_j(u), where L_j are the Lagrange * basis polynomials over the top challenges and L_0 is the "padding" factor. * - * Wire evaluations are read directly from `evals` via CONCAT_MAP — the chunk per concat - * polynomial is named by the map and accessed by field name (with `_shift` suffix when Shifted=true), - * so no intermediate `std::vector` is materialised. - * - * @tparam Shifted If true, accumulate from `name##_shift` fields instead of `name`. - * @param evals AllEntities holding wire evaluations. + * @param groups The 5 groups of 16 wire evaluations to reconstruct from. * @param challenge The full sumcheck challenge vector. * @return Array of 5 reconstructed concatenated evaluations. */ - template + template static std::array reconstruct_concatenated_evaluations( - AllEntities& evals, std::span challenge); + const std::vector>& groups, std::span challenge) + { + static constexpr size_t NUM_TOP_BITS = numeric::get_msb(CONCATENATION_GROUP_SIZE); + + // Compute CONCATENATION_GROUP_SIZE-point Lagrange basis over the top challenges + // a = u[N - 4], b = u[N - 3], c = u[N - 2], d = u[N - 1] + // L(0) = (1 - d) * (1 - c) * (1 - b) * (1 - a) + // L(1) = (1 - d) * (1 - c) * (1 - b) * ( a) + // L(2) = (1 - d) * (1 - c) * ( b) * (1 - a) + // L(3) = (1 - d) * (1 - c) * ( b) * ( a) + // ... + std::array lagrange_basis; + for (size_t j = 0; j < CONCATENATION_GROUP_SIZE; j++) { + lagrange_basis[j] = FFType(1); + for (size_t bit = 0; bit < NUM_TOP_BITS; bit++) { + const FFType& u = challenge[CONST_TRANSLATOR_LOG_N - NUM_TOP_BITS + bit]; + lagrange_basis[j] *= ((j >> bit) & 1) ? u : (FFType(1) - u); + } + } + + // L_0 is the "padding" factor from wires having support in [1, MINI) + // The reason we need to divide by L_0 is because L_j(u) already accounts for the challenges a, b, c, d: + // L_j(u) = (1 - d) * (1 - c) * (1 - b) * (1 - a) * L_j(0, u_bottom) + FFType padding_inv = lagrange_basis[0].invert(); + + auto reconstruct = [&](const auto& group) -> FFType { + FFType result = FFType(0); + for (size_t j = 0; j < CONCATENATION_GROUP_SIZE; j++) { + result += lagrange_basis[j] * group[j]; + } + return result * padding_inv; + }; + + std::array result; + for (size_t g = 0; g < NUM_CONCATENATED_POLYS; g++) { + result[g] = reconstruct(groups[g]); + } + return result; + } /** * @brief Prover: extract the full-circuit evaluations via get_full_circuit_entities(). */ template - static std::array get_full_circuit_evaluations(AllEntities& evals); + static std::array get_full_circuit_evaluations(AllEntities& evals) + { + std::array result; + size_t dst = 0; + for (auto& entity : evals.get_full_circuit_entities()) { + result[dst++] = entity; + } + return result; + } /** * @brief Verifier: write the full-circuit evaluations back via get_full_circuit_entities(). */ template static void set_full_circuit_evaluations(AllEntities& evals, - const std::array& full_circuit); + const std::array& full_circuit) + { + size_t src = 0; + for (auto& entity : evals.get_full_circuit_entities()) { + entity = full_circuit[src++]; + } + } /** * @brief A container for the prover polynomials handles. @@ -749,7 +1025,86 @@ class TranslatorFlavor { * @brief ProverPolynomials constructor * @details Initializes wire polynomials efficiently to be only minicircuit size.. */ - ProverPolynomials(); + ProverPolynomials() + { + + const size_t circuit_size = 1 << CONST_TRANSLATOR_LOG_N; + for (auto& ordered_range_constraint : get_ordered_range_constraints()) { + ordered_range_constraint = Polynomial{ /*size*/ circuit_size - 1, + /*largest possible index*/ circuit_size, + 1 }; + } + + // Initialize 5 concatenated polynomials (full circuit_size, shiftable with start_index=1) + // Row 0 of block 0 is the no-op row where all values are zero. + for (auto& concat_poly : get_concatenated()) { + concat_poly = Polynomial{ /*size*/ circuit_size - 1, + /*virtual_size*/ circuit_size, + /*start_index*/ 1 }; + } + z_perm = Polynomial{ /*size*/ circuit_size - 1, + /*virtual_size*/ circuit_size, + /*start_index*/ 1 }; + + op = Polynomial{ MINI_CIRCUIT_SIZE, circuit_size }; + + // All minicircuit wires (non-op-queue) are only non-zero in [1, MINI_CIRCUIT_SIZE) + for (auto& poly : NonOpQueueWiresToBeShiftedEntities::get_all()) { + if (poly.is_empty()) { + poly = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - 1, + /*virtual_size*/ circuit_size, + /*start_index*/ 1 }; + } + } + + // Op queue wires to be shifted + for (auto& poly : OpQueueWiresToBeShiftedEntities::get_all()) { + if (poly.is_empty()) { + poly = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - 1, + /*virtual_size*/ circuit_size, + /*start_index*/ 1 }; + } + } + + // Initialize lagrange polynomials and the ordered extra range constraints numerator (the precomputed + // polynomials) within the appropriate range they operate on + lagrange_first = Polynomial{ /*size*/ 1, /*virtual_size*/ circuit_size }; + lagrange_result_row = Polynomial{ /*size*/ 1, /*virtual_size*/ circuit_size, /*start_index*/ RESULT_ROW }; + lagrange_even_in_minicircuit = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - RESULT_ROW - NUM_MASKED_ROWS_END, + /*virtual_size*/ circuit_size, + /*start_index=*/RESULT_ROW }; + lagrange_odd_in_minicircuit = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - RESULT_ROW - NUM_MASKED_ROWS_END - 1, + /*virtual_size*/ circuit_size, + /*start_index=*/RESULT_ROW + 1 }; + lagrange_last_in_minicircuit = Polynomial{ /*size*/ 1, + /*virtual_size*/ circuit_size, + /*start_index=*/MINI_CIRCUIT_SIZE - NUM_MASKED_ROWS_END - 1 }; + lagrange_mini_masking = Polynomial{ /*size*/ MINI_CIRCUIT_SIZE - RANDOMNESS_START, + /*virtual_size*/ circuit_size, + /*start_index=*/RANDOMNESS_START }; + // With concatenation, masking rows are scattered in concatenated polys: end of each of the 16 blocks + // Must span full circuit since values go up to position 15*MINI+(MINI-1) + lagrange_masking = Polynomial{ circuit_size, circuit_size }; + // Ordered masking: contiguous at the end (marks masking positions in ordered polynomials) + lagrange_ordered_masking = Polynomial{ /*size*/ MAX_RANDOM_VALUES_PER_ORDERED, + /*virtual_size*/ circuit_size, + /*start_index*/ circuit_size - MAX_RANDOM_VALUES_PER_ORDERED }; + lagrange_last = Polynomial{ /*size*/ 1, + /*virtual_size*/ circuit_size, + /*start_index*/ circuit_size - 1 }; + // lagrange_real_last marks the last position with sorted values in ordered polynomials + // (where we check maximum value = 2^14 - 1). With contiguous masking at the end, + // this is at position circuit_size - MAX_RANDOM_VALUES_PER_ORDERED - 1. + lagrange_real_last = Polynomial{ /*size*/ 1, + /*virtual_size*/ circuit_size, + /*start_index*/ circuit_size - MAX_RANDOM_VALUES_PER_ORDERED - 1 }; + ordered_extra_range_constraints_numerator = + Polynomial{ /*size*/ SORTED_STEPS_COUNT * NUM_CONCATENATED_POLYS + MASKING_OVERFLOW_COLUMN, + /*virtual_size*/ circuit_size, + /*start_index*/ 0 }; + + set_shifted(); + } ProverPolynomials& operator=(const ProverPolynomials&) = delete; ProverPolynomials(const ProverPolynomials& o) = delete; ProverPolynomials(ProverPolynomials&& o) noexcept = default; @@ -760,10 +1115,28 @@ class TranslatorFlavor { * @brief Returns the evaluations of all prover polynomials at one point on the boolean * hypercube, which represents one row in the execution trace. */ - [[nodiscard]] AllValues get_row(size_t row_idx) const; + [[nodiscard]] AllValues get_row(size_t row_idx) const + { + AllValues result; + for (auto [result_field, polynomial] : zip_view(result.get_all(), this->get_all())) { + // Translator polynomials have different support regions (start_index/end_index) + // Return 0 for out-of-bounds access (which is the correct value outside support) + if (row_idx >= polynomial.start_index() && row_idx < polynomial.end_index()) { + result_field = polynomial[row_idx]; + } else { + result_field = FF(0); + } + } + return result; + } // Set all shifted polynomials based on their to-be-shifted counterpart. - // Uses get_all_to_be_shifted() (86 entries for Sumcheck), not get_pcs_to_be_shifted() (14 entries for PCS). - void set_shifted(); + // Uses get_all_to_be_shifted() (86 entries for Sumcheck), not get_to_be_shifted() (9 entries for PCS). + void set_shifted() + { + for (auto [shifted, to_be_shifted] : zip_view(get_shifted(), get_all_to_be_shifted())) { + shifted = to_be_shifted.shifted(); + } + } }; /** @@ -790,8 +1163,7 @@ class TranslatorFlavor { public: bool operator==(const VKEntities& other) const = default; using DataType = DataType_; - DEFINE_FIELDS_FROM_LIST(DataType, VK_COLUMNS) - DEFINE_VIEW_FROM_LIST(get_all, VK_COLUMNS) + DEFINE_FLAVOR_MEMBERS(DataType, ordered_extra_range_constraints_numerator); }; /** @@ -825,7 +1197,37 @@ class TranslatorFlavor { */ class CommitmentLabels : public AllEntities { public: - CommitmentLabels(); + CommitmentLabels() + { + // Concatenated polynomials (sent via get_non_opqueue_wires_and_ordered_range_constraints) + this->concatenated_range_constraints_0 = "CONCATENATED_RANGE_CONSTRAINTS_0"; + this->concatenated_range_constraints_1 = "CONCATENATED_RANGE_CONSTRAINTS_1"; + this->concatenated_range_constraints_2 = "CONCATENATED_RANGE_CONSTRAINTS_2"; + this->concatenated_range_constraints_3 = "CONCATENATED_RANGE_CONSTRAINTS_3"; + this->concatenated_non_range = "CONCATENATED_NON_RANGE"; + + // Ordered range constraints (sent via get_non_opqueue_wires_and_ordered_range_constraints) + this->ordered_range_constraints_0 = "ORDERED_RANGE_CONSTRAINTS_0"; + this->ordered_range_constraints_1 = "ORDERED_RANGE_CONSTRAINTS_1"; + this->ordered_range_constraints_2 = "ORDERED_RANGE_CONSTRAINTS_2"; + this->ordered_range_constraints_3 = "ORDERED_RANGE_CONSTRAINTS_3"; + this->ordered_range_constraints_4 = "ORDERED_RANGE_CONSTRAINTS_4"; + + // Grand product (committed separately) + this->z_perm = "Z_PERM"; + }; + }; + + template + class VerifierCommitments_ : public AllEntities { + public: + VerifierCommitments_(const std::shared_ptr& verification_key) + { + // Only ordered_extra_range_constraints_numerator needs a VK commitment for PCS. + // All other precomputed selectors are computable (evaluations derived from sumcheck challenge). + this->ordered_extra_range_constraints_numerator = + verification_key->ordered_extra_range_constraints_numerator; + } }; /** @@ -855,7 +1257,7 @@ class TranslatorFlavor { (polynomials.z_perm[edge_idx + 1] == polynomials.z_perm_shift[edge_idx + 1]) && polynomials.lagrange_last[edge_idx] == 0 && polynomials.lagrange_last[edge_idx + 1] == 0; } - using VerifierCommitments = AllEntities; + using VerifierCommitments = VerifierCommitments_; }; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_selectors.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_selectors.hpp index 14fde3cad3ba..4c0b48c3fb65 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_selectors.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_selectors.hpp @@ -45,7 +45,7 @@ template struct TranslatorSelectorEv static_assert(LOG_RESULT_ROW < LOG_MINI_CIRCUIT_SIZE); static_assert(LOG_MINI_CIRCUIT_SIZE > LOG_MAX_RANDOM, "Mini circuit must be larger than max random region"); - // The 10 selector evaluations (order matches PRECOMPUTED_COLUMNS in translator_flavor.hpp, + // The 10 selector evaluations (order matches PrecomputedEntities in translator_flavor.hpp, // skipping ordered_extra_range_constraints_numerator which cannot be efficiently computed) FF lagrange_first; FF lagrange_last; @@ -220,7 +220,7 @@ template struct TranslatorSelectorEv /** * @brief Write all 10 computed evaluations into any entity struct with matching named fields. - * @details Works for any native or stdlib entity container with matching named selector fields. + * @details Works for AllValues, AllEntities, PrecomputedEntities, native or stdlib. */ template void populate(Entities& target) const { diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp index 6b2f0205f8b3..ed0711f09689 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp @@ -147,10 +147,7 @@ typename TranslatorVerifier_::VerifierCommitments TranslatorVerifier_add_to_hash_buffer("vk_hash", vk_hash); vinfo("Translator vk hash in verifier: ", vk_hash); - VerifierCommitments commitments; - // Only ordered_extra_range_constraints_numerator needs a VK commitment for PCS. - // All other precomputed selectors are computable (evaluations derived from sumcheck challenge). - commitments.ordered_extra_range_constraints_numerator = key->ordered_extra_range_constraints_numerator; + VerifierCommitments commitments{ key }; CommitmentLabels commitment_labels; // For recursive verification, mark the accumulated result's prime basis limb as used @@ -224,8 +221,8 @@ typename TranslatorVerifier_::ReductionResult TranslatorVerifier_( - claimed, std::span(sumcheck_output.challenge)); + auto concat_shift_evals = TranslatorFlavor::reconstruct_concatenated_evaluations( + claimed.get_groups_to_be_concatenated_shifted(), std::span(sumcheck_output.challenge)); // --- PCS: build opening claims and verify --- auto combined_unshifted_comms = commitments.get_pcs_unshifted(); From e421d1eb75a9fa6ba0b7597b39f3db9a071ea7c5 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Mon, 11 May 2026 11:23:35 +0000 Subject: [PATCH 12/17] Reapply "k shifts resurrected" This reverts commit 6e416dcdea9b1a35957442d6658e0381f65969c4. --- .../commitment_schemes/claim_batcher.hpp | 23 ++++++++++-- .../commitment_schemes/gemini/gemini.hpp | 36 +++++++++++++++++++ .../commitment_schemes/ipa/ipa.test.cpp | 28 +++++++++++++++ .../commitment_schemes/kzg/kzg.test.cpp | 34 ++++++++++++++++++ .../utils/mock_witness_generator.hpp | 35 ++++++++++++++++-- .../barretenberg/polynomials/polynomial.cpp | 11 ++++++ .../barretenberg/polynomials/polynomial.hpp | 6 ++++ .../polynomials/polynomial.test.cpp | 28 +++++++++++++++ 8 files changed, 195 insertions(+), 6 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp index 4f7e3370c2da..b724ceeb81b7 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp @@ -34,20 +34,26 @@ template struct ClaimBatcher_ { Fr scalar = 0; }; - std::optional unshifted; // commitments and evaluations of unshifted polynomials - std::optional shifted; // commitments of to-be-shifted-by-1 polys, evals of their shifts + std::optional unshifted; // commitments and evaluations of unshifted polynomials + std::optional shifted; // commitments of to-be-shifted-by-1 polys, evals of their shifts + std::optional right_shifted_by_k; // commitments of to-be-right-shifted-by-k polys, evals of their shifts + + // Magnitude of the right-shift-by-k applied to `right_shifted_by_k` polys (assumed even). + size_t k_shift_magnitude = 0; Batch get_unshifted() { return (unshifted) ? *unshifted : Batch{}; } Batch get_shifted() { return (shifted) ? *shifted : Batch{}; } + Batch get_right_shifted_by_k() { return (right_shifted_by_k) ? *right_shifted_by_k : Batch{}; } Fr get_unshifted_batch_scalar() const { return unshifted ? unshifted->scalar : Fr{ 0 }; } /** * @brief Compute scalars used to batch each set of claims, excluding contribution from batching challenge \rho - * @details Computes scalars s_0, s_1 given by + * @details Computes scalars s_0, s_1, s_2 given by * \f[ * - s_0 = \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right) \f], * - s_1 = \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right) + * - s_2 = r^{k} \times \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right) * \f] * where the scalars used to batch the claims are given by * \f[ @@ -81,6 +87,11 @@ template struct ClaimBatcher_ { shifted->scalar = r_challenge.invert() * (inverse_vanishing_eval_pos - nu_challenge * inverse_vanishing_eval_neg); } + if (right_shifted_by_k) { + // r^k ⋅ (1/(z−r) + ν/(z+r)) + right_shifted_by_k->scalar = r_challenge.pow(static_cast(k_shift_magnitude)) * + (inverse_vanishing_eval_pos + nu_challenge * inverse_vanishing_eval_neg); + } } /** * @brief Append the commitments and scalars from each batch of claims to the Shplemini vectors which subsequently @@ -100,6 +111,7 @@ template struct ClaimBatcher_ { size_t num_powers = 0; num_powers += unshifted.has_value() ? unshifted->commitments.size() : 0; num_powers += shifted.has_value() ? shifted->commitments.size() : 0; + num_powers += right_shifted_by_k.has_value() ? right_shifted_by_k->commitments.size() : 0; Fr rho_power = Fr(1); size_t power_idx = 0; @@ -128,6 +140,11 @@ template struct ClaimBatcher_ { // i-th shifted commitments will be multiplied by ρ^{num_unshifted + i} and r⁻¹ ⋅ (1/(z−r) − ν/(z+r)) aggregate_claim_data_and_update_batched_evaluation(*shifted); } + if (right_shifted_by_k) { + // i-th right-shifted-by-k commitment will be multiplied by ρ^{num_unshifted + num_shifted + i} and + // r^k ⋅ (1/(z−r) + ν/(z+r)) + aggregate_claim_data_and_update_batched_evaluation(*right_shifted_by_k); + } BB_ASSERT_EQ(power_idx, num_powers); } diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp index 0cc9972c85c2..d0436f4f15b3 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp @@ -132,15 +132,19 @@ template class GeminiProver_ { Polynomial batched_unshifted; // linear combination of unshifted polynomials Polynomial batched_to_be_shifted_by_one; // linear combination of to-be-shifted polynomials + Polynomial batched_to_be_shifted_by_k; // linear combination of to-be-right-shifted-by-k polynomials // Batched tails: small polynomials covering only the tail region (e.g. last NUM_MASKED_ROWS positions). // Populated during compute_batched if tails are registered. Polynomial batched_unshifted_tail_; Polynomial batched_shifted_tail_; + size_t k_shift_magnitude = 0; // magnitude of right-shift-by-k (assumed even) + public: RefVector unshifted; // set of unshifted polynomials RefVector to_be_shifted_by_one; // set of polynomials to be left shifted by 1 + RefVector to_be_shifted_by_k; // set of polynomials to be right shifted by k // Tails: small polynomials (e.g. masking values) to be batched with the same rho scalar // as their corresponding base polynomial. Pairs of (index in unshifted/shifted list, tail poly). @@ -156,10 +160,18 @@ template class GeminiProver_ { bool has_unshifted() const { return unshifted.size() > 0; } bool has_to_be_shifted_by_one() const { return to_be_shifted_by_one.size() > 0; } + bool has_to_be_shifted_by_k() const { return to_be_shifted_by_k.size() > 0; } // Set references to the polynomials to be batched void set_unshifted(RefVector polynomials) { unshifted = polynomials; } void set_to_be_shifted_by_one(RefVector polynomials) { to_be_shifted_by_one = polynomials; } + void set_to_be_shifted_by_k(RefVector polynomials, const size_t shift_magnitude) + { + // k must be even for the +/- evaluation formulas to share a sign convention + BB_ASSERT_EQ(shift_magnitude % 2, static_cast(0)); + to_be_shifted_by_k = polynomials; + k_shift_magnitude = shift_magnitude; + } void add_unshifted_tail(size_t batcher_index, Polynomial&& tail) { @@ -236,6 +248,13 @@ template class GeminiProver_ { full_batched += batched_shifted_tail_.shifted(); } + if (has_to_be_shifted_by_k()) { + batched_to_be_shifted_by_k = + Polynomial(full_batched_size - k_shift_magnitude, full_batched_size, /*start_index=*/0); + batch(batched_to_be_shifted_by_k, to_be_shifted_by_k); + full_batched += batched_to_be_shifted_by_k.right_shifted(k_shift_magnitude); // A₀ += X^k * H + } + return full_batched; } @@ -258,8 +277,25 @@ template class GeminiProver_ { A_0_pos = std::move(A_0_extended); } + // The k-shift contribution lives on [0, full_batched_size - k); ensure the running + // accumulators cover that range before adding (mirrors the tail-extension pattern above). + if (has_to_be_shifted_by_k()) { + const size_t needed_end = full_batched_size - k_shift_magnitude; + if (A_0_pos.end_index() < needed_end) { + Polynomial A_0_extended(A_0_pos, needed_end - A_0_pos.start_index()); + A_0_pos = std::move(A_0_extended); + } + } + Polynomial A_0_neg = A_0_pos; + if (has_to_be_shifted_by_k()) { + const Fr r_pow_k = + r_challenge.pow(static_cast(k_shift_magnitude)); // r^k (k even ⇒ same sign at ±r) + A_0_pos.add_scaled(batched_to_be_shifted_by_k, r_pow_k); + A_0_neg.add_scaled(batched_to_be_shifted_by_k, r_pow_k); + } + Fr r_inv = r_challenge.invert(); if (has_to_be_shifted_by_one()) { A_0_pos.add_scaled(batched_to_be_shifted_by_one, r_inv); diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp index 987bed0343c8..de4d6da57d90 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -309,6 +309,34 @@ TEST_F(IPATest, ShpleminiIPAWithShift) EXPECT_EQ(result, true); } +// Shplemini + IPA. Verifies a flow with both shift-by-1 and right-shift-by-k claims. +TEST_F(IPATest, ShpleminiIPAWithShiftAndKShift) +{ + auto mle_opening_point = this->random_evaluation_point(log_n); + MockClaimGenerator mock_claims(n, + /*num_polynomials*/ 5, + /*num_to_be_shifted*/ 2, + mle_opening_point, + ck, + /*num_to_be_right_shifted_by_k*/ 2); + auto prover_transcript = NativeTranscript::test_prover_init_empty(); + + auto prover_opening_claims = + GeminiProver::prove(n, mock_claims.polynomial_batcher, mle_opening_point, ck, prover_transcript); + const auto opening_claim = ShplonkProver::prove(ck, prover_opening_claims, prover_transcript); + PCS::compute_opening_proof(ck, opening_claim, prover_transcript); + + auto verifier_transcript = NativeTranscript::test_verifier_init_empty(prover_transcript); + + const auto batch_opening_claim = + ShpleminiVerifier::compute_batch_opening_claim( + mock_claims.claim_batcher, mle_opening_point, vk.get_g1_identity(), verifier_transcript) + .batch_opening_claim; + + auto result = PCS::reduce_verify_batch_opening_claim(batch_opening_claim, vk, verifier_transcript); + + EXPECT_EQ(result, true); +} // Test `ShpleminiVerifier::remove_shifted_commitments`. Four polynomials, two of which are shifted. TEST_F(IPATest, ShpleminiIPAShiftsRemoval) { diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp index 53518cd4a51c..98ca678ce4ef 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp @@ -225,6 +225,40 @@ TEST_F(KZGTest, ShpleminiKzgWithShift) EXPECT_EQ(pairing_points.check(), true); } +// Verifies a Shplemini → KZG flow with both shift-by-1 and right-shift-by-k claims. +TEST_F(KZGTest, ShpleminiKzgWithShiftAndKShift) +{ + std::vector mle_opening_point = random_evaluation_point(log_n); + + MockClaimGenerator mock_claims(n, + /*num_polynomials*/ 5, + /*num_to_be_shifted*/ 2, + mle_opening_point, + ck, + /*num_to_be_right_shifted_by_k*/ 2); + + auto prover_transcript = NativeTranscript::test_prover_init_empty(); + + auto prover_opening_claims = + GeminiProver::prove(n, mock_claims.polynomial_batcher, mle_opening_point, ck, prover_transcript); + + const auto opening_claim = ShplonkProver::prove(ck, prover_opening_claims, prover_transcript); + + PCS::compute_opening_proof(ck, opening_claim, prover_transcript); + + auto verifier_transcript = NativeTranscript::test_verifier_init_empty(prover_transcript); + + auto batch_opening_claim = + ShpleminiVerifier::compute_batch_opening_claim( + mock_claims.claim_batcher, mle_opening_point, vk.get_g1_identity(), verifier_transcript) + .batch_opening_claim; + + const auto pairing_points = + PCS::reduce_verify_batch_opening_claim(std::move(batch_opening_claim), verifier_transcript); + + EXPECT_EQ(pairing_points.check(), true); +} + TEST_F(KZGTest, ShpleminiKzgWithShiftAndInterleaving) { std::vector mle_opening_point = random_evaluation_point(log_n); // sometimes denoted 'u' diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/mock_witness_generator.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/mock_witness_generator.hpp index 37101b69e0ce..3918f165d0dd 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/mock_witness_generator.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/mock_witness_generator.hpp @@ -34,12 +34,16 @@ template struct MockClaimGenerator { ClaimData unshifted; ClaimData to_be_shifted; + ClaimData to_be_right_shifted_by_k; std::vector const_size_mle_opening_point; PolynomialBatcher polynomial_batcher; ClaimBatcher claim_batcher; + // Mock right-shift magnitude (must be even). + static constexpr size_t k_magnitude = 6; + // Containers for mock Sumcheck data std::vector> round_univariates; std::vector sumcheck_commitments; @@ -60,7 +64,8 @@ template struct MockClaimGenerator { const size_t num_polynomials, const size_t num_to_be_shifted, const std::vector& mle_opening_point, - const CommitmentKey& commitment_key) + const CommitmentKey& commitment_key, + const size_t num_to_be_right_shifted_by_k = 0) : ck(commitment_key) // Initialize the commitment key , polynomial_batcher(poly_size) @@ -81,8 +86,9 @@ template struct MockClaimGenerator { challenge = std::span(mle_opening_point); } - BB_ASSERT_GTE(num_polynomials, num_to_be_shifted); - const size_t num_not_to_be_shifted = num_polynomials - num_to_be_shifted; + const size_t total_num_to_be_shifted = num_to_be_shifted + num_to_be_right_shifted_by_k; + BB_ASSERT_GTE(num_polynomials, total_num_to_be_shifted); + const size_t num_not_to_be_shifted = num_polynomials - total_num_to_be_shifted; Fr ebz_factor = 1; @@ -111,13 +117,36 @@ template struct MockClaimGenerator { unshifted.polys.push_back(std::move(poly)); } + // Construct claim data for polynomials that are to-be-right-shifted-by-k. The base poly's data range is + // [0, poly_size - k_magnitude); its right-shift-by-k therefore lives on [k_magnitude, poly_size). + for (size_t idx = 0; idx < num_to_be_right_shifted_by_k; idx++) { + Polynomial poly = Polynomial::random(poly_size - k_magnitude, poly_size, 0); + Commitment commitment = ck.commit(poly); + to_be_right_shifted_by_k.commitments.push_back(commitment); + to_be_right_shifted_by_k.evals.push_back(poly.right_shifted(k_magnitude).evaluate_mle(challenge) * + ebz_factor); + to_be_right_shifted_by_k.polys.push_back(poly.share()); + // Populate the unshifted counterpart in the unshifted claims + unshifted.commitments.push_back(commitment); + unshifted.evals.push_back(poly.evaluate_mle(challenge) * ebz_factor); + unshifted.polys.push_back(std::move(poly)); + } + polynomial_batcher.set_unshifted(RefVector(unshifted.polys)); polynomial_batcher.set_to_be_shifted_by_one(RefVector(to_be_shifted.polys)); + if (num_to_be_right_shifted_by_k > 0) { + polynomial_batcher.set_to_be_shifted_by_k(RefVector(to_be_right_shifted_by_k.polys), k_magnitude); + } claim_batcher = ClaimBatcher{ .unshifted = ClaimBatch{ RefVector(unshifted.commitments), RefVector(unshifted.evals) }, .shifted = ClaimBatch{ RefVector(to_be_shifted.commitments), RefVector(to_be_shifted.evals) } }; + if (num_to_be_right_shifted_by_k > 0) { + claim_batcher.right_shifted_by_k = ClaimBatch{ RefVector(to_be_right_shifted_by_k.commitments), + RefVector(to_be_right_shifted_by_k.evals) }; + claim_batcher.k_shift_magnitude = k_magnitude; + } } // Generate zero polynomials to test edge cases in PCS diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp index edb44a863612..9e17e3c4a42c 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp @@ -332,6 +332,17 @@ template Polynomial Polynomial::shifted() const return result; } +template Polynomial Polynomial::right_shifted(const size_t magnitude) const +{ + // The last `magnitude` virtual coefficients must be zero so the shift fits within `virtual_size()`. + BB_ASSERT_LTE(coefficients_.end_ + magnitude, virtual_size()); + Polynomial result; + result.coefficients_ = coefficients_; + result.coefficients_.start_ += magnitude; + result.coefficients_.end_ += magnitude; + return result; +} + template Polynomial Polynomial::reverse() const { const size_t end_index = this->end_index(); diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp index 7ac33b963303..119c4b5050a5 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp @@ -195,6 +195,12 @@ template class Polynomial { */ Polynomial shifted() const; + /** + * @brief Returns a Polynomial equal to the right-shift-by-magnitude of self. + * @note Resulting Polynomial shares the memory of that used to generate it. + */ + Polynomial right_shifted(size_t magnitude) const; + /** * @brief Returns the polynomial equal to the reverse of self * diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.test.cpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.test.cpp index 2f2e45eba77e..527e85966ad1 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.test.cpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.test.cpp @@ -29,6 +29,34 @@ TEST(Polynomial, Shifted) } } +// Simple test/demonstration of right_shifted functionality +TEST(Polynomial, RightShifted) +{ + using FF = bb::fr; + using Polynomial = bb::Polynomial; + const size_t SIZE = 10; + const size_t VIRTUAL_SIZE = 20; + const size_t START_IDX = 2; + const size_t SHIFT_MAGNITUDE = 5; + auto poly = Polynomial::random(SIZE, VIRTUAL_SIZE, START_IDX); + + auto poly_shifted = poly.right_shifted(SHIFT_MAGNITUDE); + + EXPECT_EQ(poly_shifted.size(), poly.size()); + EXPECT_EQ(poly_shifted.virtual_size(), poly.virtual_size()); + + // The shift is indeed the shift + for (size_t i = 0; i < SIZE; ++i) { + EXPECT_EQ(poly_shifted.get(i + SHIFT_MAGNITUDE), poly.get(i)); + } + + // The shifted view shares memory with the original + poly.at(3) = 25; + for (size_t i = 0; i < SIZE; ++i) { + EXPECT_EQ(poly_shifted.get(i + SHIFT_MAGNITUDE), poly.get(i)); + } +} + // Simple test/demonstration of reverse functionality TEST(Polynomial, Reversed) { From a29053a25749bbb90d1c3b3baf2db72ba8547877 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Mon, 11 May 2026 11:23:35 +0000 Subject: [PATCH 13/17] Revert "rm md" This reverts commit 5ac2a27496e3c89287412c487127cff3517062a1. --- .../FINAL_APPEND_MERGE_IN_TRANSLATOR.md | 265 ++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 barretenberg/cpp/src/barretenberg/goblin/FINAL_APPEND_MERGE_IN_TRANSLATOR.md diff --git a/barretenberg/cpp/src/barretenberg/goblin/FINAL_APPEND_MERGE_IN_TRANSLATOR.md b/barretenberg/cpp/src/barretenberg/goblin/FINAL_APPEND_MERGE_IN_TRANSLATOR.md new file mode 100644 index 000000000000..6cb382bd762f --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/goblin/FINAL_APPEND_MERGE_IN_TRANSLATOR.md @@ -0,0 +1,265 @@ +# Final APPEND Merge in Translator + +## Status + +**Explored, not pursued.** The cost/benefit ratio is unfavourable; the +final APPEND merge in [`MERGE_PROTOCOL.md`](./MERGE_PROTOCOL.md) is kept. +The analysis below is preserved as a record of the design space. + +### Why we stopped + +The headline savings — drop the final merge, save 4 $[M_j]$ commitments, +$[G]$, the Shplonk merge batch, and ~13 field-element evaluations — are +real but modest. The integration cost grows in three places that did not +appear in the initial sketch: + +1. **Two k values in the PCS, not one.** Translator's decomposition + relation reads each op-queue wire both unshifted and shift-by-1 in the + same row (the `(x_lo, y_hi)` / `(x_hi, z_1)` / `(y_lo, z_2)` pair + pattern). Extending that pattern to a right-shifted hiding wire + requires opening $t_{\text{hiding}}$ at *two* k values, $\ell$ and + $\ell-1$. The Shplemini k-shift batch we restored on the parent branch + supports a single $k$; multi-k is a non-trivial extension. Sidestepping + it requires either (a) a separate `[t_hiding-shift-ℓ]` commitment plus + a univariate consistency check (4 extra commitments per merge), or + (b) reshuffling Translator's witness layout so the shift-by-1 pattern + on op-queue wires goes away — a cross-cutting change to limb storage + and range-constraint pairings. + +2. **ZK-budget bleed-through onto Mega.** Each new opening of + $[t_{\text{hiding}}]$ at the Translator-side multilinear point + $u'$ is an extra observable that Mega's ecc_op_wire masking has to + absorb (the polynomial is committed once by the hiding kernel and + serves both protocols). With multi-k the hiding kernel needs ~2 more + masked rows than today and the soundness analysis in + `MERGE_PROTOCOL.md` (full-rank over + $\{\kappa, \kappa^{-1}, u, u', z\}$) has to be redone over a + different observable set. The "consistency check" alternative + separates the budgets but at the cost of those 4 extra commitments. + +3. **Translator becomes mode-dependent.** Two Translator VKs are needed, + distinguished by the precomputed `lagrange_prev` polynomial (Chonk vs + AVM modes). On its own this is clean — `lagrange_prev` already encodes + the mode without an additional selector — but it adds a generation + path, a VK pinning entry, and a verifier dispatch surface to maintain. + +Each item is tractable in isolation. The aggregate, against the modest +end-state savings, is not worth it. + +### What we'd resurrect this for + +The conclusion is sensitive to assumptions. Reasons to revisit: + +- The k-shift Shplemini PCS gains a multi-k extension for an unrelated + reason, eliminating cost item (1). +- Mega's ecc_op masking is restructured for an unrelated reason such + that extra observables on $[t_{\text{hiding}}]$ are free, eliminating + cost item (2). +- Final-merge cost grows materially (e.g. larger op-queue, more wires), + shifting the savings side of the ledger. + +Until then, intermediate PREPEND merges and the final APPEND merge in +`MERGE_PROTOCOL.md` are the canonical design. + +--- + +The remainder of this document captures the proposal as it stood when +the cost/benefit analysis was made, for reference. + +## Original Status + +Proposal. Supersedes the final-step APPEND merge in +[`MERGE_PROTOCOL.md`](./MERGE_PROTOCOL.md). Intermediate PREPEND merges are +untouched. + +## Motivation + +At the final Goblin step, Merge (APPEND) proves +$M_j = T_{\text{prev},j} + X^{\ell} \cdot t_{\text{hiding},j}$ with +$\deg(T_{\text{prev},j}) < \ell$, then hands Translator a commitment +$[X^t M_j]$. + +Two observations make the final merge unnecessary: + +1. **Translator already does the work.** Contribs 65/66/67 of + `TranslatorZeroConstraintsRelationImpl` pin $w_j$ to zero outside the + minicircuit. That bounds $\deg(w_j)$ (Thakur's $G$ is redundant). A + symmetric low-side zero-pin replaces the merge's leading-zero check on + $[L']$ and closes the MegaZK rows-0..3 gap in one relation. +2. **Concatenation is addition when supports are disjoint.** If + $T_{\text{prev},j}$ and $X^\ell t_{\text{hiding},j}$ have disjoint + non-zero supports, then $M_j = T_{\text{prev},j} + X^\ell t_{\text{hiding},j}$ + holds as a polynomial identity — and $[M_j]$ as a plain EC addition. + +Removing the final merge saves 4 $[M_j]$ commitments, $[G]$, the Shplonk +batch for merge, and ~13 field-element evaluations. + +## Design + +### Option A (MegaZK trace reshuffle) — too invasive + +Reshuffle the hiding kernel's Mega trace so `ecc_op` starts at row $\ell+s$ +instead of $s$. Supports become disjoint at the commitment level: +$[X^s M_j] = [X^s T_{\text{prev},j}] + [t_{\text{hiding},j}]$, a plain EC +add. Requires unifying Translator to `RANDOMNESS_START = 5`, a +hiding-kernel trace-layout variant, and touches databus / public-input +positions / trace-overflow logic. + +### Option B (Shplemini k-shifts) + +Keep all Mega trace layouts. $T_{\text{prev},j}$ and $t_{\text{hiding},j}$ +both have data starting at row $s=5$, supports overlap. Resolve via: + +**Shplemini right-shift-by-$k$ opening.** The PCS batcher produces an +opening claim for $X^k \cdot P$ from a single commitment $[P]$ via a +claim-side adjustment — no second commitment. Originally PR #11663, removed +unused by PR #18741, resurrected here. With $k = \ell$, Shplemini delivers +$(X^\ell \cdot t_{\text{hiding},j})(u')$ from $[t_{\text{hiding},j}]$. + +**Split flavor entities.** Translator's op-queue wires become two families: + +``` +OpQueueWires_prev: op_prev, x_lo_y_hi_prev, x_hi_z_1_prev, y_lo_z_2_prev +OpQueueWires_hiding: op_hiding, x_lo_y_hi_hiding, x_hi_z_1_hiding, y_lo_z_2_hiding +``` + +The three to-be-shifted wires in each family have shift-by-1 mirrors (6 +`_shift` entities total); `op` stays non-shiftable in both (2 entities +total). `_prev` commitments come from tail-kernel public inputs, `_hiding` +from hiding-kernel witness commitments. + +**Virtual op-queue wire.** Translator's existing relations are rewritten +wherever they read `x_lo_y_hi / x_hi_z_1 / y_lo_z_2 / op`: + +$$\text{op}_j \;\longrightarrow\; T_{\text{prev},j} + t_{\text{hiding},j\text{-shift-}\ell}$$ + +$$\text{op}_j^{\text{-shift-1}} \;\longrightarrow\; T_{\text{prev},j\text{-shift-1}} + t_{\text{hiding},j\text{-shift-}(\ell+1)}$$ + +Because $T_{\text{prev},j}$ has data only on $[s, s+\ell)$ and +$X^\ell t_{\text{hiding},j}$ only on $[s+\ell, \ldots)$, supports are +disjoint — no selector, no relation-degree increase. + +### Complete relation-side change list + +1. Substitute op-queue reads per the formulas above. Mechanical. +2. Contribs 65/66/67 continue to enforce $w_j = 0$ outside the minicircuit, + evaluated on the sum. Subsumes the degree bound on $T_{\text{prev}}$. +3. **New subrelation:** + `lagrange_prev · t_{hiding,j-shift-ℓ} = 0`, pinning the shifted + $t_{\text{hiding}}$ to zero on $[s, s+\ell)$. Simultaneously: + - enforces the disjoint-support requirement directly in Translator; + - pins rows 0..4 of unshifted $t_{\text{hiding}}$ (which land at rows + $[\ell, \ell+4]$ of the shifted poly, inside $[s, s+\ell)$ since $s=5$), + closing the MegaZK rows-0..3 gap and replacing `lagrange_hiding_boundary`; + - reuses the already-emitted shifted opening — no unshifted + $t_{\text{hiding}}$ opening is required anywhere. + +$T_{\text{prev}}$'s leading zeros need no extra relation: non-ZK Mega's +`EccOpQueueRelation` pins them, carried structurally through the +intermediate-merge chain. + +### Precise value of $\ell$ + +$\ell$ is a single compile-time constant = **max cumulative prev-table +size** across every Chonk configuration. It plays three roles: k-shift +amount, upper bound of $T_{\text{prev}}$'s data range, and width of +`lagrange_prev`. The only coupling to the MegaZK fix is $\ell \geq s = 5$, +trivially satisfied. + +Row accounting: after right-shift-by-$\ell$, `lagrange_prev` ∩ (non-zero +region of shifted poly) = $[\ell, s+\ell)$, corresponding to rows $[0, s)$ +of unshifted $t_{\text{hiding}}$. That pins rows 0..4. Row 4 is already +zero (pinned by `EccOpQueueRelation` since it's in the main sumcheck loop +and `lagrange_ecc_op(4) = 0`); the extra pin is a harmless no-op. Rows +0..3 are the MegaZK-unconstrained ones that actually need it. + +### Defensive $T_{\text{prev}}$ subrelation + +$$T_{\text{prev},j} \cdot (1 - \texttt{lagrange\_prev}) = 0$$ + +Degree-1, negligible cost, reuses the already-emitted $T_{\text{prev},j}(u')$ +opening. Redundant — cumulative $\deg(T_{\text{prev}}) \leq \ell$ follows +by induction on the PREPEND chain from each intermediate merge's Thakur $G$ +check — but cheap enough to include so the bound is a direct Translator +subrelation rather than an inductive argument over the intermediate merges. + +## Cost + +- Sumcheck relation degree: unchanged. +- 4 extra openings per op-queue wire at $u'$. +- Resurrect Shplemini k-shift batcher support (#11663). +- One flavor file, one relations file, one PCS batcher file. Mega untouched. + +## Interaction with AVM + +Chonk and AVM share one Translator implementation; the two modes differ +only in the precomputed `lagrange_prev` polynomial (and therefore in the +Translator VK). + +- **Chonk mode.** Both incoming subtables are non-empty. `lagrange_prev` + is the indicator of $[s, s+\ell)$ — the rows where $T_{\text{prev}}$ + carries data and where the right-shifted $t_{\text{hiding}}$ must vanish. +- **AVM mode.** `_prev` carries AVM's full ecc_op; the `_hiding` family is + absent (no commitments and no openings are sent for it). `lagrange_prev` + is the indicator of AVM's full ecc_op data range, so + $(1-\texttt{lagrange\_prev})$ vanishes there and the defensive + $T_{\text{prev}} \cdot (1-\texttt{lagrange\_prev}) = 0$ subrelation is + trivially satisfied. The new + $\texttt{lagrange\_prev} \cdot t_{\text{hiding-shift-}\ell} = 0$ + subrelation is vacuous because $t_{\text{hiding}} \equiv 0$. + +The op-queue substitution +$\text{op}_j \to T_{\text{prev},j} + t_{\text{hiding},j\text{-shift-}\ell}$ +is structural — no extra mode selector is needed. In AVM mode the second +summand drops out automatically. + +Consequence: `MegaAvmFlavor` keeps `TRACE_OFFSET = 1`. The +`APPEND_OUTPUT_SHIFT` and the $\kappa^{s-t}$ correction disappear entirely. + +## Implementation Plan + +1. **Resurrect Shplemini k-shifts.** Restore right-shift-by-$k$ support in + `PolynomialBatcher` / `ClaimBatcher` removed by PR #18741. +2. **Translator flavor.** Split op-queue entities into `_prev` / `_hiding` + families with shift-by-1 mirrors on the three shiftable wires. Flag + `_hiding` entities as requiring a k-shift claim with $k = \ell$. Update + entity counts / getters. +3. **Translator relations.** Substitute op-queue reads (item 1 in the + relation list). Add `lagrange_prev · t_{hiding-shift-ℓ} = 0` and (optional) + $T_{\text{prev}} \cdot (1-\texttt{lagrange\_prev}) = 0$. +4. **Translator prover/verifier.** Populate `_prev` and `_hiding` + polynomials from the op queue's two sub-tables; emit/consume both sets of + evaluations at $u'$; register the k-shift claim against Shplemini. +5. **Goblin.** Drop `prove_merge(APPEND)` and the corresponding + `recursively_verify_merge(APPEND)` in the hiding kernel. +6. **Chonk plumbing.** Hand $[X^s T_{\text{prev},j}]$ and + $[t_{\text{hiding},j}]$ to Translator directly; remove the $[X^t M_j]$ + consumer. +7. **AVM Translator VK.** Generate a separate Translator VK whose + `lagrange_prev` is the indicator of AVM's ecc_op data range; in this + mode the verifier consumes only the `_prev` family. +8. **Soundness.** Rewrite the "Hiding kernel ZK soundness" and ZK-budget + sections of `MERGE_PROTOCOL.md` for the new observable set. + + +## Open Questions + +- **$\ell$ constexpr.** Confirm $\ell_{\max}$ is compile-time fixed across + every Chonk configuration (init/intermediate/tail + all app counts). + Load-bearing. +- **ZK argument rewrite.** `MERGE_PROTOCOL.md`'s full-rank analysis is over + $\{\kappa, \kappa^{-1}, u, u', z\}$; the new observable set drops + $\kappa, \kappa^{-1}, [G]$, merge Shplonk quotient, and adds one Shplemini + k-shift opening per op-queue wire. Likely strictly easier (fewer + observables over the same 40 randomness coefficients), but needs a redo. +- **Effect on ECCVM.** None expected. The ECCVM–Translator consistency + check reconciles ECCVM's op-queue view with Translator's via openings at + a shared challenge, not via a shared commitment. In the new design + Translator's op-queue-wire opening at $z$ is + $T_{\text{prev}}(z) + z^\ell \cdot t_{\text{hiding}}(z)$, computed from + openings already in the transcript. ECCVM sees no change. +- **Translator ZK masking.** Contribs 65/66/67 multiply by + `in_minicircuit_or_masked`. The new `lagrange_prev · t_{hiding-shift-ℓ}` + subrelation must use the same factor (or be provably unaffected by + masked-row values), otherwise a masked row at $i \in [\ell, \ell+4)$ could + satisfy it with garbage. From a065944f5803d94ceb104243c8d3d7f07b9e01b2 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Mon, 11 May 2026 11:23:35 +0000 Subject: [PATCH 14/17] Revert "add md" This reverts commit 3186d3866a6802457085d326167669a8289f783f. --- .../FINAL_APPEND_MERGE_IN_TRANSLATOR.md | 265 ------------------ 1 file changed, 265 deletions(-) delete mode 100644 barretenberg/cpp/src/barretenberg/goblin/FINAL_APPEND_MERGE_IN_TRANSLATOR.md diff --git a/barretenberg/cpp/src/barretenberg/goblin/FINAL_APPEND_MERGE_IN_TRANSLATOR.md b/barretenberg/cpp/src/barretenberg/goblin/FINAL_APPEND_MERGE_IN_TRANSLATOR.md deleted file mode 100644 index 6cb382bd762f..000000000000 --- a/barretenberg/cpp/src/barretenberg/goblin/FINAL_APPEND_MERGE_IN_TRANSLATOR.md +++ /dev/null @@ -1,265 +0,0 @@ -# Final APPEND Merge in Translator - -## Status - -**Explored, not pursued.** The cost/benefit ratio is unfavourable; the -final APPEND merge in [`MERGE_PROTOCOL.md`](./MERGE_PROTOCOL.md) is kept. -The analysis below is preserved as a record of the design space. - -### Why we stopped - -The headline savings — drop the final merge, save 4 $[M_j]$ commitments, -$[G]$, the Shplonk merge batch, and ~13 field-element evaluations — are -real but modest. The integration cost grows in three places that did not -appear in the initial sketch: - -1. **Two k values in the PCS, not one.** Translator's decomposition - relation reads each op-queue wire both unshifted and shift-by-1 in the - same row (the `(x_lo, y_hi)` / `(x_hi, z_1)` / `(y_lo, z_2)` pair - pattern). Extending that pattern to a right-shifted hiding wire - requires opening $t_{\text{hiding}}$ at *two* k values, $\ell$ and - $\ell-1$. The Shplemini k-shift batch we restored on the parent branch - supports a single $k$; multi-k is a non-trivial extension. Sidestepping - it requires either (a) a separate `[t_hiding-shift-ℓ]` commitment plus - a univariate consistency check (4 extra commitments per merge), or - (b) reshuffling Translator's witness layout so the shift-by-1 pattern - on op-queue wires goes away — a cross-cutting change to limb storage - and range-constraint pairings. - -2. **ZK-budget bleed-through onto Mega.** Each new opening of - $[t_{\text{hiding}}]$ at the Translator-side multilinear point - $u'$ is an extra observable that Mega's ecc_op_wire masking has to - absorb (the polynomial is committed once by the hiding kernel and - serves both protocols). With multi-k the hiding kernel needs ~2 more - masked rows than today and the soundness analysis in - `MERGE_PROTOCOL.md` (full-rank over - $\{\kappa, \kappa^{-1}, u, u', z\}$) has to be redone over a - different observable set. The "consistency check" alternative - separates the budgets but at the cost of those 4 extra commitments. - -3. **Translator becomes mode-dependent.** Two Translator VKs are needed, - distinguished by the precomputed `lagrange_prev` polynomial (Chonk vs - AVM modes). On its own this is clean — `lagrange_prev` already encodes - the mode without an additional selector — but it adds a generation - path, a VK pinning entry, and a verifier dispatch surface to maintain. - -Each item is tractable in isolation. The aggregate, against the modest -end-state savings, is not worth it. - -### What we'd resurrect this for - -The conclusion is sensitive to assumptions. Reasons to revisit: - -- The k-shift Shplemini PCS gains a multi-k extension for an unrelated - reason, eliminating cost item (1). -- Mega's ecc_op masking is restructured for an unrelated reason such - that extra observables on $[t_{\text{hiding}}]$ are free, eliminating - cost item (2). -- Final-merge cost grows materially (e.g. larger op-queue, more wires), - shifting the savings side of the ledger. - -Until then, intermediate PREPEND merges and the final APPEND merge in -`MERGE_PROTOCOL.md` are the canonical design. - ---- - -The remainder of this document captures the proposal as it stood when -the cost/benefit analysis was made, for reference. - -## Original Status - -Proposal. Supersedes the final-step APPEND merge in -[`MERGE_PROTOCOL.md`](./MERGE_PROTOCOL.md). Intermediate PREPEND merges are -untouched. - -## Motivation - -At the final Goblin step, Merge (APPEND) proves -$M_j = T_{\text{prev},j} + X^{\ell} \cdot t_{\text{hiding},j}$ with -$\deg(T_{\text{prev},j}) < \ell$, then hands Translator a commitment -$[X^t M_j]$. - -Two observations make the final merge unnecessary: - -1. **Translator already does the work.** Contribs 65/66/67 of - `TranslatorZeroConstraintsRelationImpl` pin $w_j$ to zero outside the - minicircuit. That bounds $\deg(w_j)$ (Thakur's $G$ is redundant). A - symmetric low-side zero-pin replaces the merge's leading-zero check on - $[L']$ and closes the MegaZK rows-0..3 gap in one relation. -2. **Concatenation is addition when supports are disjoint.** If - $T_{\text{prev},j}$ and $X^\ell t_{\text{hiding},j}$ have disjoint - non-zero supports, then $M_j = T_{\text{prev},j} + X^\ell t_{\text{hiding},j}$ - holds as a polynomial identity — and $[M_j]$ as a plain EC addition. - -Removing the final merge saves 4 $[M_j]$ commitments, $[G]$, the Shplonk -batch for merge, and ~13 field-element evaluations. - -## Design - -### Option A (MegaZK trace reshuffle) — too invasive - -Reshuffle the hiding kernel's Mega trace so `ecc_op` starts at row $\ell+s$ -instead of $s$. Supports become disjoint at the commitment level: -$[X^s M_j] = [X^s T_{\text{prev},j}] + [t_{\text{hiding},j}]$, a plain EC -add. Requires unifying Translator to `RANDOMNESS_START = 5`, a -hiding-kernel trace-layout variant, and touches databus / public-input -positions / trace-overflow logic. - -### Option B (Shplemini k-shifts) - -Keep all Mega trace layouts. $T_{\text{prev},j}$ and $t_{\text{hiding},j}$ -both have data starting at row $s=5$, supports overlap. Resolve via: - -**Shplemini right-shift-by-$k$ opening.** The PCS batcher produces an -opening claim for $X^k \cdot P$ from a single commitment $[P]$ via a -claim-side adjustment — no second commitment. Originally PR #11663, removed -unused by PR #18741, resurrected here. With $k = \ell$, Shplemini delivers -$(X^\ell \cdot t_{\text{hiding},j})(u')$ from $[t_{\text{hiding},j}]$. - -**Split flavor entities.** Translator's op-queue wires become two families: - -``` -OpQueueWires_prev: op_prev, x_lo_y_hi_prev, x_hi_z_1_prev, y_lo_z_2_prev -OpQueueWires_hiding: op_hiding, x_lo_y_hi_hiding, x_hi_z_1_hiding, y_lo_z_2_hiding -``` - -The three to-be-shifted wires in each family have shift-by-1 mirrors (6 -`_shift` entities total); `op` stays non-shiftable in both (2 entities -total). `_prev` commitments come from tail-kernel public inputs, `_hiding` -from hiding-kernel witness commitments. - -**Virtual op-queue wire.** Translator's existing relations are rewritten -wherever they read `x_lo_y_hi / x_hi_z_1 / y_lo_z_2 / op`: - -$$\text{op}_j \;\longrightarrow\; T_{\text{prev},j} + t_{\text{hiding},j\text{-shift-}\ell}$$ - -$$\text{op}_j^{\text{-shift-1}} \;\longrightarrow\; T_{\text{prev},j\text{-shift-1}} + t_{\text{hiding},j\text{-shift-}(\ell+1)}$$ - -Because $T_{\text{prev},j}$ has data only on $[s, s+\ell)$ and -$X^\ell t_{\text{hiding},j}$ only on $[s+\ell, \ldots)$, supports are -disjoint — no selector, no relation-degree increase. - -### Complete relation-side change list - -1. Substitute op-queue reads per the formulas above. Mechanical. -2. Contribs 65/66/67 continue to enforce $w_j = 0$ outside the minicircuit, - evaluated on the sum. Subsumes the degree bound on $T_{\text{prev}}$. -3. **New subrelation:** - `lagrange_prev · t_{hiding,j-shift-ℓ} = 0`, pinning the shifted - $t_{\text{hiding}}$ to zero on $[s, s+\ell)$. Simultaneously: - - enforces the disjoint-support requirement directly in Translator; - - pins rows 0..4 of unshifted $t_{\text{hiding}}$ (which land at rows - $[\ell, \ell+4]$ of the shifted poly, inside $[s, s+\ell)$ since $s=5$), - closing the MegaZK rows-0..3 gap and replacing `lagrange_hiding_boundary`; - - reuses the already-emitted shifted opening — no unshifted - $t_{\text{hiding}}$ opening is required anywhere. - -$T_{\text{prev}}$'s leading zeros need no extra relation: non-ZK Mega's -`EccOpQueueRelation` pins them, carried structurally through the -intermediate-merge chain. - -### Precise value of $\ell$ - -$\ell$ is a single compile-time constant = **max cumulative prev-table -size** across every Chonk configuration. It plays three roles: k-shift -amount, upper bound of $T_{\text{prev}}$'s data range, and width of -`lagrange_prev`. The only coupling to the MegaZK fix is $\ell \geq s = 5$, -trivially satisfied. - -Row accounting: after right-shift-by-$\ell$, `lagrange_prev` ∩ (non-zero -region of shifted poly) = $[\ell, s+\ell)$, corresponding to rows $[0, s)$ -of unshifted $t_{\text{hiding}}$. That pins rows 0..4. Row 4 is already -zero (pinned by `EccOpQueueRelation` since it's in the main sumcheck loop -and `lagrange_ecc_op(4) = 0`); the extra pin is a harmless no-op. Rows -0..3 are the MegaZK-unconstrained ones that actually need it. - -### Defensive $T_{\text{prev}}$ subrelation - -$$T_{\text{prev},j} \cdot (1 - \texttt{lagrange\_prev}) = 0$$ - -Degree-1, negligible cost, reuses the already-emitted $T_{\text{prev},j}(u')$ -opening. Redundant — cumulative $\deg(T_{\text{prev}}) \leq \ell$ follows -by induction on the PREPEND chain from each intermediate merge's Thakur $G$ -check — but cheap enough to include so the bound is a direct Translator -subrelation rather than an inductive argument over the intermediate merges. - -## Cost - -- Sumcheck relation degree: unchanged. -- 4 extra openings per op-queue wire at $u'$. -- Resurrect Shplemini k-shift batcher support (#11663). -- One flavor file, one relations file, one PCS batcher file. Mega untouched. - -## Interaction with AVM - -Chonk and AVM share one Translator implementation; the two modes differ -only in the precomputed `lagrange_prev` polynomial (and therefore in the -Translator VK). - -- **Chonk mode.** Both incoming subtables are non-empty. `lagrange_prev` - is the indicator of $[s, s+\ell)$ — the rows where $T_{\text{prev}}$ - carries data and where the right-shifted $t_{\text{hiding}}$ must vanish. -- **AVM mode.** `_prev` carries AVM's full ecc_op; the `_hiding` family is - absent (no commitments and no openings are sent for it). `lagrange_prev` - is the indicator of AVM's full ecc_op data range, so - $(1-\texttt{lagrange\_prev})$ vanishes there and the defensive - $T_{\text{prev}} \cdot (1-\texttt{lagrange\_prev}) = 0$ subrelation is - trivially satisfied. The new - $\texttt{lagrange\_prev} \cdot t_{\text{hiding-shift-}\ell} = 0$ - subrelation is vacuous because $t_{\text{hiding}} \equiv 0$. - -The op-queue substitution -$\text{op}_j \to T_{\text{prev},j} + t_{\text{hiding},j\text{-shift-}\ell}$ -is structural — no extra mode selector is needed. In AVM mode the second -summand drops out automatically. - -Consequence: `MegaAvmFlavor` keeps `TRACE_OFFSET = 1`. The -`APPEND_OUTPUT_SHIFT` and the $\kappa^{s-t}$ correction disappear entirely. - -## Implementation Plan - -1. **Resurrect Shplemini k-shifts.** Restore right-shift-by-$k$ support in - `PolynomialBatcher` / `ClaimBatcher` removed by PR #18741. -2. **Translator flavor.** Split op-queue entities into `_prev` / `_hiding` - families with shift-by-1 mirrors on the three shiftable wires. Flag - `_hiding` entities as requiring a k-shift claim with $k = \ell$. Update - entity counts / getters. -3. **Translator relations.** Substitute op-queue reads (item 1 in the - relation list). Add `lagrange_prev · t_{hiding-shift-ℓ} = 0` and (optional) - $T_{\text{prev}} \cdot (1-\texttt{lagrange\_prev}) = 0$. -4. **Translator prover/verifier.** Populate `_prev` and `_hiding` - polynomials from the op queue's two sub-tables; emit/consume both sets of - evaluations at $u'$; register the k-shift claim against Shplemini. -5. **Goblin.** Drop `prove_merge(APPEND)` and the corresponding - `recursively_verify_merge(APPEND)` in the hiding kernel. -6. **Chonk plumbing.** Hand $[X^s T_{\text{prev},j}]$ and - $[t_{\text{hiding},j}]$ to Translator directly; remove the $[X^t M_j]$ - consumer. -7. **AVM Translator VK.** Generate a separate Translator VK whose - `lagrange_prev` is the indicator of AVM's ecc_op data range; in this - mode the verifier consumes only the `_prev` family. -8. **Soundness.** Rewrite the "Hiding kernel ZK soundness" and ZK-budget - sections of `MERGE_PROTOCOL.md` for the new observable set. - - -## Open Questions - -- **$\ell$ constexpr.** Confirm $\ell_{\max}$ is compile-time fixed across - every Chonk configuration (init/intermediate/tail + all app counts). - Load-bearing. -- **ZK argument rewrite.** `MERGE_PROTOCOL.md`'s full-rank analysis is over - $\{\kappa, \kappa^{-1}, u, u', z\}$; the new observable set drops - $\kappa, \kappa^{-1}, [G]$, merge Shplonk quotient, and adds one Shplemini - k-shift opening per op-queue wire. Likely strictly easier (fewer - observables over the same 40 randomness coefficients), but needs a redo. -- **Effect on ECCVM.** None expected. The ECCVM–Translator consistency - check reconciles ECCVM's op-queue view with Translator's via openings at - a shared challenge, not via a shared commitment. In the new design - Translator's op-queue-wire opening at $z$ is - $T_{\text{prev}}(z) + z^\ell \cdot t_{\text{hiding}}(z)$, computed from - openings already in the transcript. ECCVM sees no change. -- **Translator ZK masking.** Contribs 65/66/67 multiply by - `in_minicircuit_or_masked`. The new `lagrange_prev · t_{hiding-shift-ℓ}` - subrelation must use the same factor (or be provably unaffected by - masked-row values), otherwise a masked row at $i \in [\ell, \ell+4)$ could - satisfy it with garbage. From aa33a8799f0653fd91d3fd39de1917cf6ecbf34f Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Mon, 11 May 2026 11:23:35 +0000 Subject: [PATCH 15/17] Revert "k shifts resurrected" This reverts commit 337d3bb97e4d044b42f953c23b8ba63c30ff09e6. --- .../commitment_schemes/claim_batcher.hpp | 23 ++---------- .../commitment_schemes/gemini/gemini.hpp | 36 ------------------- .../commitment_schemes/ipa/ipa.test.cpp | 28 --------------- .../commitment_schemes/kzg/kzg.test.cpp | 34 ------------------ .../utils/mock_witness_generator.hpp | 35 ++---------------- .../barretenberg/polynomials/polynomial.cpp | 11 ------ .../barretenberg/polynomials/polynomial.hpp | 6 ---- .../polynomials/polynomial.test.cpp | 28 --------------- 8 files changed, 6 insertions(+), 195 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp index b724ceeb81b7..4f7e3370c2da 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/claim_batcher.hpp @@ -34,26 +34,20 @@ template struct ClaimBatcher_ { Fr scalar = 0; }; - std::optional unshifted; // commitments and evaluations of unshifted polynomials - std::optional shifted; // commitments of to-be-shifted-by-1 polys, evals of their shifts - std::optional right_shifted_by_k; // commitments of to-be-right-shifted-by-k polys, evals of their shifts - - // Magnitude of the right-shift-by-k applied to `right_shifted_by_k` polys (assumed even). - size_t k_shift_magnitude = 0; + std::optional unshifted; // commitments and evaluations of unshifted polynomials + std::optional shifted; // commitments of to-be-shifted-by-1 polys, evals of their shifts Batch get_unshifted() { return (unshifted) ? *unshifted : Batch{}; } Batch get_shifted() { return (shifted) ? *shifted : Batch{}; } - Batch get_right_shifted_by_k() { return (right_shifted_by_k) ? *right_shifted_by_k : Batch{}; } Fr get_unshifted_batch_scalar() const { return unshifted ? unshifted->scalar : Fr{ 0 }; } /** * @brief Compute scalars used to batch each set of claims, excluding contribution from batching challenge \rho - * @details Computes scalars s_0, s_1, s_2 given by + * @details Computes scalars s_0, s_1 given by * \f[ * - s_0 = \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right) \f], * - s_1 = \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right) - * - s_2 = r^{k} \times \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right) * \f] * where the scalars used to batch the claims are given by * \f[ @@ -87,11 +81,6 @@ template struct ClaimBatcher_ { shifted->scalar = r_challenge.invert() * (inverse_vanishing_eval_pos - nu_challenge * inverse_vanishing_eval_neg); } - if (right_shifted_by_k) { - // r^k ⋅ (1/(z−r) + ν/(z+r)) - right_shifted_by_k->scalar = r_challenge.pow(static_cast(k_shift_magnitude)) * - (inverse_vanishing_eval_pos + nu_challenge * inverse_vanishing_eval_neg); - } } /** * @brief Append the commitments and scalars from each batch of claims to the Shplemini vectors which subsequently @@ -111,7 +100,6 @@ template struct ClaimBatcher_ { size_t num_powers = 0; num_powers += unshifted.has_value() ? unshifted->commitments.size() : 0; num_powers += shifted.has_value() ? shifted->commitments.size() : 0; - num_powers += right_shifted_by_k.has_value() ? right_shifted_by_k->commitments.size() : 0; Fr rho_power = Fr(1); size_t power_idx = 0; @@ -140,11 +128,6 @@ template struct ClaimBatcher_ { // i-th shifted commitments will be multiplied by ρ^{num_unshifted + i} and r⁻¹ ⋅ (1/(z−r) − ν/(z+r)) aggregate_claim_data_and_update_batched_evaluation(*shifted); } - if (right_shifted_by_k) { - // i-th right-shifted-by-k commitment will be multiplied by ρ^{num_unshifted + num_shifted + i} and - // r^k ⋅ (1/(z−r) + ν/(z+r)) - aggregate_claim_data_and_update_batched_evaluation(*right_shifted_by_k); - } BB_ASSERT_EQ(power_idx, num_powers); } diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp index d0436f4f15b3..0cc9972c85c2 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp @@ -132,19 +132,15 @@ template class GeminiProver_ { Polynomial batched_unshifted; // linear combination of unshifted polynomials Polynomial batched_to_be_shifted_by_one; // linear combination of to-be-shifted polynomials - Polynomial batched_to_be_shifted_by_k; // linear combination of to-be-right-shifted-by-k polynomials // Batched tails: small polynomials covering only the tail region (e.g. last NUM_MASKED_ROWS positions). // Populated during compute_batched if tails are registered. Polynomial batched_unshifted_tail_; Polynomial batched_shifted_tail_; - size_t k_shift_magnitude = 0; // magnitude of right-shift-by-k (assumed even) - public: RefVector unshifted; // set of unshifted polynomials RefVector to_be_shifted_by_one; // set of polynomials to be left shifted by 1 - RefVector to_be_shifted_by_k; // set of polynomials to be right shifted by k // Tails: small polynomials (e.g. masking values) to be batched with the same rho scalar // as their corresponding base polynomial. Pairs of (index in unshifted/shifted list, tail poly). @@ -160,18 +156,10 @@ template class GeminiProver_ { bool has_unshifted() const { return unshifted.size() > 0; } bool has_to_be_shifted_by_one() const { return to_be_shifted_by_one.size() > 0; } - bool has_to_be_shifted_by_k() const { return to_be_shifted_by_k.size() > 0; } // Set references to the polynomials to be batched void set_unshifted(RefVector polynomials) { unshifted = polynomials; } void set_to_be_shifted_by_one(RefVector polynomials) { to_be_shifted_by_one = polynomials; } - void set_to_be_shifted_by_k(RefVector polynomials, const size_t shift_magnitude) - { - // k must be even for the +/- evaluation formulas to share a sign convention - BB_ASSERT_EQ(shift_magnitude % 2, static_cast(0)); - to_be_shifted_by_k = polynomials; - k_shift_magnitude = shift_magnitude; - } void add_unshifted_tail(size_t batcher_index, Polynomial&& tail) { @@ -248,13 +236,6 @@ template class GeminiProver_ { full_batched += batched_shifted_tail_.shifted(); } - if (has_to_be_shifted_by_k()) { - batched_to_be_shifted_by_k = - Polynomial(full_batched_size - k_shift_magnitude, full_batched_size, /*start_index=*/0); - batch(batched_to_be_shifted_by_k, to_be_shifted_by_k); - full_batched += batched_to_be_shifted_by_k.right_shifted(k_shift_magnitude); // A₀ += X^k * H - } - return full_batched; } @@ -277,25 +258,8 @@ template class GeminiProver_ { A_0_pos = std::move(A_0_extended); } - // The k-shift contribution lives on [0, full_batched_size - k); ensure the running - // accumulators cover that range before adding (mirrors the tail-extension pattern above). - if (has_to_be_shifted_by_k()) { - const size_t needed_end = full_batched_size - k_shift_magnitude; - if (A_0_pos.end_index() < needed_end) { - Polynomial A_0_extended(A_0_pos, needed_end - A_0_pos.start_index()); - A_0_pos = std::move(A_0_extended); - } - } - Polynomial A_0_neg = A_0_pos; - if (has_to_be_shifted_by_k()) { - const Fr r_pow_k = - r_challenge.pow(static_cast(k_shift_magnitude)); // r^k (k even ⇒ same sign at ±r) - A_0_pos.add_scaled(batched_to_be_shifted_by_k, r_pow_k); - A_0_neg.add_scaled(batched_to_be_shifted_by_k, r_pow_k); - } - Fr r_inv = r_challenge.invert(); if (has_to_be_shifted_by_one()) { A_0_pos.add_scaled(batched_to_be_shifted_by_one, r_inv); diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp index de4d6da57d90..987bed0343c8 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -309,34 +309,6 @@ TEST_F(IPATest, ShpleminiIPAWithShift) EXPECT_EQ(result, true); } -// Shplemini + IPA. Verifies a flow with both shift-by-1 and right-shift-by-k claims. -TEST_F(IPATest, ShpleminiIPAWithShiftAndKShift) -{ - auto mle_opening_point = this->random_evaluation_point(log_n); - MockClaimGenerator mock_claims(n, - /*num_polynomials*/ 5, - /*num_to_be_shifted*/ 2, - mle_opening_point, - ck, - /*num_to_be_right_shifted_by_k*/ 2); - auto prover_transcript = NativeTranscript::test_prover_init_empty(); - - auto prover_opening_claims = - GeminiProver::prove(n, mock_claims.polynomial_batcher, mle_opening_point, ck, prover_transcript); - const auto opening_claim = ShplonkProver::prove(ck, prover_opening_claims, prover_transcript); - PCS::compute_opening_proof(ck, opening_claim, prover_transcript); - - auto verifier_transcript = NativeTranscript::test_verifier_init_empty(prover_transcript); - - const auto batch_opening_claim = - ShpleminiVerifier::compute_batch_opening_claim( - mock_claims.claim_batcher, mle_opening_point, vk.get_g1_identity(), verifier_transcript) - .batch_opening_claim; - - auto result = PCS::reduce_verify_batch_opening_claim(batch_opening_claim, vk, verifier_transcript); - - EXPECT_EQ(result, true); -} // Test `ShpleminiVerifier::remove_shifted_commitments`. Four polynomials, two of which are shifted. TEST_F(IPATest, ShpleminiIPAShiftsRemoval) { diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp index 98ca678ce4ef..53518cd4a51c 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp @@ -225,40 +225,6 @@ TEST_F(KZGTest, ShpleminiKzgWithShift) EXPECT_EQ(pairing_points.check(), true); } -// Verifies a Shplemini → KZG flow with both shift-by-1 and right-shift-by-k claims. -TEST_F(KZGTest, ShpleminiKzgWithShiftAndKShift) -{ - std::vector mle_opening_point = random_evaluation_point(log_n); - - MockClaimGenerator mock_claims(n, - /*num_polynomials*/ 5, - /*num_to_be_shifted*/ 2, - mle_opening_point, - ck, - /*num_to_be_right_shifted_by_k*/ 2); - - auto prover_transcript = NativeTranscript::test_prover_init_empty(); - - auto prover_opening_claims = - GeminiProver::prove(n, mock_claims.polynomial_batcher, mle_opening_point, ck, prover_transcript); - - const auto opening_claim = ShplonkProver::prove(ck, prover_opening_claims, prover_transcript); - - PCS::compute_opening_proof(ck, opening_claim, prover_transcript); - - auto verifier_transcript = NativeTranscript::test_verifier_init_empty(prover_transcript); - - auto batch_opening_claim = - ShpleminiVerifier::compute_batch_opening_claim( - mock_claims.claim_batcher, mle_opening_point, vk.get_g1_identity(), verifier_transcript) - .batch_opening_claim; - - const auto pairing_points = - PCS::reduce_verify_batch_opening_claim(std::move(batch_opening_claim), verifier_transcript); - - EXPECT_EQ(pairing_points.check(), true); -} - TEST_F(KZGTest, ShpleminiKzgWithShiftAndInterleaving) { std::vector mle_opening_point = random_evaluation_point(log_n); // sometimes denoted 'u' diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/mock_witness_generator.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/mock_witness_generator.hpp index 3918f165d0dd..37101b69e0ce 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/mock_witness_generator.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/mock_witness_generator.hpp @@ -34,16 +34,12 @@ template struct MockClaimGenerator { ClaimData unshifted; ClaimData to_be_shifted; - ClaimData to_be_right_shifted_by_k; std::vector const_size_mle_opening_point; PolynomialBatcher polynomial_batcher; ClaimBatcher claim_batcher; - // Mock right-shift magnitude (must be even). - static constexpr size_t k_magnitude = 6; - // Containers for mock Sumcheck data std::vector> round_univariates; std::vector sumcheck_commitments; @@ -64,8 +60,7 @@ template struct MockClaimGenerator { const size_t num_polynomials, const size_t num_to_be_shifted, const std::vector& mle_opening_point, - const CommitmentKey& commitment_key, - const size_t num_to_be_right_shifted_by_k = 0) + const CommitmentKey& commitment_key) : ck(commitment_key) // Initialize the commitment key , polynomial_batcher(poly_size) @@ -86,9 +81,8 @@ template struct MockClaimGenerator { challenge = std::span(mle_opening_point); } - const size_t total_num_to_be_shifted = num_to_be_shifted + num_to_be_right_shifted_by_k; - BB_ASSERT_GTE(num_polynomials, total_num_to_be_shifted); - const size_t num_not_to_be_shifted = num_polynomials - total_num_to_be_shifted; + BB_ASSERT_GTE(num_polynomials, num_to_be_shifted); + const size_t num_not_to_be_shifted = num_polynomials - num_to_be_shifted; Fr ebz_factor = 1; @@ -117,36 +111,13 @@ template struct MockClaimGenerator { unshifted.polys.push_back(std::move(poly)); } - // Construct claim data for polynomials that are to-be-right-shifted-by-k. The base poly's data range is - // [0, poly_size - k_magnitude); its right-shift-by-k therefore lives on [k_magnitude, poly_size). - for (size_t idx = 0; idx < num_to_be_right_shifted_by_k; idx++) { - Polynomial poly = Polynomial::random(poly_size - k_magnitude, poly_size, 0); - Commitment commitment = ck.commit(poly); - to_be_right_shifted_by_k.commitments.push_back(commitment); - to_be_right_shifted_by_k.evals.push_back(poly.right_shifted(k_magnitude).evaluate_mle(challenge) * - ebz_factor); - to_be_right_shifted_by_k.polys.push_back(poly.share()); - // Populate the unshifted counterpart in the unshifted claims - unshifted.commitments.push_back(commitment); - unshifted.evals.push_back(poly.evaluate_mle(challenge) * ebz_factor); - unshifted.polys.push_back(std::move(poly)); - } - polynomial_batcher.set_unshifted(RefVector(unshifted.polys)); polynomial_batcher.set_to_be_shifted_by_one(RefVector(to_be_shifted.polys)); - if (num_to_be_right_shifted_by_k > 0) { - polynomial_batcher.set_to_be_shifted_by_k(RefVector(to_be_right_shifted_by_k.polys), k_magnitude); - } claim_batcher = ClaimBatcher{ .unshifted = ClaimBatch{ RefVector(unshifted.commitments), RefVector(unshifted.evals) }, .shifted = ClaimBatch{ RefVector(to_be_shifted.commitments), RefVector(to_be_shifted.evals) } }; - if (num_to_be_right_shifted_by_k > 0) { - claim_batcher.right_shifted_by_k = ClaimBatch{ RefVector(to_be_right_shifted_by_k.commitments), - RefVector(to_be_right_shifted_by_k.evals) }; - claim_batcher.k_shift_magnitude = k_magnitude; - } } // Generate zero polynomials to test edge cases in PCS diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp index 9e17e3c4a42c..edb44a863612 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp @@ -332,17 +332,6 @@ template Polynomial Polynomial::shifted() const return result; } -template Polynomial Polynomial::right_shifted(const size_t magnitude) const -{ - // The last `magnitude` virtual coefficients must be zero so the shift fits within `virtual_size()`. - BB_ASSERT_LTE(coefficients_.end_ + magnitude, virtual_size()); - Polynomial result; - result.coefficients_ = coefficients_; - result.coefficients_.start_ += magnitude; - result.coefficients_.end_ += magnitude; - return result; -} - template Polynomial Polynomial::reverse() const { const size_t end_index = this->end_index(); diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp index 119c4b5050a5..7ac33b963303 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp @@ -195,12 +195,6 @@ template class Polynomial { */ Polynomial shifted() const; - /** - * @brief Returns a Polynomial equal to the right-shift-by-magnitude of self. - * @note Resulting Polynomial shares the memory of that used to generate it. - */ - Polynomial right_shifted(size_t magnitude) const; - /** * @brief Returns the polynomial equal to the reverse of self * diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.test.cpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.test.cpp index 527e85966ad1..2f2e45eba77e 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.test.cpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.test.cpp @@ -29,34 +29,6 @@ TEST(Polynomial, Shifted) } } -// Simple test/demonstration of right_shifted functionality -TEST(Polynomial, RightShifted) -{ - using FF = bb::fr; - using Polynomial = bb::Polynomial; - const size_t SIZE = 10; - const size_t VIRTUAL_SIZE = 20; - const size_t START_IDX = 2; - const size_t SHIFT_MAGNITUDE = 5; - auto poly = Polynomial::random(SIZE, VIRTUAL_SIZE, START_IDX); - - auto poly_shifted = poly.right_shifted(SHIFT_MAGNITUDE); - - EXPECT_EQ(poly_shifted.size(), poly.size()); - EXPECT_EQ(poly_shifted.virtual_size(), poly.virtual_size()); - - // The shift is indeed the shift - for (size_t i = 0; i < SIZE; ++i) { - EXPECT_EQ(poly_shifted.get(i + SHIFT_MAGNITUDE), poly.get(i)); - } - - // The shifted view shares memory with the original - poly.at(3) = 25; - for (size_t i = 0; i < SIZE; ++i) { - EXPECT_EQ(poly_shifted.get(i + SHIFT_MAGNITUDE), poly.get(i)); - } -} - // Simple test/demonstration of reverse functionality TEST(Polynomial, Reversed) { From 22d8ca8660e6f53685d7d810f6f964e2c620e33e Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Mon, 11 May 2026 11:31:25 +0000 Subject: [PATCH 16/17] add a static assert --- .../translator_vm/translator_flavor.hpp | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp index 19859cd96f8d..b590e0ff5634 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp @@ -30,6 +30,13 @@ namespace bb { +namespace detail { +template struct ref_array_extent; +template struct ref_array_extent> { + static constexpr std::size_t value = N; +}; +} // namespace detail + class TranslatorFlavor { public: @@ -1260,4 +1267,17 @@ class TranslatorFlavor { using VerifierCommitments = VerifierCommitments_; }; +// Guard against drift between the runtime PCS entity lists and their compile-time counts; +// REPEATED_COMMITMENTS and PROOF_LENGTH depend on these counts and desync silently otherwise. +static_assert(detail::ref_array_extent&>() + .get_pcs_unshifted())>::value == + TranslatorFlavor::NUM_PCS_UNSHIFTED, + "get_pcs_unshifted() entity count must equal NUM_PCS_UNSHIFTED. If you added a witness entity, " + "update both the runtime list and NUM_UNSHIFTED_WITNESSES_WITHOUT_CONCATENATED."); +static_assert(detail::ref_array_extent&>() + .get_pcs_to_be_shifted())>::value == + TranslatorFlavor::NUM_PCS_TO_BE_SHIFTED, + "get_pcs_to_be_shifted() entity count must equal NUM_PCS_TO_BE_SHIFTED. If you added a to-be-shifted " + "entity, update both the runtime list and NUM_TO_BE_SHIFTED."); + } // namespace bb From cfe3a4d9ef474274d915d383e5d57b59fcd137be Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Mon, 11 May 2026 11:47:07 +0000 Subject: [PATCH 17/17] fix gate count --- .../src/barretenberg/dsl/acir_format/gate_count_constants.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp index b7ed640ce3a2..e4f6adb5cc71 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp @@ -114,7 +114,7 @@ constexpr std::tuple HONK_RECURSION_CONSTANTS( // ======================================== // Gate count for Chonk recursive verification (Ultra with RollupIO) -inline constexpr size_t CHONK_RECURSION_GATES = 1563538; +inline constexpr size_t CHONK_RECURSION_GATES = 1563553; // ======================================== // Hypernova Recursion Constants