diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp index 02eae628ce34..8fb8d96a31a1 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp @@ -366,8 +366,6 @@ template class GeminiVerifier_ { { const size_t log_n = multilinear_challenge.size(); const bool has_interleaved = claim_batcher.interleaved.has_value(); - // GeminiVerifier is only used in tests, so no padding required. - static constexpr bool use_padding = false; const Fr rho = transcript->template get_challenge("rho"); @@ -450,10 +448,11 @@ template class GeminiVerifier_ { p_pos = transcript->template receive_from_prover("Gemini:P_0_pos"); p_neg = transcript->template receive_from_prover("Gemini:P_0_neg"); } + std::vector padding_indicator_array(log_n, Fr{ 1 }); // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., m-1 - std::vector gemini_fold_pos_evaluations = compute_fold_pos_evaluations( - log_n, batched_evaluation, multilinear_challenge, r_squares, evaluations, p_neg); + std::vector gemini_fold_pos_evaluations = compute_fold_pos_evaluations( + padding_indicator_array, batched_evaluation, multilinear_challenge, r_squares, evaluations, p_neg); // Extract the evaluation A₀(r) = A₀₊(r) + P₊(r^s) auto full_a_0_pos = gemini_fold_pos_evaluations[0]; std::vector> fold_polynomial_opening_claims; @@ -539,27 +538,33 @@ template class GeminiVerifier_ { * In the case of interleaving, the first "negative" evaluation has to be corrected by the contribution from \f$ * P_{-}(-r^s)\f$, where \f$ s \f$ is the size of the group to be interleaved. * - * @param log_n The log of the size of the polynomial being opened. + * This method uses `padding_indicator_array`, whose i-th entry is FF{1} if i < log_n and 0 otherwise. + * We use these entries to either assign `eval_pos_prev` the value `eval_pos` computed in the current iteration of + * the loop, or to propagate the batched evaluation of the multilinear polynomials to the next iteration. This + * ensures the correctnes of the computation of the required positive evaluations. + * + * To ensure that dummy evaluations cannot be used to tamper with the final batch_mul result, we multiply dummy + * positive evaluations by the entries of `padding_indicator_array`. + * + * @param padding_indicator_array An array with first log_n entries equal to 1, and the remaining entries are 0. * @param batched_evaluation The evaluation of the batched polynomial at \f$ (u_0, \ldots, u_{d-1})\f$. * @param evaluation_point Evaluation point \f$ (u_0, \ldots, u_{d-1}) \f$ padded to CONST_PROOF_SIZE_LOG_N. * @param challenge_powers Powers of \f$ r \f$, \f$ r^2 \), ..., \( r^{2^{d-1}} \f$. * @param fold_neg_evals Evaluations \f$ A_{i-1}(-r^{2^{i-1}}) \f$. - * @return Evaluation \f$ A_0(r) \f$. + * @return \f A_{i}}(r^{2^{i}})\f$ \f$ i = 0, \ldots, \text{virtual_log_n} - 1 \f$. */ - template - static std::vector compute_fold_pos_evaluations( - const size_t log_n, - const Fr& batched_evaluation, - std::span evaluation_point, // CONST_PROOF_SIZE - std::span challenge_powers, // r_squares CONST_PROOF_SIZE_LOG_N - std::span fold_neg_evals, - Fr p_neg = Fr(0)) + static std::vector compute_fold_pos_evaluations(std::span padding_indicator_array, + const Fr& batched_evaluation, + std::span evaluation_point, // size = virtual_log_n + std::span challenge_powers, // size = virtual_log_n + std::span fold_neg_evals, // size = virtual_log_n + Fr p_neg = Fr(0)) { + const size_t virtual_log_n = evaluation_point.size(); + std::vector evals(fold_neg_evals.begin(), fold_neg_evals.end()); Fr eval_pos_prev = batched_evaluation; - // Virtual size allows padding in Shplemini. - const size_t virtual_log_n = use_padding ? evaluation_point.size() : log_n; Fr zero{ 0 }; if constexpr (Curve::is_stdlib_type) { @@ -568,8 +573,6 @@ template class GeminiVerifier_ { std::vector fold_pos_evaluations; fold_pos_evaluations.reserve(virtual_log_n); - // Either a computed eval of A_i at r^{2^i}, or 0 - Fr value_to_emplace; // Add the contribution of P-((-r)ˢ) to get A_0(-r), which is 0 if there are no interleaved polynomials evals[0] += p_neg; @@ -585,29 +588,14 @@ template class GeminiVerifier_ { Fr eval_pos = ((challenge_power * eval_pos_prev * 2) - eval_neg * (challenge_power * (Fr(1) - u) - u)); // Divide by the denominator eval_pos *= (challenge_power * (Fr(1) - u) + u).invert(); - if constexpr (use_padding) { - if constexpr (Curve::is_stdlib_type) { - auto builder = evaluation_point[0].get_context(); - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1114): insecure dummy_round derivation! - stdlib::bool_t dummy_round = stdlib::witness_t(builder, l > log_n); - // If current index is bigger than log_n, we propagate `batched_evaluation` to the next - // round. Otherwise, current `eval_pos` A₍ₗ₋₁₎(−r²⁽ˡ⁻¹⁾) becomes `eval_pos_prev` in the round l-2. - eval_pos_prev = Fr::conditional_assign(dummy_round, eval_pos_prev, eval_pos); - // If current index is bigger than log_n, we emplace 0, which is later multiplied against - // Commitment::one(). - value_to_emplace = Fr::conditional_assign(dummy_round, zero, eval_pos_prev); - - } else { - // Perform the same logic natively - bool dummy_round = l > log_n; - eval_pos_prev = dummy_round ? eval_pos_prev : eval_pos; - value_to_emplace = dummy_round ? zero : eval_pos_prev; - }; - } else { - eval_pos_prev = eval_pos; - value_to_emplace = eval_pos_prev; - } - fold_pos_evaluations.emplace_back(value_to_emplace); + + // If current index is bigger than log_n, we propagate `batched_evaluation` to the next + // round. Otherwise, current `eval_pos` A₍ₗ₋₁₎(−r²⁽ˡ⁻¹⁾) becomes `eval_pos_prev` in the round l-2. + eval_pos_prev = + padding_indicator_array[l - 1] * eval_pos + (Fr{ 1 } - padding_indicator_array[l - 1]) * eval_pos_prev; + // If current index is bigger than log_n, we emplace 0, which is later multiplied against + // Commitment::one(). + fold_pos_evaluations.emplace_back(padding_indicator_array[l - 1] * eval_pos_prev); } std::reverse(fold_pos_evaluations.begin(), fold_pos_evaluations.end()); 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 8835ba455577..7163760c2d61 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -11,8 +11,6 @@ using Curve = curve::Grumpkin; class IPATest : public CommitmentTest { public: - static constexpr bool USE_PADDING = false; - using Fr = typename Curve::ScalarField; using GroupElement = typename Curve::Element; using CK = CommitmentKey; @@ -25,7 +23,7 @@ class IPATest : public CommitmentTest { using ShplonkVerifier = ShplonkVerifier_; using GeminiProver = GeminiProver_; using GeminiVerifier = GeminiVerifier_; - using ShpleminiVerifier = ShpleminiVerifier_; + using ShpleminiVerifier = ShpleminiVerifier_; using ClaimBatcher = ClaimBatcher_; using ClaimBatch = ClaimBatcher::Batch; @@ -305,8 +303,14 @@ TEST_F(IPATest, ShpleminiIPAWithShift) auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript); - const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim( - small_log_n, mock_claims.claim_batcher, mle_opening_point, vk->get_g1_identity(), verifier_transcript); + std::array padding_indicator_array; + std::ranges::fill(padding_indicator_array, Fr{ 1 }); + + const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array, + mock_claims.claim_batcher, + mle_opening_point, + vk->get_g1_identity(), + verifier_transcript); auto result = PCS::reduce_verify_batch_opening_claim(batch_opening_claim, vk, verifier_transcript); // auto result = PCS::reduce_verify(vk, shplonk_verifier_claim, verifier_transcript); @@ -357,7 +361,10 @@ TEST_F(IPATest, ShpleminiIPAShiftsRemoval) // vectors corresponding to the "shifted" commitment auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript); - const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(small_log_n, + std::array padding_indicator_array; + std::ranges::fill(padding_indicator_array, Fr{ 1 }); + + const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array, mock_claims.claim_batcher, mle_opening_point, vk->get_g1_identity(), 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 24fd04890fa0..a0feffba486b 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp @@ -10,7 +10,6 @@ using Curve = curve::BN254; class KZGTest : public CommitmentTest { public: - static constexpr bool USE_PADDING = true; using Fr = typename Curve::ScalarField; using Commitment = typename Curve::AffineElement; using PCS = KZG; @@ -19,7 +18,7 @@ class KZGTest : public CommitmentTest { using ShplonkVerifier = ShplonkVerifier_; using GeminiProver = GeminiProver_; using GeminiVerifier = GeminiVerifier_; - using ShpleminiVerifier = ShpleminiVerifier_; + using ShpleminiVerifier = ShpleminiVerifier_; static constexpr size_t n = 16; static constexpr size_t log_n = 4; @@ -191,8 +190,14 @@ TEST_F(KZGTest, ShpleminiKzgWithShift) // Gemini verifier output: // - claim: d+1 commitments to Fold_{r}^(0), Fold_{-r}^(0), Fold^(l), d+1 evaluations a_0_pos, a_l, l = 0:d-1 - const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim( - log_n, mock_claims.claim_batcher, mle_opening_point, vk->get_g1_identity(), verifier_transcript); + std::array padding_indicator_array; + std::ranges::fill(padding_indicator_array, Fr{ 1 }); + + const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array, + mock_claims.claim_batcher, + mle_opening_point, + vk->get_g1_identity(), + verifier_transcript); const auto pairing_points = PCS::reduce_verify_batch_opening_claim(batch_opening_claim, verifier_transcript); // Final pairing check: e([Q] - [Q_z] + z[W], [1]_2) = e([W], [x]_2) @@ -239,7 +244,10 @@ TEST_F(KZGTest, ShpleminiKzgWithShiftAndInterleaving) // Gemini verifier output: // - claim: d+1 commitments to Fold_{r}^(0), Fold_{-r}^(0), Fold^(l), d+1 evaluations a_0_pos, a_l, l = 0:d-1 - const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(log_n, + std::array padding_indicator_array; + std::ranges::fill(padding_indicator_array, Fr{ 1 }); + + const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array, mock_claims.claim_batcher, mle_opening_point, vk->get_g1_identity(), @@ -306,7 +314,10 @@ TEST_F(KZGTest, ShpleminiKzgShiftsRemoval) // Gemini verifier output: // - claim: d+1 commitments to Fold_{r}^(0), Fold_{-r}^(0), Fold^(l), d+1 evaluations a_0_pos, a_l, l = 0:d-1 - const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(log_n, + std::array padding_indicator_array; + std::ranges::fill(padding_indicator_array, Fr{ 1 }); + + const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array, mock_claims.claim_batcher, mle_opening_point, vk->get_g1_identity(), diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp index d5a4feff2f8c..61af14a73414 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp @@ -186,7 +186,7 @@ template class ShpleminiProver_ { * */ -template class ShpleminiVerifier_ { +template class ShpleminiVerifier_ { using Fr = typename Curve::ScalarField; using GroupElement = typename Curve::Element; using Commitment = typename Curve::AffineElement; @@ -196,9 +196,14 @@ template class ShpleminiVerifier_ { using ClaimBatcher = ClaimBatcher_; public: + /** + * @brief Non-padding version of \ref compute_batch_opening_claim. Used by all native verifiers and by recursive + * Translator and ECCVM verifiers. + * + */ template static BatchOpeningClaim compute_batch_opening_claim( - const size_t log_n, + std::span padding_indicator_array, ClaimBatcher& claim_batcher, const std::vector& multivariate_challenge, const Commitment& g1_identity, @@ -213,11 +218,10 @@ template class ShpleminiVerifier_ { const std::vector>& sumcheck_round_evaluations = {}) { - const bool committed_sumcheck = !sumcheck_round_evaluations.empty(); - - // When padding is enabled, the size of the multilinear challenge may be bigger than the log of `circuit_size`. const size_t virtual_log_n = multivariate_challenge.size(); + const bool committed_sumcheck = !sumcheck_round_evaluations.empty(); + Fr batched_evaluation = Fr{ 0 }; // While Shplemini is not templated on Flavor, we derive ZK flag this way @@ -285,12 +289,8 @@ template class ShpleminiVerifier_ { // Initialize the vector of scalars placing the scalar 1 correposnding to Q_commitment std::vector scalars; - if constexpr (Curve::is_stdlib_type) { - auto builder = shplonk_batching_challenge.get_context(); - scalars.emplace_back(Fr(builder, 1)); - } else { - scalars.emplace_back(Fr(1)); - } + + scalars.emplace_back(Fr(1)); // Compute 1/(z − r), 1/(z + r), 1/(z - r²), 1/(z + r²), … , 1/(z - r^{2^{d-1}}), 1/(z + r^{2^{d-1}}) // These represent the denominators of the summand terms in Shplonk partially evaluated polynomial Q_z @@ -324,7 +324,7 @@ template class ShpleminiVerifier_ { if (claim_batcher.interleaved) { // Currently, the prover places the Interleaving claims before the Gemini dummy claims. // TODO(https://github.com/AztecProtocol/barretenberg/issues/1293): Decouple Gemini from Interleaving. - const size_t interleaved_pos_index = 2 * log_n; + const size_t interleaved_pos_index = 2 * virtual_log_n; const size_t interleaved_neg_index = interleaved_pos_index + 1; shplonk_batching_pos = shplonk_batching_challenge_powers[interleaved_pos_index]; shplonk_batching_neg = shplonk_batching_challenge_powers[interleaved_neg_index]; @@ -343,17 +343,17 @@ template class ShpleminiVerifier_ { // Reconstruct Aᵢ(r²ⁱ) for i=0, ..., d - 1 from the batched evaluation of the multilinear polynomials and // Aᵢ(−r²ⁱ) for i = 0, ..., d - 1. In the case of interleaving, we compute A₀(r) as A₀₊(r) + P₊(r^s). const std::vector gemini_fold_pos_evaluations = - GeminiVerifier_::template compute_fold_pos_evaluations(log_n, - batched_evaluation, - multivariate_challenge, - gemini_eval_challenge_powers, - gemini_fold_neg_evaluations, - p_neg); + GeminiVerifier_::compute_fold_pos_evaluations(padding_indicator_array, + batched_evaluation, + multivariate_challenge, + gemini_eval_challenge_powers, + gemini_fold_neg_evaluations, + p_neg); // Place the commitments to Gemini fold polynomials Aᵢ in the vector of batch_mul commitments, compute the // contributions from Aᵢ(−r²ⁱ) for i=1, … , d − 1 to the constant term accumulator, add corresponding scalars // for the batch mul - batch_gemini_claims_received_from_prover(log_n, + batch_gemini_claims_received_from_prover(padding_indicator_array, fold_commitments, gemini_fold_neg_evaluations, gemini_fold_pos_evaluations, @@ -429,9 +429,11 @@ template class ShpleminiVerifier_ { * \frac{\nu^2}{z - r^2} + \frac{\nu^3}{z + r^2}, * \frac{\nu^4}{z - r^4} + \frac{\nu^5}{z + r^4}, * \ldots, - * \frac{\nu^{2 \cdot d} } {z - r^{2^{d-1}}} + \frac{\nu^{2 \cdot d + 1}}{z + r^{2^{d-1}}}. \f} - * The commitments \f$ [A_1]_1, \ldots, [A_{d-1}]_1 \f$ are multiplied by these scalars in the final `batch_mul` - * perfomed by KZG or IPA. + * \frac{\nu^{2 \cdot d} } {z - r^{2^{d-1}}} + \frac{\nu^{2 \cdot d + 1}}{z + r^{2^{d-1}}} \f} + * and multiplies them against the entries of `padding_indicator_array`. The commitments \f$ [A_1]_1, \ldots, + * [A_{d-1}]_1 \f$ are multiplied by these scalars in the final `batch_mul` perfomed by KZG or IPA. Since + * `padding_indicator_array[i]` = 1 for i < log_n, and 0 otherwise, it ensures that the contributions from "dummy" + * rounds do not affect the final `batch mul`. * * 3. Accumulates the summands of the constant term: * \f{align}{ @@ -439,7 +441,7 @@ template class ShpleminiVerifier_ { * A_i\left(-r^{2^i}\right)}{z+ r^{2^i}} \f} for \f$ i = 1, \ldots, d-1 \f$ and adds them to the * 'constant_term_accumulator'. * - * @param log_n The logarithm of the circuit size, determining the depth of the Gemini protocol. + * @param padding_indicator_array An array with first log_n entries equal to 1, and the remaining entries are 0. * @param fold_commitments A vector containing the commitments to the Gemini fold polynomials \f$ A_i \f$. * @param gemini_neg_evaluations The evaluations of Gemini fold polynomials \f$ A_i \f$ at \f$ -r^{2^i} \f$ for \f$ * i = 0, \ldots, d - 1 \f$. @@ -452,18 +454,17 @@ template class ShpleminiVerifier_ { * @param scalars Output vector where the computed scalars will be stored. * @param constant_term_accumulator The accumulator for the summands of the Shplonk constant term. */ - static void batch_gemini_claims_received_from_prover(const size_t log_n, + static void batch_gemini_claims_received_from_prover(std::span padding_indicator_array, const std::vector& fold_commitments, - const std::vector& gemini_neg_evaluations, - const std::vector& gemini_pos_evaluations, - const std::vector& inverse_vanishing_evals, - const std::vector& shplonk_batching_challenge_powers, + std::span gemini_neg_evaluations, + std::span gemini_pos_evaluations, + std::span inverse_vanishing_evals, + std::span shplonk_batching_challenge_powers, std::vector& commitments, std::vector& scalars, Fr& constant_term_accumulator) { - const size_t virtual_log_n = use_padding ? gemini_neg_evaluations.size() : log_n; - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1159): Decouple constants from primitives. + const size_t virtual_log_n = gemini_neg_evaluations.size(); // Start from 1, because the commitment to A_0 is reconstructed from the commitments to the multilinear // polynomials. The corresponding evaluations are also handled separately. for (size_t j = 1; j < virtual_log_n; ++j) { @@ -482,23 +483,8 @@ template class ShpleminiVerifier_ { constant_term_accumulator += scaling_factor_neg * gemini_neg_evaluations[j] + scaling_factor_pos * gemini_pos_evaluations[j]; - if constexpr (use_padding) { - if constexpr (Curve::is_stdlib_type) { - auto builder = gemini_neg_evaluations[0].get_context(); - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1114): insecure! - stdlib::bool_t dummy_round = stdlib::witness_t(builder, j >= log_n); - Fr zero = Fr(0); - scaling_factor_neg = Fr::conditional_assign(dummy_round, zero, scaling_factor_neg); - scaling_factor_pos = Fr::conditional_assign(dummy_round, zero, scaling_factor_pos); - } else { - if (j >= log_n) { - scaling_factor_neg = 0; - scaling_factor_pos = 0; - } - } - } // Place the scaling factor to the 'scalars' vector - scalars.emplace_back(-scaling_factor_neg - scaling_factor_pos); + scalars.emplace_back(-padding_indicator_array[j] * (scaling_factor_neg + scaling_factor_pos)); // Move com(Aᵢ) to the 'commitments' vector commitments.emplace_back(std::move(fold_commitments[j - 1])); } diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.test.cpp index 678fd48a9ae4..8af96e53efc3 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.test.cpp @@ -152,8 +152,7 @@ TYPED_TEST(ShpleminiTest, CorrectnessOfGeminiClaimBatching) { using Curve = TypeParam::Curve; using GeminiProver = GeminiProver_; - static constexpr bool USE_PADDING = true; - using ShpleminiVerifier = ShpleminiVerifier_; + using ShpleminiVerifier = ShpleminiVerifier_; using ShplonkVerifier = ShplonkVerifier_; using Fr = typename Curve::ScalarField; using GroupElement = typename Curve::Element; @@ -235,18 +234,19 @@ TYPED_TEST(ShpleminiTest, CorrectnessOfGeminiClaimBatching) ShplonkVerifier::compute_inverted_gemini_denominators(shplonk_eval_challenge, r_squares); Fr expected_constant_term_accumulator{ 0 }; + std::vector padding_indicator_array(this->log_n, Fr{ 1 }); std::vector gemini_fold_pos_evaluations = - GeminiVerifier_::template compute_fold_pos_evaluations(this->log_n, - expected_constant_term_accumulator, - mle_opening_point, - r_squares, - prover_evaluations, - expected_constant_term_accumulator); + GeminiVerifier_::compute_fold_pos_evaluations(padding_indicator_array, + expected_constant_term_accumulator, + mle_opening_point, + r_squares, + prover_evaluations, + expected_constant_term_accumulator); std::vector commitments; std::vector scalars; - ShpleminiVerifier::batch_gemini_claims_received_from_prover(this->log_n, + ShpleminiVerifier::batch_gemini_claims_received_from_prover(padding_indicator_array, prover_commitments, prover_evaluations, gemini_fold_pos_evaluations, @@ -272,8 +272,7 @@ TYPED_TEST(ShpleminiTest, ShpleminiZKNoSumcheckOpenings) using ZKData = ZKSumcheckData; using Curve = TypeParam::Curve; using ShpleminiProver = ShpleminiProver_; - static constexpr bool USE_PADDING = true; - using ShpleminiVerifier = ShpleminiVerifier_; + using ShpleminiVerifier = ShpleminiVerifier_; using Fr = typename Curve::ScalarField; using Commitment = typename Curve::AffineElement; using CK = typename TypeParam::CommitmentKey; @@ -289,10 +288,7 @@ TYPED_TEST(ShpleminiTest, ShpleminiZKNoSumcheckOpenings) ZKData zk_sumcheck_data(this->log_n, prover_transcript, ck); // Generate multivariate challenge of size CONST_PROOF_SIZE_LOG_N - std::vector const_size_mle_opening_point = this->random_evaluation_point(CONST_PROOF_SIZE_LOG_N); - // Truncate the multivariate challenge to evaluate prover polynomials (As in Sumcheck) - const std::vector mle_opening_point(const_size_mle_opening_point.begin(), - const_size_mle_opening_point.begin() + this->log_n); + std::vector mle_opening_point = this->random_evaluation_point(this->log_n); // Generate random prover polynomials, compute their evaluations and commitments MockClaimGenerator mock_claims(this->n, @@ -304,19 +300,19 @@ TYPED_TEST(ShpleminiTest, ShpleminiZKNoSumcheckOpenings) // Compute the sum of the Libra constant term and Libra univariates evaluated at Sumcheck challenges const Fr claimed_inner_product = SmallSubgroupIPAProver::compute_claimed_inner_product( - zk_sumcheck_data, const_size_mle_opening_point, this->log_n); + zk_sumcheck_data, mle_opening_point, this->log_n); prover_transcript->template send_to_verifier("Libra:claimed_evaluation", claimed_inner_product); // Instantiate SmallSubgroupIPAProver, this prover sends commitments to Big Sum and Quotient polynomials SmallSubgroupIPAProver small_subgroup_ipa_prover( - zk_sumcheck_data, const_size_mle_opening_point, claimed_inner_product, prover_transcript, ck); + zk_sumcheck_data, mle_opening_point, claimed_inner_product, prover_transcript, ck); small_subgroup_ipa_prover.prove(); // Reduce to KZG or IPA based on the curve used in the test Flavor const auto opening_claim = ShpleminiProver::prove(this->n, mock_claims.polynomial_batcher, - const_size_mle_opening_point, + mle_opening_point, ck, prover_transcript, small_subgroup_ipa_prover.get_witness_polynomials()); @@ -354,9 +350,11 @@ TYPED_TEST(ShpleminiTest, ShpleminiZKNoSumcheckOpenings) bool consistency_checked = true; // Run Shplemini - const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(this->log_n, + std::vector padding_indicator_array(this->log_n, Fr{ 1 }); + + const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array, mock_claims.claim_batcher, - const_size_mle_opening_point, + mle_opening_point, this->vk()->get_g1_identity(), verifier_transcript, {}, @@ -391,8 +389,7 @@ TYPED_TEST(ShpleminiTest, ShpleminiZKWithSumcheckOpenings) using CK = typename TypeParam::CommitmentKey; using ShpleminiProver = ShpleminiProver_; - static constexpr bool USE_PADDING = true; - using ShpleminiVerifier = ShpleminiVerifier_; + using ShpleminiVerifier = ShpleminiVerifier_; std::shared_ptr ck = create_commitment_key(4096); @@ -461,7 +458,9 @@ TYPED_TEST(ShpleminiTest, ShpleminiZKWithSumcheckOpenings) bool consistency_checked = true; // Run Shplemini - const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(this->log_n, + std::vector padding_indicator_array(this->log_n, Fr{ 1 }); + + const auto batch_opening_claim = ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array, mock_claims.claim_batcher, challenge, this->vk()->get_g1_identity(), diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes_recursion/shplemini.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes_recursion/shplemini.test.cpp index c0c53cc49b8c..b4720082a5bc 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes_recursion/shplemini.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes_recursion/shplemini.test.cpp @@ -9,6 +9,7 @@ #include "barretenberg/srs/global_crs.hpp" #include "barretenberg/stdlib/primitives/curves/bn254.hpp" #include "barretenberg/stdlib/primitives/curves/grumpkin.hpp" +#include "barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp" #include "barretenberg/stdlib/transcript/transcript.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" #include @@ -35,8 +36,7 @@ TEST(ShpleminiRecursionTest, ProveAndVerifySingle) using NativePCS = std::conditional_t, KZG, IPA>; using CommitmentKey = typename NativePCS::CK; using ShpleminiProver = ShpleminiProver_; - static constexpr bool USE_PADDING = true; - using ShpleminiVerifier = ShpleminiVerifier_; + using ShpleminiVerifier = ShpleminiVerifier_; using Fr = typename Curve::ScalarField; using NativeFr = typename Curve::NativeCurve::ScalarField; using Transcript = bb::BaseTranscript>; @@ -49,6 +49,8 @@ TEST(ShpleminiRecursionTest, ProveAndVerifySingle) using diff_t = std::vector::difference_type; size_t N = 1 << log_circuit_size; + const auto padding_indicator_array = + stdlib::compute_padding_indicator_array(log_circuit_size); constexpr size_t NUM_POLYS = 5; constexpr size_t NUM_SHIFTED = 2; constexpr size_t NUM_RIGHT_SHIFTED_BY_K = 1; @@ -123,7 +125,7 @@ TEST(ShpleminiRecursionTest, ProveAndVerifySingle) .k_shift_magnitude = MockClaimGen::k_magnitude }; - const auto opening_claim = ShpleminiVerifier::compute_batch_opening_claim(log_circuit_size, + const auto opening_claim = ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array, claim_batcher, u_challenge_in_circuit, Commitment::one(&builder), diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm.test.cpp index 3e5e21eea64c..5d7c6ae75103 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm.test.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm.test.cpp @@ -149,7 +149,7 @@ TEST_F(ECCVMTests, CommittedSumcheck) std::shared_ptr verifier_transcript = std::make_shared(prover_transcript->proof_data); // Execute Sumcheck Verifier - SumcheckVerifier sumcheck_verifier(CONST_ECCVM_LOG_N, verifier_transcript); + SumcheckVerifier sumcheck_verifier(verifier_transcript); SumcheckOutput verifier_output = sumcheck_verifier.verify(relation_parameters, alpha, gate_challenges); // Evaluate prover's round univariates at corresponding challenges and compare them with the claimed evaluations diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp index 1126c5974927..e9a33a8c3e99 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp @@ -18,7 +18,7 @@ namespace bb { bool ECCVMVerifier::verify_proof(const ECCVMProof& proof) { using Curve = typename Flavor::Curve; - using Shplemini = ShpleminiVerifier_; + using Shplemini = ShpleminiVerifier_; using Shplonk = ShplonkVerifier_; using OpeningClaim = OpeningClaim; using ClaimBatcher = ClaimBatcher_; @@ -55,7 +55,7 @@ bool ECCVMVerifier::verify_proof(const ECCVMProof& proof) commitments.z_perm = transcript->template receive_from_prover(commitment_labels.z_perm); // Execute Sumcheck Verifier - SumcheckVerifier sumcheck(CONST_ECCVM_LOG_N, transcript); + SumcheckVerifier sumcheck(transcript); FF alpha = transcript->template get_challenge("Sumcheck:alpha"); std::vector gate_challenges(CONST_ECCVM_LOG_N); for (size_t idx = 0; idx < gate_challenges.size(); idx++) { @@ -84,8 +84,12 @@ bool ECCVMVerifier::verify_proof(const ECCVMProof& proof) .unshifted = ClaimBatch{ commitments.get_unshifted(), sumcheck_output.claimed_evaluations.get_unshifted() }, .shifted = ClaimBatch{ commitments.get_to_be_shifted(), sumcheck_output.claimed_evaluations.get_shifted() } }; + + std::array padding_indicator_array; + std::ranges::fill(padding_indicator_array, FF{ 1 }); + BatchOpeningClaim sumcheck_batch_opening_claims = - Shplemini::compute_batch_opening_claim(CONST_ECCVM_LOG_N, + Shplemini::compute_batch_opening_claim(padding_indicator_array, claim_batcher, sumcheck_output.challenge, key->pcs_verification_key->get_g1_identity(), diff --git a/barretenberg/cpp/src/barretenberg/polynomials/gate_separator.hpp b/barretenberg/cpp/src/barretenberg/polynomials/gate_separator.hpp index 012efeba3aa5..1de867c7b585 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/gate_separator.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/gate_separator.hpp @@ -87,21 +87,6 @@ template struct GateSeparatorPolynomial { */ FF univariate_eval(FF challenge) const { return (FF(1) + (challenge * (betas[current_element_idx] - FF(1)))); }; - /** - * @brief Evaluate \f$ ((1−X_{i}) + X_{i}\cdot \beta_{i})\f$ at the challenge point \f$ X_{i}=u_{i} \f$. - */ - template FF univariate_eval(const FF& challenge, const Bool& dummy_round) const - { - // For the Ultra Recursive flavor to ensure constant size proofs, we perform constant amount of hashing - // producing 28 gate betas and we need to use the betas in the dummy rounds to ensure the permutation related - // selectors stay the same regardless of real circuit size. - FF one{ 1 }; - one.convert_constant_to_fixed_witness(challenge.get_context()); - - FF beta_val = FF::conditional_assign(dummy_round, one, betas[current_element_idx]); - return (FF(1) + (challenge * (beta_val - FF(1)))); - } - /** * @brief Partially evaluate the \f$pow_{\beta} \f$-polynomial at the new challenge and update \f$ c_i \f$ * @details Update the constant \f$c_{i} \to c_{i+1} \f$ multiplying it by \f$pow_{\beta}\f$'s factor \f$\left( @@ -121,13 +106,15 @@ template struct GateSeparatorPolynomial { * @details Update the constant \f$c_{i} \to c_{i+1} \f$ multiplying it by \f$pow_{\beta}\f$'s factor \f$\left( * (1-X_i) + X_i\cdot \beta_i\right)\vert_{X_i = u_i}\f$ computed by \ref univariate_eval. * @param challenge \f$ i \f$-th verifier challenge \f$ u_{i}\f$ + * @param indicator An entry of `padding_indicator_array`, which is equal to 1 when round_idx < log_circuit_size + * and is 0 otherwise. */ - template void partially_evaluate(const FF& challenge, const stdlib::bool_t& dummy) + void partially_evaluate(const FF& challenge, const FF& indicator) { - FF current_univariate_eval = univariate_eval(challenge, dummy); + FF current_univariate_eval = univariate_eval(challenge); // If dummy round, make no update to the partial_evaluation_result - partial_evaluation_result = FF::conditional_assign( - dummy, partial_evaluation_result, partial_evaluation_result * current_univariate_eval); + partial_evaluation_result = (FF(1) - indicator) * partial_evaluation_result + + indicator * partial_evaluation_result * current_univariate_eval; current_element_idx++; periodicity *= 2; } diff --git a/barretenberg/cpp/src/barretenberg/polynomials/row_disabling_polynomial.hpp b/barretenberg/cpp/src/barretenberg/polynomials/row_disabling_polynomial.hpp index 5a46477c017e..8b8fa93ddcb3 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/row_disabling_polynomial.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/row_disabling_polynomial.hpp @@ -180,28 +180,25 @@ template struct RowDisablingPolynomial { return FF{ 1 } - evaluation_at_multivariate_challenge; } + /** - * @brief stdlib version of the above that ensures that the verifier's work does not depend on `log_circuit_size`. + * @brief A variant of the above that uses `padding_indicator_array`. * + * @param multivariate_challenge Sumcheck evaluation challenge + * @param padding_indicator_array An array with first log_n entries equal to 1, and the remaining entries are 0. */ - template - static FF evaluate_at_challenge(std::vector multivariate_challenge, - const size_t log_circuit_size, - Builder* builder) + template + static FF evaluate_at_challenge(std::span multivariate_challenge, + const std::array& padding_indicator_array) { - const size_t virtual_log_n = multivariate_challenge.size(); FF evaluation_at_multivariate_challenge{ 1 }; - const FF one = FF{ 1 }; for (size_t idx = 2; idx < virtual_log_n; idx++) { - stdlib::bool_t dummy_round = stdlib::witness_t(builder, idx >= log_circuit_size); - evaluation_at_multivariate_challenge = - FF::conditional_assign(dummy_round, - evaluation_at_multivariate_challenge * one, - evaluation_at_multivariate_challenge * multivariate_challenge[idx]); + const FF& indicator = padding_indicator_array[idx]; + evaluation_at_multivariate_challenge *= FF{ 1 } - indicator + indicator * multivariate_challenge[idx]; } - return one - evaluation_at_multivariate_challenge; + return FF{ 1 } - evaluation_at_multivariate_challenge; } }; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.cpp index 728f17ac55c8..aefd227c0915 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.cpp @@ -29,7 +29,7 @@ std::pair, std::shared_ptr::verify_proof(const ECCVMProof& proof) { using Curve = typename Flavor::Curve; - using Shplemini = ShpleminiVerifier_; + using Shplemini = ShpleminiVerifier_; using Shplonk = ShplonkVerifier_; using OpeningClaim = OpeningClaim; using ClaimBatcher = ClaimBatcher_; @@ -71,7 +71,7 @@ ECCVMRecursiveVerifier_::verify_proof(const ECCVMProof& proof) commitments.z_perm = transcript->template receive_from_prover(commitment_labels.z_perm); // Execute Sumcheck Verifier - Sumcheck sumcheck(CONST_PROOF_SIZE_LOG_N, transcript); + Sumcheck sumcheck(transcript); const FF alpha = transcript->template get_challenge("Sumcheck:alpha"); std::vector gate_challenges(CONST_ECCVM_LOG_N); for (size_t idx = 0; idx < gate_challenges.size(); idx++) { @@ -95,8 +95,15 @@ ECCVMRecursiveVerifier_::verify_proof(const ECCVMProof& proof) .unshifted = ClaimBatch{ commitments.get_unshifted(), sumcheck_output.claimed_evaluations.get_unshifted() }, .shifted = ClaimBatch{ commitments.get_to_be_shifted(), sumcheck_output.claimed_evaluations.get_shifted() } }; + + FF one{ 1 }; + one.convert_constant_to_fixed_witness(builder); + + std::array padding_indicator_array; + std::ranges::fill(padding_indicator_array, one); + BatchOpeningClaim sumcheck_batch_opening_claims = - Shplemini::compute_batch_opening_claim(CONST_ECCVM_LOG_N, + Shplemini::compute_batch_opening_claim(padding_indicator_array, claim_batcher, sumcheck_output.challenge, key->pcs_verification_key->get_g1_identity(), diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/decider_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/decider_recursive_verifier.cpp index 91eb5d725bfd..5d94b37e6cda 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/decider_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/decider_recursive_verifier.cpp @@ -7,6 +7,7 @@ #include "barretenberg/stdlib/honk_verifier/decider_recursive_verifier.hpp" #include "barretenberg/commitment_schemes/shplonk/shplemini.hpp" #include "barretenberg/numeric/bitop/get_msb.hpp" +#include "barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp" #include "barretenberg/transcript/transcript.hpp" namespace bb::stdlib::recursion::honk { @@ -22,7 +23,7 @@ DeciderRecursiveVerifier_::AggregationObject DeciderRecursiveVerifier_; using PCS = typename Flavor::PCS; using Curve = typename Flavor::Curve; - using Shplemini = ::bb::ShpleminiVerifier_; + using Shplemini = ::bb::ShpleminiVerifier_; using VerifierCommitments = typename Flavor::VerifierCommitments; using Transcript = typename Flavor::Transcript; using ClaimBatcher = ClaimBatcher_; @@ -33,19 +34,22 @@ DeciderRecursiveVerifier_::AggregationObject DeciderRecursiveVerifier_verification_key, accumulator->witness_commitments }; - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1283): fix log_circuit_size usage in stdlib cases. - const size_t log_circuit_size = static_cast(accumulator->verification_key->log_circuit_size.get_value()); - Sumcheck sumcheck(log_circuit_size, transcript, accumulator->target_sum); + const auto padding_indicator_array = + compute_padding_indicator_array(accumulator->verification_key->log_circuit_size); - SumcheckOutput output = - sumcheck.verify(accumulator->relation_parameters, accumulator->alphas, accumulator->gate_challenges); + constrain_log_circuit_size(padding_indicator_array, accumulator->verification_key->circuit_size); + + Sumcheck sumcheck(transcript, accumulator->target_sum); + + SumcheckOutput output = sumcheck.verify( + accumulator->relation_parameters, accumulator->alphas, accumulator->gate_challenges, padding_indicator_array); // Execute Shplemini rounds. ClaimBatcher claim_batcher{ .unshifted = ClaimBatch{ commitments.get_unshifted(), output.claimed_evaluations.get_unshifted() }, .shifted = ClaimBatch{ commitments.get_to_be_shifted(), output.claimed_evaluations.get_shifted() } }; - const auto opening_claim = Shplemini::compute_batch_opening_claim(log_circuit_size, + const auto opening_claim = Shplemini::compute_batch_opening_claim(padding_indicator_array, claim_batcher, output.challenge, Commitment::one(builder), diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp index c9db30587539..bbe9ab5a664f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp @@ -8,6 +8,7 @@ #include "barretenberg/commitment_schemes/shplonk/shplemini.hpp" #include "barretenberg/numeric/bitop/get_msb.hpp" #include "barretenberg/plonk_honk_shared/library/grand_product_delta.hpp" +#include "barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp" #include "barretenberg/stdlib/primitives/public_input_component/public_input_component.hpp" #include "barretenberg/transcript/transcript.hpp" @@ -49,7 +50,7 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ using Sumcheck = ::bb::SumcheckVerifier; using PCS = typename Flavor::PCS; using Curve = typename Flavor::Curve; - using Shplemini = ::bb::ShpleminiVerifier_; + using Shplemini = ::bb::ShpleminiVerifier_; using VerifierCommitments = typename Flavor::VerifierCommitments; using Transcript = typename Flavor::Transcript; using ClaimBatcher = ClaimBatcher_; @@ -73,6 +74,8 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ honk_proof = proof; } transcript = std::make_shared(honk_proof); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1364): Improve VKs. Clarify the usage of + // RecursiveDeciderVK here. Seems unnecessary. auto verification_key = std::make_shared(builder, key); OinkVerifier oink_verifier{ builder, verification_key, transcript }; oink_verifier.verify(); @@ -96,17 +99,21 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ // Execute Sumcheck Verifier and extract multivariate opening point u = (u_0, ..., u_{d-1}) and purported // multivariate evaluations at u - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1283): Suspicious get_value(). - const size_t log_circuit_size = numeric::get_msb(static_cast(key->circuit_size.get_value())); - Sumcheck sumcheck(log_circuit_size, transcript); + + const auto padding_indicator_array = + compute_padding_indicator_array(key->log_circuit_size); + + constrain_log_circuit_size(padding_indicator_array, key->circuit_size); + + auto sumcheck = Sumcheck(transcript); // Receive commitments to Libra masking polynomials std::array libra_commitments = {}; if constexpr (Flavor::HasZK) { libra_commitments[0] = transcript->template receive_from_prover("Libra:concatenation_commitment"); } - SumcheckOutput sumcheck_output = - sumcheck.verify(verification_key->relation_parameters, verification_key->alphas, gate_challenges); + SumcheckOutput sumcheck_output = sumcheck.verify( + verification_key->relation_parameters, verification_key->alphas, gate_challenges, padding_indicator_array); // For MegaZKFlavor: the sumcheck output contains claimed evaluations of the Libra polynomials if constexpr (Flavor::HasZK) { @@ -120,7 +127,7 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ .shifted = ClaimBatch{ commitments.get_to_be_shifted(), sumcheck_output.claimed_evaluations.get_shifted() } }; const BatchOpeningClaim opening_claim = - Shplemini::compute_batch_opening_claim(log_circuit_size, + Shplemini::compute_batch_opening_claim(padding_indicator_array, claim_batcher, sumcheck_output.challenge, Commitment::one(builder), diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.test.cpp index 99b2c58f1f7f..ba363ce0ee58 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.test.cpp @@ -176,10 +176,10 @@ template class RecursiveVerifierTest : public testing }; auto [blocks_10, verification_key_10] = get_blocks(10); - auto [blocks_11, verification_key_11] = get_blocks(11); + auto [blocks_14, verification_key_14] = get_blocks(14); - compare_ultra_blocks_and_verification_keys({ blocks_10, blocks_11 }, - { verification_key_10, verification_key_11 }); + compare_ultra_blocks_and_verification_keys({ blocks_10, blocks_14 }, + { verification_key_10, verification_key_14 }); } /** diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp index 53b2216cc8c6..91f2aca83a77 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp @@ -17,8 +17,8 @@ namespace bb::stdlib { * \f{align}{ \text{indicator_padding_array}[i] = \text{"} i < x \text{"}. \f}. To achieve the strict ineqaulity, we * evaluate all Lagranges at (x-1) and compute step functions. More concretely * - * 1) Constrain x to be in the range \f$ [2, \ldots, N] \f$ by asserting - * \f{align}{ \prod_{i=1}^{N-1} (x - 1 - i) = 0 \f}. + * 1) Constrain x to be in the range \f$ [1, \ldots, N] \f$ by asserting + * \f{align}{ \prod_{i=0}^{N-1} (x - 1 - i) = 0 \f}. * * 2) For \f$ i = 0, ..., N-1 \f$, evaluate \f$ L_i(x) \f$. * Since \f$ 1 < x <= N \f$, \f$ L_i(x - 1) = 1 \f$ if and only if \f$ x - 1 = i \f$. @@ -34,21 +34,20 @@ namespace bb::stdlib { * \f$ [b_0(x-1), \ldots, b_{N-1}(x-1)] \f$ only depends on \f$ N \f$ adding ~\f$ 4\cdot N \f$ gates to the circuit. * */ -template -static std::array compute_padding_indicator_array(const Fr& log_n) +template +static std::array compute_padding_indicator_array( + const typename Curve::ScalarField& log_n) { + using Fr = typename Curve::ScalarField; // Create a domain of size `virtual_log_n` and compute Lagrange denominators using Data = BarycentricDataRunTime; std::array result{}; - Builder* builder = log_n.get_context(); - Fr zero{ 0 }; - zero.convert_constant_to_fixed_witness(builder); // 1) Build prefix products: // prefix[i] = ∏_{m=0..(i-1)} (x - 1 - big_domain[m]), with prefix[0] = 1. - std::vector prefix(virtual_log_n + 1, Fr{ 1 }); - for (size_t i = 0; i < virtual_log_n; ++i) { - prefix[i + 1] = prefix[i] * (log_n - Fr{ 1 } - Data::big_domain[i]); + std::vector prefix(virtual_log_n, Fr{ 1 }); + for (size_t i = 1; i < virtual_log_n; ++i) { + prefix[i] = prefix[i - 1] * (log_n - Fr{ 1 } - Data::big_domain[i - 1]); } // 2) Build suffix products: @@ -62,7 +61,9 @@ static std::array compute_padding_indicator_array(const Fr& l // To ensure 0 < log_n < N, note that suffix[1] = \prod_{i=1}^{N-1} (x - 1 - i), therefore we just need to ensure // that this product is 0. - suffix[1].assert_equal(zero); + if constexpr (Curve::is_stdlib_type) { + suffix[0].assert_equal(Fr{ 0 }); + } // 3) Combine prefixes & suffixes to get L_i(x-1): // L_i(x-1) = (1 / lagrange_denominators[i]) * prefix[i] * suffix[i+1]. @@ -79,4 +80,27 @@ static std::array compute_padding_indicator_array(const Fr& l return result; } +/** + * @brief Given a witness `n` and a padding indicator array computed from `log_n`, check in-circuit that the latter is + * indded the logarithm of `n`. + * + * @details It is crucial that `log_n` is constrained to be in range [1, virtual_log_n], as it forces the first `log_n` + * entries of `padding_indicator_array` to be equal to 1 and the rest of the entries to be equal to 0. This implies that + * `n` can be reconstructed by incrementing each entry in the array and taking their product. + * + * @tparam Fr + * @tparam virtual_log_n + * @param padding_indicator_array + * @param n expected = 2^(log_n) + */ +template +static void constrain_log_circuit_size(const std::array& padding_indicator_array, const Fr& n) +{ + Fr accumulated_circuit_size{ 1 }; + for (auto& indicator : padding_indicator_array) { + accumulated_circuit_size *= indicator + Fr{ 1 }; + }; + + n.assert_equal(accumulated_circuit_size); +} } // namespace bb::stdlib diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.test.cpp index ddf8461c7124..ab36549b5598 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.test.cpp @@ -11,32 +11,36 @@ using namespace bb; namespace { auto& engine = numeric::get_debug_randomness(); -template struct PaddingTestParams { - using Fr = Fr_; +template struct PaddingTestParams { + using Curve = Curve_; using Builder = Builder_; }; template class PaddingIndicatorArrayTest : public testing::Test { public: - using Fr = typename Param::Fr; + using Curve = typename Param::Curve; + using Fr = typename Curve::ScalarField; using Builder = typename Param::Builder; static constexpr size_t domain_size = 25; - public: void test_value_in_range() { - for (size_t idx = 2; idx <= domain_size; idx++) { + for (size_t idx = 1; idx <= domain_size; idx++) { Builder builder; Fr x = Fr::from_witness(&builder, idx); - auto result = compute_padding_indicator_array(x); + auto result = compute_padding_indicator_array(x); EXPECT_TRUE(result[idx - 1].get_value() == 1); info("num gates = ", builder.get_estimated_num_finalized_gates()); // Check that the sum of indicators is indeed x Fr sum_of_indicators = std::accumulate(result.begin(), result.end(), Fr{ 0 }); EXPECT_TRUE((sum_of_indicators == x).get_value()); + // Create a witness = 2^idx + Fr exponent = Fr::from_witness(&builder, 1 << idx); + // Using the indicator values, compute 2^idx in-circuit. + stdlib::constrain_log_circuit_size(result, exponent); // Check the correctness of the circuit EXPECT_TRUE(CircuitChecker::check(builder)); } @@ -51,7 +55,7 @@ template class PaddingIndicatorArrayTest : public testing::Test Fr zero = Fr::from_witness(&builder, 0); - [[maybe_unused]] auto result = compute_padding_indicator_array(zero); + compute_padding_indicator_array(zero); info("num gates = ", builder.get_estimated_num_finalized_gates()); EXPECT_FALSE(CircuitChecker::check(builder)); @@ -63,7 +67,7 @@ template class PaddingIndicatorArrayTest : public testing::Test Fr N = Fr::from_witness(&builder, domain_size); - [[maybe_unused]] auto result = compute_padding_indicator_array(N); + compute_padding_indicator_array(N); info("num gates = ", builder.get_estimated_num_finalized_gates()); EXPECT_TRUE(CircuitChecker::check(builder)); @@ -78,7 +82,7 @@ template class PaddingIndicatorArrayTest : public testing::Test Fr x = Fr::from_witness(&builder, scalar_raw); - [[maybe_unused]] auto result = compute_padding_indicator_array(x); + compute_padding_indicator_array(x); info("num gates = ", builder.get_estimated_num_finalized_gates()); EXPECT_FALSE(CircuitChecker::check(builder)); @@ -87,31 +91,58 @@ template class PaddingIndicatorArrayTest : public testing::Test void test_gate_count_independence() { - auto get_gate_count = [](const uint256_t& scalar_raw) -> size_t { + auto get_gate_count = [](const uint32_t& scalar_raw) -> size_t { Builder builder; Fr x = Fr::from_witness(&builder, scalar_raw); - [[maybe_unused]] auto result = compute_padding_indicator_array(x); + auto result = compute_padding_indicator_array(x); size_t gate_count = builder.get_estimated_num_finalized_gates(); + // Create a witness = 2^(idx) + Fr exponent = Fr::from_witness(&builder, 1UL << scalar_raw); + // Using the indicator values, compute 2^idx in-circuit. + stdlib::constrain_log_circuit_size(result, exponent); return gate_count; }; // Valid input: x in [1, domain_size - 1] - uint256_t x_in_range = (domain_size - 1) / 2; + uint32_t x_in_range = (domain_size - 1) / 2; size_t gates_in_range = get_gate_count(x_in_range); // Random input - uint256_t random_scalar = engine.get_random_uint256(); + uint32_t random_scalar = engine.get_random_uint32(); size_t gates_random = get_gate_count(random_scalar); EXPECT_EQ(gates_in_range, gates_random); } + + void test_log_constraint_failure() + { + for (size_t idx = 1; idx <= domain_size; idx++) { + Builder builder; + Fr x = Fr::from_witness(&builder, idx); + + auto result = compute_padding_indicator_array(x); + EXPECT_TRUE(result[idx - 1].get_value() == 1); + + info("num gates = ", builder.get_estimated_num_finalized_gates()); + // Check that the sum of indicators is indeed x + Fr sum_of_indicators = std::accumulate(result.begin(), result.end(), Fr{ 0 }); + EXPECT_TRUE((sum_of_indicators == x).get_value()); + // Check the correctness of the circuit + EXPECT_TRUE(CircuitChecker::check(builder)); + // Create a witness = 2^idx + Fr exponent = Fr::from_witness(&builder, (1 << idx) + 1); + // Using the indicator values, compute tampered "2^idx" in-circuit. + stdlib::constrain_log_circuit_size(result, exponent); + // Circuit check must fail as 2^(log_n) != n + EXPECT_FALSE(CircuitChecker::check(builder)); + } + } }; using TestTypes = testing::Types< - PaddingTestParams>>::ScalarField, - bb::MegaCircuitBuilder>, - PaddingTestParams::ScalarField, bb::UltraCircuitBuilder>>; + PaddingTestParams>>, bb::MegaCircuitBuilder>, + PaddingTestParams, bb::UltraCircuitBuilder>>; TYPED_TEST_SUITE(PaddingIndicatorArrayTest, TestTypes); @@ -132,4 +163,8 @@ TYPED_TEST(PaddingIndicatorArrayTest, TestGateCountIndependence) { TestFixture::test_gate_count_independence(); } +TYPED_TEST(PaddingIndicatorArrayTest, TestLogConstraintFailure) +{ + TestFixture::test_log_constraint_failure(); +} } // namespace diff --git a/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.cpp index 63968ee297e9..792ca07f80db 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.cpp @@ -69,7 +69,7 @@ TranslatorRecursiveVerifier_::AggregationObject TranslatorRecursiveVerif using Sumcheck = ::bb::SumcheckVerifier; using PCS = typename Flavor::PCS; using Curve = typename Flavor::Curve; - using Shplemini = ::bb::ShpleminiVerifier_; + using Shplemini = ::bb::ShpleminiVerifier_; using VerifierCommitments = typename Flavor::VerifierCommitments; using CommitmentLabels = typename Flavor::CommitmentLabels; using ClaimBatcher = ClaimBatcher_; @@ -103,7 +103,7 @@ TranslatorRecursiveVerifier_::AggregationObject TranslatorRecursiveVerif commitments.z_perm = transcript->template receive_from_prover(commitment_labels.z_perm); // Execute Sumcheck Verifier - Sumcheck sumcheck(TranslatorFlavor::CONST_TRANSLATOR_LOG_N, transcript); + Sumcheck sumcheck(transcript); FF alpha = transcript->template get_challenge("Sumcheck:alpha"); std::vector gate_challenges(TranslatorFlavor::CONST_TRANSLATOR_LOG_N); for (size_t idx = 0; idx < gate_challenges.size(); idx++) { @@ -113,7 +113,13 @@ TranslatorRecursiveVerifier_::AggregationObject TranslatorRecursiveVerif std::array libra_commitments = {}; libra_commitments[0] = transcript->template receive_from_prover("Libra:concatenation_commitment"); - auto sumcheck_output = sumcheck.verify(relation_parameters, alpha, gate_challenges); + FF one{ 1 }; + one.convert_constant_to_fixed_witness(builder); + + std::array padding_indicator_array; + std::ranges::fill(padding_indicator_array, one); + + auto sumcheck_output = sumcheck.verify(relation_parameters, alpha, gate_challenges, padding_indicator_array); libra_commitments[1] = transcript->template receive_from_prover("Libra:grand_sum_commitment"); libra_commitments[2] = transcript->template receive_from_prover("Libra:quotient_commitment"); @@ -128,7 +134,7 @@ TranslatorRecursiveVerifier_::AggregationObject TranslatorRecursiveVerif .evaluations = sumcheck_output.claimed_evaluations.get_interleaved() } }; const BatchOpeningClaim opening_claim = - Shplemini::compute_batch_opening_claim(TranslatorFlavor::CONST_TRANSLATOR_LOG_N, + Shplemini::compute_batch_opening_claim(padding_indicator_array, claim_batcher, sumcheck_output.challenge, Commitment::one(builder), diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_zk_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_zk_recursive_flavor.hpp index c0bdf10bc8e3..4c694b08b8ea 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_zk_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_zk_recursive_flavor.hpp @@ -40,7 +40,6 @@ template class MegaZKRecursiveFlavor_ : public MegaRecurs public: using NativeFlavor = MegaZKFlavor; - // Indicates that this flavor runs with non-ZK Sumcheck. static constexpr bool HasZK = true; // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp index e9713507be83..54bb502d9a1f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp @@ -155,6 +155,11 @@ template class UltraRecursiveFlavor_ { size_t num_frs_read = 0; this->circuit_size = deserialize_from_frs(builder, elements, num_frs_read); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1364): Improve VKs. log_circuit_size must be a + // witness to make the Recursive Verifier circuit constant. Seems that other members also need to be turned + // into witnesses. + this->log_circuit_size = + FF::from_witness(&builder, numeric::get_msb(static_cast(this->circuit_size.get_value()))); this->num_public_inputs = deserialize_from_frs(builder, elements, num_frs_read); this->pub_inputs_offset = deserialize_from_frs(builder, elements, num_frs_read); this->contains_pairing_point_accumulator = diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_rollup_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_rollup_recursive_flavor.hpp index b8b9c67a2eb9..004c81992dd4 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_rollup_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_rollup_recursive_flavor.hpp @@ -98,6 +98,11 @@ template class UltraRollupRecursiveFlavor_ : public Ultra size_t num_frs_read = 0; this->circuit_size = deserialize_from_frs(builder, elements, num_frs_read); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1364): Improve VKs. log_circuit_size must be a + // witness to make the Recursive Verifier circuit constant. Seems that other members also need to be turned + // into witnesses. + this->log_circuit_size = + FF::from_witness(&builder, numeric::get_msb(static_cast(this->circuit_size.get_value()))); this->num_public_inputs = deserialize_from_frs(builder, elements, num_frs_read); this->pub_inputs_offset = deserialize_from_frs(builder, elements, num_frs_read); this->contains_pairing_point_accumulator = diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp index 7bd7e671852d..d8b976588836 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp @@ -627,11 +627,6 @@ template class * */ static constexpr size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES; - /** - * @brief Number of variables in Prover Polynomials. - * - */ - const size_t multivariate_d; std::shared_ptr transcript; SumcheckVerifierRound round; @@ -644,10 +639,8 @@ template class std::vector round_univariate_commitments = {}; std::vector> round_univariate_evaluations = {}; - // Verifier instantiates sumcheck with circuit size, optionally a different target sum than 0 can be specified. - explicit SumcheckVerifier(size_t multivariate_d, std::shared_ptr transcript, FF target_sum = 0) - : multivariate_d(multivariate_d) - , transcript(transcript) + explicit SumcheckVerifier(std::shared_ptr transcript, FF target_sum = 0) + : transcript(transcript) , round(target_sum){}; /** * @brief Extract round univariate, check sum, generate challenge, compute next target sum..., repeat until @@ -660,12 +653,13 @@ template class */ SumcheckOutput verify(const bb::RelationParameters& relation_parameters, RelationSeparator alpha, - std::vector& gate_challenges) + std::vector& gate_challenges, + const std::array& padding_indicator_array) requires(!IsGrumpkinFlavor) { bool verified(true); - // Pad gate challenges for Protogalaxy DeciderVerifier + // Pad gate challenges for Protogalaxy DeciderVerifier and AVM if constexpr (Flavor::USE_PADDING) { round.pad_gate_challenges(gate_challenges); } @@ -674,12 +668,6 @@ template class // All but final round. // target_total_sum is initialized to zero then mutated in place. - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1144): Add proper constraints for taking the log of - // a field_t link multivariate_d. - if (multivariate_d == 0) { - throw_or_abort("Number of variables in multivariate is 0."); - } - bb::Univariate round_univariate; if constexpr (Flavor::HasZK) { @@ -701,30 +689,11 @@ template class FF round_challenge = transcript->template get_challenge("Sumcheck:u_" + std::to_string(round_idx)); multivariate_challenge.emplace_back(round_challenge); - // The recursive logic differs from the native one because of a hack making Sumcheck circuits in - // Ultra, Mega, and their derivatives constant. Note that there's no artificial padding in - // Translator - if constexpr (IsRecursiveFlavor && Flavor::USE_PADDING) { - typename Flavor::CircuitBuilder* builder = round_challenge.get_context(); - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1114): insecure dummy_round derivation! - stdlib::bool_t dummy_round = stdlib::witness_t(builder, round_idx >= multivariate_d); - bool checked = round.check_sum(round_univariate, dummy_round); - // Only utilize the checked value if this is not a constant proof size padding round - if (round_idx < multivariate_d) { - verified = verified && checked; - } - - round.compute_next_target_sum(round_univariate, round_challenge, dummy_round); - gate_separators.partially_evaluate(round_challenge, dummy_round); + const bool checked = round.check_sum(round_univariate, padding_indicator_array[round_idx]); + round.compute_next_target_sum(round_univariate, round_challenge, padding_indicator_array[round_idx]); + gate_separators.partially_evaluate(round_challenge, padding_indicator_array[round_idx]); - } else { - if (round_idx < multivariate_d) { - bool checked = round.check_sum(round_univariate); - verified = verified && checked; - round.compute_next_target_sum(round_univariate, round_challenge); - gate_separators.partially_evaluate(round_challenge); - } - } + verified = verified && checked; } // Extract claimed evaluations of Libra univariates and compute their sum multiplied by the Libra challenge // Final round @@ -743,14 +712,9 @@ template class // For ZK Flavors: the evaluation of the Row Disabling Polynomial at the sumcheck challenge if constexpr (Flavor::HasZK) { libra_evaluation = transcript->template receive_from_prover("Libra:claimed_evaluation"); - if constexpr (!IsRecursiveFlavor) { - correcting_factor = - RowDisablingPolynomial::evaluate_at_challenge(multivariate_challenge, multivariate_d); - } else { - typename Flavor::CircuitBuilder* builder = libra_evaluation.get_context(); - correcting_factor = - RowDisablingPolynomial::evaluate_at_challenge(multivariate_challenge, multivariate_d, builder); - } + + correcting_factor = + RowDisablingPolynomial::evaluate_at_challenge(multivariate_challenge, padding_indicator_array); full_honk_purported_value = full_honk_purported_value * correcting_factor + libra_evaluation * libra_challenge; diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.test.cpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.test.cpp index 2d2fcd16fa9a..5525d5e625ab 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.test.cpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.test.cpp @@ -242,7 +242,7 @@ template class SumcheckTests : public ::testing::Test { .public_input_delta = FF::one(), }; auto prover_transcript = Flavor::Transcript::prover_init_empty(); - auto sumcheck_prover = SumcheckProver(multivariate_n, prover_transcript); + auto sumcheck_prover = SumcheckProver(multivariate_n, prover_transcript); RelationSeparator prover_alpha; for (size_t idx = 0; idx < prover_alpha.size(); idx++) { @@ -264,7 +264,7 @@ template class SumcheckTests : public ::testing::Test { auto verifier_transcript = Flavor::Transcript::verifier_init_empty(prover_transcript); - auto sumcheck_verifier = SumcheckVerifier(multivariate_d, verifier_transcript); + auto sumcheck_verifier = SumcheckVerifier(verifier_transcript); RelationSeparator verifier_alpha; for (size_t idx = 0; idx < verifier_alpha.size(); idx++) { verifier_alpha[idx] = @@ -275,7 +275,10 @@ template class SumcheckTests : public ::testing::Test { verifier_gate_challenges[idx] = verifier_transcript->template get_challenge("Sumcheck:gate_challenge_" + std::to_string(idx)); } - auto verifier_output = sumcheck_verifier.verify(relation_parameters, verifier_alpha, verifier_gate_challenges); + std::array padding_indicator_array; + std::ranges::fill(padding_indicator_array, FF{ 1 }); + auto verifier_output = sumcheck_verifier.verify( + relation_parameters, verifier_alpha, verifier_gate_challenges, padding_indicator_array); auto verified = verifier_output.verified; @@ -331,7 +334,7 @@ template class SumcheckTests : public ::testing::Test { .public_input_delta = FF::one(), }; auto prover_transcript = Flavor::Transcript::prover_init_empty(); - auto sumcheck_prover = SumcheckProver(multivariate_n, prover_transcript); + auto sumcheck_prover = SumcheckProver(multivariate_n, prover_transcript); RelationSeparator prover_alpha; for (size_t idx = 0; idx < prover_alpha.size(); idx++) { @@ -354,7 +357,7 @@ template class SumcheckTests : public ::testing::Test { auto verifier_transcript = Flavor::Transcript::verifier_init_empty(prover_transcript); - auto sumcheck_verifier = SumcheckVerifier(multivariate_d, verifier_transcript); + auto sumcheck_verifier = SumcheckVerifier(verifier_transcript); RelationSeparator verifier_alpha; for (size_t idx = 0; idx < verifier_alpha.size(); idx++) { verifier_alpha[idx] = @@ -365,7 +368,11 @@ template class SumcheckTests : public ::testing::Test { verifier_gate_challenges[idx] = verifier_transcript->template get_challenge("Sumcheck:gate_challenge_" + std::to_string(idx)); } - auto verifier_output = sumcheck_verifier.verify(relation_parameters, verifier_alpha, verifier_gate_challenges); + + std::array padding_indicator_array; + std::ranges::fill(padding_indicator_array, FF{ 1 }); + auto verifier_output = sumcheck_verifier.verify( + relation_parameters, verifier_alpha, verifier_gate_challenges, padding_indicator_array); auto verified = verifier_output.verified; diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp index c92cd867e74e..2a9dd1c3e529 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp @@ -588,6 +588,7 @@ template class SumcheckVerifierRound { { Utils::zero_elements(relation_evaluations); }; + /** * @brief Check that the round target sum is correct * @details The verifier receives the claimed evaluations of the round univariate \f$ \tilde{S}^i \f$ at \f$X_i = @@ -596,15 +597,17 @@ template class SumcheckVerifierRound { * @param univariate Round univariate \f$\tilde{S}^{i}\f$ represented by its evaluations over \f$0,\ldots,D\f$. * */ - bool check_sum(SumcheckRoundUnivariate& univariate) + bool check_sum(bb::Univariate& univariate, const FF& indicator) { - FF total_sum = univariate.value_at(0) + univariate.value_at(1); - // TODO(#673): Conditionals like this can go away once native verification is is just recursive verification - // with a simulated builder. + FF total_sum = + (FF(1) - indicator) * target_total_sum + indicator * (univariate.value_at(0) + univariate.value_at(1)); bool sumcheck_round_failed(false); - // This method is used by TranslatorRecursiveFlavor where dummy_round flags are not needed. if constexpr (IsRecursiveFlavor) { - sumcheck_round_failed = target_total_sum.get_value() != total_sum.get_value(); + // This bool is only needed for debugging + if (indicator.get_value() == FF{ 1 }.get_value()) { + sumcheck_round_failed = (target_total_sum.get_value() != total_sum.get_value()); + } + target_total_sum.assert_equal(total_sum); } else { sumcheck_round_failed = (target_total_sum != total_sum); @@ -614,46 +617,6 @@ template class SumcheckVerifierRound { return !sumcheck_round_failed; }; - /** - * @brief Check that the round target sum is correct - * @details The verifier receives the claimed evaluations of the round univariate \f$ \tilde{S}^i \f$ at \f$X_i = - * 0,\ldots, D \f$ and checks \f$\sigma_i = \tilde{S}^{i-1}(u_{i-1}) \stackrel{?}{=} \tilde{S}^i(0) + \tilde{S}^i(1) - * \f$ - * @param univariate Round univariate \f$\tilde{S}^{i}\f$ represented by its evaluations over \f$0,\ldots,D\f$. - * - */ - template - bool check_sum(bb::Univariate& univariate, stdlib::bool_t dummy_round) - { - FF total_sum = - FF::conditional_assign(dummy_round, target_total_sum, univariate.value_at(0) + univariate.value_at(1)); - // TODO(#673): Conditionals like this can go away once native verification is is just recursive verification - // with a simulated builder. - bool sumcheck_round_failed(false); - // This bool is only needed for debugging - if (!dummy_round.get_value()) { - sumcheck_round_failed = (target_total_sum.get_value() != total_sum.get_value()); - } - - target_total_sum.assert_equal(total_sum); - - round_failed = round_failed || sumcheck_round_failed; - return !sumcheck_round_failed; - }; - - /** - * @brief After checking that the univariate is good for this round, compute the next target sum given by the - * evaluation \f$\tilde{S}^i(u_i)\f$. - * - * @param univariate \f$ \tilde{S}^i(X) \f$, given by its evaluations over \f$ \{0,1,2,\ldots, D\}\f$. - * @param round_challenge \f$ u_i\f$ - */ - void compute_next_target_sum(SumcheckRoundUnivariate& univariate, FF& round_challenge) - { - // Evaluate \f$\tilde{S}^{i}(u_{i}) \f$ - target_total_sum = univariate.evaluate(round_challenge); - } - /** * @brief After checking that the univariate is good for this round, compute the next target sum given by the * evaluation \f$ \tilde{S}^i(u_i) \f$. @@ -662,13 +625,12 @@ template class SumcheckVerifierRound { * @param round_challenge \f$ u_i\f$ * */ - template void compute_next_target_sum(bb::Univariate& univariate, FF& round_challenge, - stdlib::bool_t dummy_round) + const FF& indicator) { // Evaluate \f$\tilde{S}^{i}(u_{i}) \f$ - target_total_sum = FF::conditional_assign(dummy_round, target_total_sum, univariate.evaluate(round_challenge)); + target_total_sum = (FF(1) - indicator) * target_total_sum + indicator * univariate.evaluate(round_challenge); } /** diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp index f50d86d798a0..cde4bedabe2b 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp @@ -63,7 +63,7 @@ bool TranslatorVerifier::verify_proof(const HonkProof& proof, { using Curve = Flavor::Curve; using PCS = Flavor::PCS; - using Shplemini = ShpleminiVerifier_; + using Shplemini = ShpleminiVerifier_; using ClaimBatcher = ClaimBatcher_; using ClaimBatch = ClaimBatcher::Batch; using InterleavedBatch = ClaimBatcher::InterleavedBatch; @@ -97,7 +97,7 @@ bool TranslatorVerifier::verify_proof(const HonkProof& proof, commitments.z_perm = transcript->template receive_from_prover(commitment_labels.z_perm); // Execute Sumcheck Verifier - Sumcheck sumcheck(TranslatorFlavor::CONST_TRANSLATOR_LOG_N, transcript); + Sumcheck sumcheck(transcript); FF alpha = transcript->template get_challenge("Sumcheck:alpha"); std::vector gate_challenges(Flavor::CONST_TRANSLATOR_LOG_N); for (size_t idx = 0; idx < gate_challenges.size(); idx++) { @@ -108,7 +108,10 @@ bool TranslatorVerifier::verify_proof(const HonkProof& proof, std::array libra_commitments = {}; libra_commitments[0] = transcript->template receive_from_prover("Libra:concatenation_commitment"); - auto sumcheck_output = sumcheck.verify(relation_parameters, alpha, gate_challenges); + std::array padding_indicator_array; + std::ranges::fill(padding_indicator_array, FF{ 1 }); + + auto sumcheck_output = sumcheck.verify(relation_parameters, alpha, gate_challenges, padding_indicator_array); // If Sumcheck did not verify, return false if (!sumcheck_output.verified) { @@ -128,7 +131,7 @@ bool TranslatorVerifier::verify_proof(const HonkProof& proof, .evaluations = sumcheck_output.claimed_evaluations.get_interleaved() } }; const BatchOpeningClaim opening_claim = - Shplemini::compute_batch_opening_claim(TranslatorFlavor::CONST_TRANSLATOR_LOG_N, + Shplemini::compute_batch_opening_claim(padding_indicator_array, claim_batcher, sumcheck_output.challenge, Commitment::one(), diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/decider_verifier.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/decider_verifier.cpp index e36c89361bc8..80926fef84bc 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/decider_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/decider_verifier.cpp @@ -7,6 +7,7 @@ #include "decider_verifier.hpp" #include "barretenberg/commitment_schemes/shplonk/shplemini.hpp" #include "barretenberg/numeric/bitop/get_msb.hpp" +#include "barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" #include "barretenberg/transcript/transcript.hpp" #include "barretenberg/ultra_honk/decider_verification_key.hpp" @@ -42,7 +43,7 @@ template bool DeciderVerifier_::verify() { using PCS = typename Flavor::PCS; using Curve = typename Flavor::Curve; - using Shplemini = ShpleminiVerifier_; + using Shplemini = ShpleminiVerifier_; using VerifierCommitments = typename Flavor::VerifierCommitments; using ClaimBatcher = ClaimBatcher_; using ClaimBatch = ClaimBatcher::Batch; @@ -51,14 +52,21 @@ template bool DeciderVerifier_::verify() VerifierCommitments commitments{ accumulator->verification_key, accumulator->witness_commitments }; const size_t log_circuit_size = static_cast(accumulator->verification_key->log_circuit_size); - SumcheckVerifier sumcheck(log_circuit_size, transcript, accumulator->target_sum); + + std::array padding_indicator_array; + + for (size_t idx = 0; idx < CONST_PROOF_SIZE_LOG_N; idx++) { + padding_indicator_array[idx] = (idx < log_circuit_size) ? FF{ 1 } : FF{ 0 }; + } + + SumcheckVerifier sumcheck(transcript, accumulator->target_sum); // For MegaZKFlavor: receive commitments to Libra masking polynomials std::array libra_commitments = {}; if constexpr (Flavor::HasZK) { libra_commitments[0] = transcript->template receive_from_prover("Libra:concatenation_commitment"); } - SumcheckOutput sumcheck_output = - sumcheck.verify(accumulator->relation_parameters, accumulator->alphas, accumulator->gate_challenges); + SumcheckOutput sumcheck_output = sumcheck.verify( + accumulator->relation_parameters, accumulator->alphas, accumulator->gate_challenges, padding_indicator_array); // For MegaZKFlavor: the sumcheck output contains claimed evaluations of the Libra polynomials if constexpr (Flavor::HasZK) { @@ -77,7 +85,7 @@ template bool DeciderVerifier_::verify() .shifted = ClaimBatch{ commitments.get_to_be_shifted(), sumcheck_output.claimed_evaluations.get_shifted() } }; const BatchOpeningClaim opening_claim = - Shplemini::compute_batch_opening_claim(log_circuit_size, + Shplemini::compute_batch_opening_claim(padding_indicator_array, claim_batcher, sumcheck_output.challenge, Commitment::one(), diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/sumcheck.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/sumcheck.test.cpp index 69bd748e319c..56d13d46c0a8 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/sumcheck.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/sumcheck.test.cpp @@ -175,7 +175,7 @@ TEST_F(SumcheckTestsRealCircuit, Ultra) auto verifier_transcript = Transcript::verifier_init_empty(prover_transcript); - auto sumcheck_verifier = SumcheckVerifier(log_circuit_size, verifier_transcript); + auto sumcheck_verifier = SumcheckVerifier(verifier_transcript); RelationSeparator verifier_alphas; for (size_t idx = 0; idx < verifier_alphas.size(); idx++) { verifier_alphas[idx] = verifier_transcript->template get_challenge("Sumcheck:alpha_" + std::to_string(idx)); @@ -186,8 +186,12 @@ TEST_F(SumcheckTestsRealCircuit, Ultra) verifier_gate_challenges[idx] = verifier_transcript->template get_challenge("Sumcheck:gate_challenge_" + std::to_string(idx)); } - auto verifier_output = - sumcheck_verifier.verify(decider_pk->relation_parameters, verifier_alphas, verifier_gate_challenges); + std::array padding_indicator_array; + for (size_t idx = 0; idx < padding_indicator_array.size(); idx++) { + padding_indicator_array[idx] = (idx < log_circuit_size) ? FF{ 1 } : FF{ 0 }; + } + auto verifier_output = sumcheck_verifier.verify( + decider_pk->relation_parameters, verifier_alphas, verifier_gate_challenges, padding_indicator_array); auto verified = verifier_output.verified; diff --git a/barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/recursive_verifier.cpp index 117c2a74fba7..a5e93db191ea 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/recursive_verifier.cpp @@ -9,6 +9,7 @@ #include "barretenberg/polynomials/polynomial.hpp" #include "barretenberg/polynomials/shared_shifted_virtual_zeroes_array.hpp" #include "barretenberg/stdlib/primitives/field/field.hpp" +#include "barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp" #include "barretenberg/transcript/transcript.hpp" #include "barretenberg/vm2/common/aztec_constants.hpp" @@ -79,7 +80,7 @@ AvmRecursiveVerifier_::AggregationObject AvmRecursiveVerifier_:: using VerifierCommitments = typename Flavor::VerifierCommitments; using RelationParams = RelationParameters; using Transcript = typename Flavor::Transcript; - using Shplemini = ShpleminiVerifier_; + using Shplemini = ShpleminiVerifier_; using ClaimBatcher = ClaimBatcher_; using ClaimBatch = ClaimBatcher::Batch; @@ -109,7 +110,9 @@ AvmRecursiveVerifier_::AggregationObject AvmRecursiveVerifier_:: // unconstrained const size_t log_circuit_size = numeric::get_msb(static_cast(circuit_size.get_value())); - auto sumcheck = SumcheckVerifier(log_circuit_size, transcript); + const auto padding_indicator_array = + stdlib::compute_padding_indicator_array(FF(log_circuit_size)); + auto sumcheck = SumcheckVerifier(transcript); FF alpha = transcript->template get_challenge("Sumcheck:alpha"); @@ -120,8 +123,8 @@ AvmRecursiveVerifier_::AggregationObject AvmRecursiveVerifier_:: // No need to constrain that sumcheck_verified is true as this is guaranteed by the implementation of // when called over a "circuit field" types. - SumcheckOutput output = sumcheck.verify(relation_parameters, alpha, gate_challenges); - + SumcheckOutput output = + sumcheck.verify(relation_parameters, alpha, gate_challenges, padding_indicator_array); vinfo("verified sumcheck: ", (output.verified)); // Public columns evaluation checks @@ -140,7 +143,7 @@ AvmRecursiveVerifier_::AggregationObject AvmRecursiveVerifier_:: .shifted = ClaimBatch{ commitments.get_to_be_shifted(), output.claimed_evaluations.get_shifted() } }; const BatchOpeningClaim opening_claim = Shplemini::compute_batch_opening_claim( - log_circuit_size, claim_batcher, output.challenge, Commitment::one(&builder), transcript); + padding_indicator_array, claim_batcher, output.challenge, Commitment::one(&builder), transcript); auto pairing_points = PCS::reduce_verify_batch_opening_claim(opening_claim, transcript); diff --git a/barretenberg/cpp/src/barretenberg/vm2/constraining/verifier.cpp b/barretenberg/cpp/src/barretenberg/vm2/constraining/verifier.cpp index 36aeb1335a59..48abf292b09b 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/constraining/verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/constraining/verifier.cpp @@ -44,7 +44,7 @@ bool AvmVerifier::verify_proof(const HonkProof& proof, const std::vector; + using Shplemini = ShpleminiVerifier_; using ClaimBatcher = ClaimBatcher_; using ClaimBatch = ClaimBatcher::Batch; using VerifierCommitmentKey = typename Flavor::VerifierCommitmentKey; @@ -77,7 +77,13 @@ bool AvmVerifier::verify_proof(const HonkProof& proof, const std::vector(log_circuit_size, transcript); + + std::array padding_indicator_array; + + for (size_t idx = 0; idx < CONST_PROOF_SIZE_LOG_N; idx++) { + padding_indicator_array[idx] = (idx < log_circuit_size) ? FF{ 1 } : FF{ 0 }; + } + auto sumcheck = SumcheckVerifier(transcript); FF alpha = transcript->template get_challenge("Sumcheck:alpha"); @@ -86,7 +92,8 @@ bool AvmVerifier::verify_proof(const HonkProof& proof, const std::vectortemplate get_challenge("Sumcheck:gate_challenge_" + std::to_string(idx)); } - SumcheckOutput output = sumcheck.verify(relation_parameters, alpha, gate_challenges); + SumcheckOutput output = + sumcheck.verify(relation_parameters, alpha, gate_challenges, padding_indicator_array); // If Sumcheck did not verify, return false if (!output.verified) { @@ -109,7 +116,7 @@ bool AvmVerifier::verify_proof(const HonkProof& proof, const std::vector opening_claim = Shplemini::compute_batch_opening_claim( - log_circuit_size, claim_batcher, output.challenge, Commitment::one(), transcript); + padding_indicator_array, claim_batcher, output.challenge, Commitment::one(), transcript); const auto pairing_points = PCS::reduce_verify_batch_opening_claim(opening_claim, transcript); VerifierCommitmentKey pcs_vkey{};