diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/claim.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/claim.hpp index 1afb02a3c66b..97a1e51e4187 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/claim.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/claim.hpp @@ -33,6 +33,9 @@ template class ProverOpeningClaim { public: Polynomial polynomial; // p OpeningPair opening_pair; // (challenge r, evaluation v = p(r)) + // Gemini Folds have to be opened at `challenge` and -`challenge`. Instead of copying a polynomial into 2 claims, we + // raise the flag that turns on relevant claim processing logic in Shplonk. + bool gemini_fold = false; }; /** diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp index c5b8affd1363..4af3fdb5f559 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp @@ -38,15 +38,15 @@ * - A₀₊(X) = F(X) + G(X)/r * - A₀₋(X) = F(X) − G(X)/r * So that A₀₊(r) = A₀(r) and A₀₋(-r) = A₀(-r). - * The verifier is able to computed the simulated commitments to A₀₊(X) and A₀₋(X) + * The verifier is able to compute the simulated commitments to A₀₊(X) and A₀₋(X) * since they are linear-combinations of the commitments [fⱼ] and [gⱼ]. */ namespace bb { /** * @brief Prover output (evalutation pair, witness) that can be passed on to Shplonk batch opening. - * @details Evaluation pairs {r, A₀₊(r)}, {-r, A₀₋(-r)}, {-r^{2^j}, Aⱼ(-r^{2^j)}, j = [1, ..., m-1] - * and witness (Fold) polynomials + * @details Evaluation pairs {r, A₀₊(r)}, {-r, A₀₋(-r)}, {r^{2^j}, Aⱼ(r^{2^j)}, {-r^{2^j}, Aⱼ(-r^{2^j)}, j = [1, ..., + * m-1] and witness (Fold) polynomials * [ * A₀₊(X) = F(X) + r⁻¹⋅G(X) * A₀₋(X) = F(X) - r⁻¹⋅G(X) @@ -443,18 +443,23 @@ template class GeminiVerifier_ { p_neg = transcript->template receive_from_prover("Gemini:P_0_neg"); } - // Compute the full of evaluation A₀(r) = A₀₊(r) + P₊(r^s) - Fr full_a_0_pos = compute_gemini_batched_univariate_evaluation( + // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., m-1 + std::vector gemini_fold_pos_evaluations = compute_fold_pos_evaluations( num_variables, 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; - fold_polynomial_opening_claims.reserve(num_variables + 1); + fold_polynomial_opening_claims.reserve(2 * num_variables + 2); // ( [A₀₊], r, A₀₊(r) ) fold_polynomial_opening_claims.emplace_back(OpeningClaim{ { r, full_a_0_pos - p_pos }, C0_r_pos }); // ( [A₀₋], -r, A₀-(-r) ) fold_polynomial_opening_claims.emplace_back(OpeningClaim{ { -r, evaluations[0] }, C0_r_neg }); for (size_t l = 0; l < num_variables - 1; ++l) { - // ([A₀₋], −r^{2ˡ}, Aₗ(−r^{2ˡ}) ) + // ([Aₗ], r^{2ˡ}, Aₗ(r^{2ˡ}) ) + fold_polynomial_opening_claims.emplace_back( + OpeningClaim{ { r_squares[l + 1], gemini_fold_pos_evaluations[l + 1] }, commitments[l] }); + // ([Aₗ], −r^{2ˡ}, Aₗ(−r^{2ˡ}) ) fold_polynomial_opening_claims.emplace_back( OpeningClaim{ { -r_squares[l + 1], evaluations[l + 1] }, commitments[l] }); } @@ -493,36 +498,49 @@ template class GeminiVerifier_ { } /** - * @brief Compute the expected evaluation of the univariate commitment to the batched polynomial. + * @brief Compute \f$ A_0(r), A_1(r^2), \ldots, A_{d-1}(r^{2^{d-1}})\f$ * - * Compute the evaluation \f$ A_0(r) = \sum \rho^i \cdot f_i + \frac{1}{r} \cdot \sum \rho^{i+k} g_i \f$, where \f$ + * Recall that \f$ A_0(r) = \sum \rho^i \cdot f_i + \frac{1}{r} \cdot \sum \rho^{i+k} g_i \f$, where \f$ * k \f$ is the number of "unshifted" commitments. * - * @details Initialize \f$ A_{d}(r) \f$ with the batched evaluation \f$ \sum \rho^i f_i(\vec{u}) + \sum \rho^{i+k} - * g_i(\vec{u}) \f$. The folding property ensures that - * \f{align}{ - * A_\ell\left(r^{2^\ell}\right) = (1 - u_{\ell-1}) \cdot \frac{A_{\ell-1}\left(r^{2^{\ell-1}}\right) + - * A_{\ell-1}\left(-r^{2^{\ell-1}}\right)}{2} - * + u_{\ell-1} \cdot \frac{A_{\ell-1}\left(r^{2^{\ell-1}}\right) - - * A_{\ell-1}\left(-r^{2^{\ell-1}}\right)}{2r^{2^{\ell-1}}} - * \f} - * Therefore, the verifier can recover \f$ A_0(r) \f$ by solving several linear equations. + * @details Initialize `a_pos` = \f$ A_{d}(r) \f$ with the batched evaluation \f$ \sum \rho^i f_i(\vec{u}) + \sum + * \rho^{i+k} g_i(\vec{u}) \f$. The verifier recovers \f$ A_{l-1}(r^{2^{l-1}}) \f$ from the "negative" value \f$ + * A_{l-1}\left(-r^{2^{l-1}}\right) \f$ received from the prover and the value \f$ A_{l}\left(r^{2^{l}}\right) \f$ + * computed at the previous step. Namely, the verifier computes + * \f{align}{ A_{l-1}\left(r^{2^{l-1}}\right) = + * \frac{2 \cdot r^{2^{l-1}} \cdot A_{l}\left(r^{2^l}\right) - A_{l-1}\left( -r^{2^{l-1}} \right)\cdot + * \left(r^{2^{l-1}} (1-u_{l-1}) - u_{l-1}\right)} {r^{2^{l-1}} (1- u_{l-1}) + u_{l-1}}. \f} + * + * 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 batched_mle_eval 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$. - * @param challenge_powers Powers of \f$ r \f$, \f$ r^2 \), ..., \( r^{2^{m-1}} \f$. - * @param fold_polynomial_evals Evaluations \f$ A_{i-1}(-r^{2^{i-1}}) \f$. + * @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$. */ - static Fr compute_gemini_batched_univariate_evaluation( + static std::vector compute_fold_pos_evaluations( const size_t num_variables, - Fr& batched_eval_accumulator, + 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_polynomial_evals, + std::span fold_neg_evals, Fr p_neg = Fr(0)) { - std::vector evals(fold_polynomial_evals.begin(), fold_polynomial_evals.end()); + std::vector evals(fold_neg_evals.begin(), fold_neg_evals.end()); + + Fr eval_pos_prev = batched_evaluation; + + Fr zero{ 0 }; + if constexpr (Curve::is_stdlib_type) { + zero.convert_constant_to_fixed_witness(fold_neg_evals[0].get_context()); + } + + std::vector fold_pos_evaluations; + fold_pos_evaluations.reserve(CONST_PROOF_SIZE_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; @@ -536,26 +554,33 @@ template class GeminiVerifier_ { const Fr& eval_neg = evals[l - 1]; // Get A₍ₗ₋₁₎(−r²⁽ˡ⁻¹⁾) // Compute the numerator - Fr batched_eval_round_acc = - ((challenge_power * batched_eval_accumulator * 2) - eval_neg * (challenge_power * (Fr(1) - u) - u)); + Fr eval_pos = ((challenge_power * eval_pos_prev * 2) - eval_neg * (challenge_power * (Fr(1) - u) - u)); // Divide by the denominator - batched_eval_round_acc *= (challenge_power * (Fr(1) - u) + u).invert(); + eval_pos *= (challenge_power * (Fr(1) - u) + u).invert(); 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 > num_variables); - batched_eval_accumulator = - Fr::conditional_assign(dummy_round, batched_eval_accumulator, batched_eval_round_acc); + // If current index is bigger than log_circuit_size, 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_circuit_size, we emplace 0, which is later multiplied against + // Commitment::one(). + value_to_emplace = Fr::conditional_assign(dummy_round, zero, eval_pos_prev); } else { - if (l <= num_variables) { - batched_eval_accumulator = batched_eval_round_acc; - } - } + // Perform the same logic natively + bool dummy_round = l > num_variables; + eval_pos_prev = dummy_round ? eval_pos_prev : eval_pos; + value_to_emplace = dummy_round ? zero : eval_pos_prev; + }; + fold_pos_evaluations.emplace_back(value_to_emplace); } - return batched_eval_accumulator; + std::reverse(fold_pos_evaluations.begin(), fold_pos_evaluations.end()); + + return fold_pos_evaluations; } }; diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.test.cpp index 60bcaa42ee06..14cbd8bde55a 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.test.cpp @@ -38,20 +38,46 @@ template class GeminiTest : public CommitmentTest { auto prover_output = GeminiProver::prove( this->n, mock_claims.polynomial_batcher, multilinear_evaluation_point, ck, prover_transcript); + // The prover output needs to be completed by adding the "positive" Fold claims, i.e. evaluations of Fold^(i) at + // r^{2^i} for i=1, ..., d-1. Although here we are copying polynomials, it is not the case when GeminiProver is + // combined with ShplonkProver. + std::vector> prover_claims_with_pos_evals; + // `prover_output` consists of d+1 opening claims, we add another d-1 claims for each positive evaluation + // Fold^i(r^{2^i}) for i = 1, ..., d-1 + const size_t total_num_claims = 2 * log_n; + prover_claims_with_pos_evals.reserve(total_num_claims); + + for (auto& claim : prover_output) { + if (claim.gemini_fold) { + if (claim.gemini_fold) { + // "positive" evaluation challenge r^{2^i} for i = 1, ..., d-1 + const Fr evaluation_challenge = -claim.opening_pair.challenge; + // Fold^(i) at r^{2^i} for i=1, ..., d-1 + const Fr pos_evaluation = claim.polynomial.evaluate(evaluation_challenge); + // Add the positive Fold claims to the vector of claims + ProverOpeningClaim pos_fold_claim = { .polynomial = claim.polynomial, + .opening_pair = { .challenge = evaluation_challenge, + .evaluation = pos_evaluation } }; + prover_claims_with_pos_evals.emplace_back(pos_fold_claim); + } + } + prover_claims_with_pos_evals.emplace_back(claim); + } + // Check that the Fold polynomials have been evaluated correctly in the prover - this->verify_batch_opening_pair(prover_output); + this->verify_batch_opening_pair(prover_claims_with_pos_evals); auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript); // Compute: - // - Single opening pair: {r, \hat{a}_0} + // - d opening pairs: {r^{2^i}, \hat{a}_i} for i = 0, ..., d-1 // - 2 partially evaluated Fold polynomial commitments [Fold_{r}^(0)] and [Fold_{-r}^(0)] - // Aggregate: d+1 opening pairs and d+1 Fold poly commitments into verifier claim + // Aggregate: 2d opening pairs and 2d Fold poly commitments into verifier claim auto verifier_claims = GeminiVerifier::reduce_verification( multilinear_evaluation_point, mock_claims.claim_batcher, verifier_transcript); // Check equality of the opening pairs computed by prover and verifier - for (auto [prover_claim, verifier_claim] : zip_view(prover_output, verifier_claims)) { + for (auto [prover_claim, verifier_claim] : zip_view(prover_claims_with_pos_evals, verifier_claims)) { this->verify_opening_claim(verifier_claim, prover_claim.polynomial, ck); ASSERT_EQ(prover_claim.opening_pair, verifier_claim.opening_pair); } @@ -102,7 +128,7 @@ TYPED_TEST(GeminiTest, DoubleWithShift) this->execute_gemini_and_verify_claims(u, mock_claims); } -TYPED_TEST(GeminiTest, DoubleWithShiftAndConcatenation) +TYPED_TEST(GeminiTest, DoubleWithShiftAndInterleaving) { auto u = this->random_evaluation_point(this->log_n); @@ -124,14 +150,24 @@ TYPED_TEST(GeminiTest, DoubleWithShiftAndConcatenation) */ TYPED_TEST(GeminiTest, SoundnessRegression) { + using ClaimBatcher = ClaimBatcher_; + using ClaimBatch = ClaimBatcher::Batch; + using Claim = ProverOpeningClaim; + using Commitment = TypeParam::AffineElement; using Fr = TypeParam::ScalarField; const size_t log_n = 3; const size_t n = 8; auto prover_transcript = NativeTranscript::prover_init_empty(); + const Fr rho = prover_transcript->template get_challenge("rho"); + std::vector> fold_polynomials; + fold_polynomials.reserve(log_n); - bb::Polynomial zero_polynomial(n); - auto u = this->random_evaluation_point(this->log_n); + Polynomial fold_0(n); + Polynomial fold_1(n / 2); + Polynomial fold_2(n / 4); + + auto u = this->random_evaluation_point(log_n); // Generate a random evaluation v, the prover claims that `zero_polynomial`(u) = v Fr claimed_multilinear_eval = Fr::random_element(); @@ -139,8 +175,6 @@ TYPED_TEST(GeminiTest, SoundnessRegression) // Go through the Gemini Prover steps: compute fold polynomials and their evaluations std::vector fold_evals; fold_evals.reserve(log_n); - Polynomial fold_2(2); - Polynomial fold_1(4); // By defining the coefficients of fold polynomials as below, a malicious prover can make sure that the values // fold₁(r²) = 0, and hence fold₀(r), computed by the verifier, are 0. At the same time, the prover can open @@ -158,11 +192,18 @@ TYPED_TEST(GeminiTest, SoundnessRegression) fold_1.at(2) = -(Fr(1) - u[1]) * fold_1.at(1) * u[1].invert(); // fold₁[2] = -(1 - u₁) ⋅ fold₁[1] / u₁ fold_1.at(3) = Fr(0); + prover_transcript->template send_to_verifier("Gemini:FOLD_1", this->ck->commit(fold_1)); + prover_transcript->template send_to_verifier("Gemini:FOLD_2", this->ck->commit(fold_2)); + + for (size_t idx = log_n; idx < CONST_PROOF_SIZE_LOG_N; idx++) { + prover_transcript->template send_to_verifier("Gemini:FOLD_" + std::to_string(idx), Commitment::one()); + } + // Get Gemini evaluation challenge - const Fr gemini_r = Fr::random_element(); + const Fr gemini_r = prover_transcript->template get_challenge("Gemini:r"); // Place honest eval of fold₀(-r) to the vector of evals - fold_evals.emplace_back(Fr(0)); + fold_evals.emplace_back(fold_0.evaluate(-gemini_r)); // Compute univariate opening queries rₗ = r^{2ˡ} for l = 0, 1, 2 std::vector r_squares = gemini::powers_of_evaluation_challenge(gemini_r, log_n); @@ -170,18 +211,55 @@ TYPED_TEST(GeminiTest, SoundnessRegression) // Compute honest evaluations fold₁(-r²) and fold₂(-r⁴) fold_evals.emplace_back(fold_1.evaluate(-r_squares[1])); fold_evals.emplace_back(fold_2.evaluate(-r_squares[2])); + prover_transcript->template send_to_verifier("Gemini:a_1", fold_evals[0]); + prover_transcript->template send_to_verifier("Gemini:a_2", fold_evals[1]); + prover_transcript->template send_to_verifier("Gemini:a_3", fold_evals[2]); + for (size_t idx = log_n + 1; idx <= CONST_PROOF_SIZE_LOG_N; idx++) { + prover_transcript->template send_to_verifier("Gemini:a_" + std::to_string(idx), Fr{ 0 }); + } // Compute the powers of r used by the verifier. It is an artifact of the const proof size logic. const std::vector gemini_eval_challenge_powers = gemini::powers_of_evaluation_challenge(gemini_r, CONST_PROOF_SIZE_LOG_N); - // Compute fold₀(r) as Verifier would compute it - const Fr full_a_0_pos = GeminiVerifier_::compute_gemini_batched_univariate_evaluation( - log_n, claimed_multilinear_eval, u, gemini_eval_challenge_powers, fold_evals); + std::vector prover_opening_claims; + prover_opening_claims.reserve(2 * log_n); + + prover_opening_claims.emplace_back(Claim{ fold_0, { gemini_r, Fr{ 0 } } }); + prover_opening_claims.emplace_back(Claim{ fold_0, { -gemini_r, Fr{ 0 } } }); + prover_opening_claims.emplace_back(Claim{ fold_1, { r_squares[1], fold_1.evaluate(r_squares[1]) } }); + prover_opening_claims.emplace_back(Claim{ fold_1, { -r_squares[1], fold_evals[1] } }); + prover_opening_claims.emplace_back(Claim{ fold_2, { r_squares[2], fold_2.evaluate(r_squares[2]) } }); + prover_opening_claims.emplace_back(Claim{ fold_2, { -r_squares[2], fold_evals[2] } }); - // Check that fold₀(r) = 0. Therefore, a malicious prover could open it using the commitment to the zero - // polynomial. - EXPECT_TRUE(full_a_0_pos == Fr(0)); + // Check that the Fold polynomials have been evaluated correctly in the prover + this->verify_batch_opening_pair(prover_opening_claims); + + auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript); + + std::vector unshifted_commitments = { this->ck->commit(fold_0) }; + std::vector unshifted_evals = { claimed_multilinear_eval * rho.pow(0) }; + + ClaimBatcher claim_batcher{ .unshifted = + ClaimBatch{ RefVector(unshifted_commitments), RefVector(unshifted_evals) } }; + + auto verifier_claims = GeminiVerifier_::reduce_verification(u, claim_batcher, verifier_transcript); + + // Malicious prover could honestly prove all "negative" evaluations and several "positive evaluations". In + // particular, the evaluation of `fold_0` at r. + std::vector matching_claim_indices{ 0, 1, 3, 4, 5 }; + // However, the evaluation of `fold_1` at r^2 that the verifier computes assuming that fold has been performed + // correctly, does not match the actual evaluation of tampered fold_1 that the prover can open. + std::vector mismatching_claim_indices = { 2 }; + for (auto idx : matching_claim_indices) { + EXPECT_TRUE(prover_opening_claims[idx].opening_pair == verifier_claims[idx].opening_pair); + } + + // The mismatch in claims below leads to Gemini and Shplemini Verifier rejecting the tampered proof and confirms the + // necessity of opening `fold_i` at r^{2^i} for i = 1, ..., log_n - 1. + for (auto idx : mismatching_claim_indices) { + EXPECT_FALSE(prover_opening_claims[idx].opening_pair == verifier_claims[idx].opening_pair); + } } template std::shared_ptr::CK> GeminiTest::ck = nullptr; diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini_impl.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini_impl.hpp index 5e7a2daad601..cc40f4308f5a 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini_impl.hpp @@ -233,10 +233,14 @@ std::vector::Claim> GeminiProver_::construc // Compute univariate opening queries rₗ = r^{2ˡ} for l = 0, 1, ..., m-1 std::vector r_squares = gemini::powers_of_evaluation_challenge(r_challenge, log_n); + // Each fold polynomial Aₗ has to be opened at −r^{2ˡ} and r^{2ˡ}. To avoid storing two copies of Aₗ for l = 1,..., + // m-1, we use a flag that is processed by ShplonkProver. + const bool gemini_fold = true; + // Compute the remaining m opening pairs {−r^{2ˡ}, Aₗ(−r^{2ˡ})}, l = 1, ..., m-1. for (size_t l = 0; l < log_n - 1; ++l) { Fr evaluation = fold_polynomials[l].evaluate(-r_squares[l + 1]); - claims.emplace_back(Claim{ std::move(fold_polynomials[l]), { -r_squares[l + 1], evaluation } }); + claims.emplace_back(Claim{ std::move(fold_polynomials[l]), { -r_squares[l + 1], evaluation }, gemini_fold }); } return claims; 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 2aba6e60c54a..571e359ec878 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp @@ -199,7 +199,7 @@ TEST_F(KZGTest, ShpleminiKzgWithShift) EXPECT_EQ(vk->pairing_check(pairing_points[0], pairing_points[1]), true); } -TEST_F(KZGTest, ShpleminiKzgWithShiftAndConcatenation) +TEST_F(KZGTest, ShpleminiKzgWithShiftAndInterleaving) { std::vector mle_opening_point = random_evaluation_point(log_n); // sometimes denoted 'u' // Generate multilinear polynomials, their commitments (genuine and mocked) and evaluations (genuine) at a random diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp index 22536c1762bf..ab23b0e76fb8 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp @@ -84,7 +84,7 @@ template class ShpleminiProver_ { const std::array evaluation_points = { gemini_r, gemini_r * subgroup_generator, gemini_r, gemini_r }; - for (size_t idx = 0; idx < 4; idx++) { + for (size_t idx = 0; idx < NUM_SMALL_IPA_EVALUATIONS; idx++) { new_claim.polynomial = std::move(libra_polynomials[idx]); new_claim.opening_pair.challenge = evaluation_points[idx]; new_claim.opening_pair.evaluation = new_claim.polynomial.evaluate(evaluation_points[idx]); @@ -207,6 +207,8 @@ template class ShpleminiVerifier_ { const std::vector>& sumcheck_round_evaluations = {}) { + const bool committed_sumcheck = !sumcheck_round_evaluations.empty(); + // Extract log_circuit_size size_t log_circuit_size{ 0 }; if constexpr (Curve::is_stdlib_type) { @@ -236,7 +238,7 @@ template class ShpleminiVerifier_ { const Fr gemini_evaluation_challenge = transcript->template get_challenge("Gemini:r"); // - Get evaluations (A₀(−r), A₁(−r²), ... , Aₙ₋₁(−r²⁽ⁿ⁻¹⁾)) - const std::vector gemini_evaluations = GeminiVerifier::get_gemini_evaluations(transcript); + const std::vector gemini_fold_neg_evaluations = GeminiVerifier::get_gemini_evaluations(transcript); // Get evaluations of partially evaluated batched interleaved polynomials P₊(rˢ) and P₋((-r)ˢ) Fr p_pos = Fr(0); @@ -261,6 +263,11 @@ template class ShpleminiVerifier_ { // Process Shplonk transcript data: // - Get Shplonk batching challenge const Fr shplonk_batching_challenge = transcript->template get_challenge("Shplonk:nu"); + + // Compute the powers of ν that are required for batching Gemini, SmallSubgroupIPA, and committed sumcheck + // univariate opening claims. + const std::vector shplonk_batching_challenge_powers = + compute_shplonk_batching_challenge_powers(shplonk_batching_challenge, has_zk, committed_sumcheck); // - Get the quotient commitment for the Shplonk batching of Gemini opening claims const auto Q_commitment = transcript->template receive_from_prover("Shplonk:Q"); @@ -283,10 +290,10 @@ template class ShpleminiVerifier_ { scalars.emplace_back(Fr(1)); } - // Compute 1/(z − r), 1/(z + r), 1/(z + r²), … , 1/(z + r²⁽ⁿ⁻¹⁾) + // Compute 1/(z − r), 1/(z + r), 1/(z - r²), 1/(z + r²), … , 1/(z - r²⁽ⁿ⁻¹⁾), 1/(z + r²⁽ⁿ⁻¹⁾) // These represent the denominators of the summand terms in Shplonk partially evaluated polynomial Q_z const std::vector inverse_vanishing_evals = ShplonkVerifier::compute_inverted_gemini_denominators( - log_circuit_size + 1, shplonk_evaluation_challenge, gemini_eval_challenge_powers); + shplonk_evaluation_challenge, gemini_eval_challenge_powers); // Compute the Shplonk denominator for the interleaved opening claims 1/(z − r^s) where s is the group size const Fr interleaving_vanishing_eval = (shplonk_evaluation_challenge - @@ -321,8 +328,12 @@ template class ShpleminiVerifier_ { Fr shplonk_batching_pos = Fr{ 0 }; Fr shplonk_batching_neg = Fr{ 0 }; if (claim_batcher.interleaved) { - shplonk_batching_pos = shplonk_batching_challenge.pow(log_circuit_size + 1); - shplonk_batching_neg = shplonk_batching_pos * shplonk_batching_challenge; + // 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_circuit_size; + 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]; constant_term_accumulator += p_pos * interleaving_vanishing_eval * shplonk_batching_pos + p_neg * interleaving_vanishing_eval * shplonk_batching_neg; } @@ -335,34 +346,38 @@ template class ShpleminiVerifier_ { shplonk_batching_pos, shplonk_batching_neg); + // Reconstruct Aᵢ(r²ⁱ) for i=0, ..., n-1 from the batched evaluation of the multilinear polynomials and Aᵢ(−r²ⁱ) + // for i = 0, ..., n-1. + // In the case of interleaving, we compute A₀(r) as A₀₊(r) + P₊(r^s). + const std::vector gemini_fold_pos_evaluations = + GeminiVerifier_::compute_fold_pos_evaluations(log_circuit_size, + 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, … , n−1 to the constant term accumulator, add corresponding scalars for // the batch mul batch_gemini_claims_received_from_prover(log_circuit_size, fold_commitments, - gemini_evaluations, + gemini_fold_neg_evaluations, + gemini_fold_pos_evaluations, inverse_vanishing_evals, - shplonk_batching_challenge, + shplonk_batching_challenge_powers, commitments, scalars, constant_term_accumulator); - - // Compute A₀(r) = A₀₊(r) + P₊(r^s) - const Fr full_a_0_pos = - GeminiVerifier_::compute_gemini_batched_univariate_evaluation(log_circuit_size, - batched_evaluation, - multivariate_challenge, - gemini_eval_challenge_powers, - gemini_evaluations, - p_neg); - + const Fr full_a_0_pos = gemini_fold_pos_evaluations[0]; // Retrieve the contribution without P₊(r^s) Fr a_0_pos = full_a_0_pos - p_pos; // Add contributions from A₀₊(r) and A₀₋(-r) to constant_term_accumulator: // Add A₀₊(r)/(z−r) to the constant term accumulator constant_term_accumulator += a_0_pos * inverse_vanishing_evals[0]; // Add A₀₋(-r)/(z+r) to the constant term accumulator - constant_term_accumulator += gemini_evaluations[0] * shplonk_batching_challenge * inverse_vanishing_evals[1]; + constant_term_accumulator += + gemini_fold_neg_evaluations[0] * shplonk_batching_challenge * inverse_vanishing_evals[1]; remove_repeated_commitments(commitments, scalars, repeated_commitments, has_zk); @@ -375,7 +390,7 @@ template class ShpleminiVerifier_ { libra_commitments, libra_evaluations, gemini_evaluation_challenge, - shplonk_batching_challenge, + shplonk_batching_challenge_powers, shplonk_evaluation_challenge); *consistency_checked = SmallSubgroupIPAVerifier::check_libra_evaluations_consistency( @@ -383,13 +398,13 @@ template class ShpleminiVerifier_ { } // Currently, only used in ECCVM - if (!sumcheck_round_evaluations.empty()) { + if (committed_sumcheck) { batch_sumcheck_round_claims(log_circuit_size, commitments, scalars, constant_term_accumulator, multivariate_challenge, - shplonk_batching_challenge, + shplonk_batching_challenge_powers, shplonk_evaluation_challenge, sumcheck_round_commitments, sumcheck_round_evaluations); @@ -403,11 +418,11 @@ template class ShpleminiVerifier_ { }; /** - * @brief Populates the 'commitments' and 'scalars' vectors with the commitments to Gemini fold polynomials \f$ - * A_i \f$. + * @brief Place fold polynomial commitments to `commitments` and compute the corresponding scalar multipliers. * - * @details Once the commitments to Gemini "fold" polynomials \f$ A_i \f$ and their evaluations at \f$ -r^{2^i} - * \f$, where \f$ i = 1, \ldots, n-1 \f$, are received by the verifier, it performs the following operations: + * @details Once the commitments to Gemini "fold" polynomials \f$ A_i \f$ and their negative evaluations, i.e. \f$ + * A_i(-r^{2^i}) \f$, for \f$ i = 1, \ldots, n-1 \f$, are obtained, and the verifier has reconstructed the positive + * fold evaluation \f$ A_i(r^{2^i}) \f$ for \f$ i=1, \ldots, n - 1 \f$, it performs the following operations: * * 1. Moves the vector * \f[ @@ -415,66 +430,81 @@ template class ShpleminiVerifier_ { * \f] * to the 'commitments' vector. * - * 2. Computes the scalars: - * \f[ - * \frac{\nu^{2}}{z + r^2}, \frac{\nu^3}{z + r^4}, \ldots, \frac{\nu^{n-1}}{z + r^{2^{n-1}}} - * \f] - * and places them into the 'scalars' vector. + * 2. Computes the scalars + * \f{align}{ + * \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 n} } {z - r^{2^{n-1}}} + \frac{\nu^{2 \cdot n + 1}}{z + r^{2^{n-1}}}. \f} + * The commitments \f$ [A_1]_1, \ldots, [A_{n-1}]_1 \f$ are multiplied by these scalars in the final `batch_mul` + * perfomed by KZG or IPA. * * 3. Accumulates the summands of the constant term: - * \f[ - * \sum_{i=2}^{n-1} \frac{\nu^{i} \cdot A_i(-r^{2^i})}{z + r^{2^i}} - * \f] - * and adds them to the 'constant_term_accumulator'. + * \f{align}{ + * \frac{\nu^{2 i} \cdot A_i\left(r^{2^i} \right)}{z - r^{2^i}} + \frac{\nu^{2 \cdot i+1} \cdot + * A_i\left(-r^{2^i}\right)}{z+ r^{2^i}} \f} for \f$ i = 1, \ldots, n-1 \f$ and adds them to the + * 'constant_term_accumulator'. * * @param log_circuit_size The logarithm of the circuit size, determining the depth of the Gemini protocol. * @param fold_commitments A vector containing the commitments to the Gemini fold polynomials \f$ A_i \f$. - * @param gemini_evaluations A vector containing the evaluations of the Gemini fold polynomials \f$ A_i \f$ at - * points \f$ -r^{2^i} \f$. - * @param inverse_vanishing_evals A vector containing the inverse evaluations of the vanishing polynomial. - * @param shplonk_batching_challenge The batching challenge \f$ \nu \f$ used in the SHPLONK protocol. + * @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, n - 1 \f$. + * @param gemini_pos_evaluations The evaluations of Gemini fold polynomials \f$ A_i \f$ at \f$ r^{2^i} \f$ for \f$ + * i = 0, \ldots, n - 1 \f$ + * @param inverse_vanishing_evals \f$ 1/(z − r), 1/(z + r), 1/(z - r^2), 1/(z + r^2), \ldots, 1/(z - r^{2^{n-1}}), + * 1/(z + r^{2^{n-1}}) \f$ + * @param shplonk_batching_challenge_powers A vector of powers of \f$ \nu \f$ used to batch all univariate claims. * @param commitments Output vector where the commitments to the Gemini fold polynomials will be stored. * @param scalars Output vector where the computed scalars will be stored. - * @param constant_term_accumulator The accumulator for the summands of the constant term. + * @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_circuit_size, const std::vector& fold_commitments, - const std::vector& gemini_evaluations, + const std::vector& gemini_neg_evaluations, + const std::vector& gemini_pos_evaluations, const std::vector& inverse_vanishing_evals, - const Fr& shplonk_batching_challenge, + const std::vector& shplonk_batching_challenge_powers, std::vector& commitments, std::vector& scalars, Fr& constant_term_accumulator) { - // Initialize batching challenge as ν² - Fr current_batching_challenge = shplonk_batching_challenge.sqr(); // TODO(https://github.com/AztecProtocol/barretenberg/issues/1159): Decouple constants from primitives. - for (size_t j = 0; j < CONST_PROOF_SIZE_LOG_N - 1; ++j) { - // Compute the scaling factor (ν²⁺ⁱ) / (z + r²⁽ⁱ⁺²⁾) for i = 0, … , d-2 - Fr scaling_factor = current_batching_challenge * inverse_vanishing_evals[j + 2]; - - // Add Aᵢ(−r²ⁱ) for i = 1, … , n-1 to the constant term accumulator - constant_term_accumulator += scaling_factor * gemini_evaluations[j + 1]; - - // Update the batching challenge - current_batching_challenge *= shplonk_batching_challenge; + // 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 < CONST_PROOF_SIZE_LOG_N; ++j) { + // The index of 1/ (z - r^{2^{j}}) in the vector of inverted Gemini denominators + const size_t pos_index = 2 * j; + // The index of 1/ (z + r^{2^{j}}) in the vector of inverted Gemini denominators + const size_t neg_index = 2 * j + 1; + + // Compute the "positive" scaling factor (ν^{2j}) / (z - r^{2^{j}}) + Fr scaling_factor_pos = shplonk_batching_challenge_powers[pos_index] * inverse_vanishing_evals[pos_index]; + // Compute the "negative" scaling factor (ν^{2j+1}) / (z + r^{2^{j}}) + Fr scaling_factor_neg = shplonk_batching_challenge_powers[neg_index] * inverse_vanishing_evals[neg_index]; + + // Accumulate the const term contribution given by + // v^{2j} * A_j(r^{2^j}) /(z - r^{2^j}) + v^{2j+1} * A_j(-r^{2^j}) /(z+ r^{2^j}) + constant_term_accumulator += + scaling_factor_neg * gemini_neg_evaluations[j] + scaling_factor_pos * gemini_pos_evaluations[j]; if constexpr (Curve::is_stdlib_type) { - auto builder = shplonk_batching_challenge.get_context(); + 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_circuit_size - 1)); + stdlib::bool_t dummy_round = stdlib::witness_t(builder, j >= log_circuit_size); Fr zero = Fr(0); - scaling_factor = Fr::conditional_assign(dummy_round, zero, scaling_factor); + 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_circuit_size - 1)) { - scaling_factor = 0; + if (j >= log_circuit_size) { + scaling_factor_neg = 0; + scaling_factor_pos = 0; } } // Place the scaling factor to the 'scalars' vector - scalars.emplace_back(-scaling_factor); + scalars.emplace_back(-scaling_factor_neg - scaling_factor_pos); // Move com(Aᵢ) to the 'commitments' vector - commitments.emplace_back(std::move(fold_commitments[j])); + commitments.emplace_back(std::move(fold_commitments[j - 1])); } } @@ -580,16 +610,10 @@ template class ShpleminiVerifier_ { const std::array& libra_commitments, const std::array& libra_evaluations, const Fr& gemini_evaluation_challenge, - const Fr& shplonk_batching_challenge, + const std::vector& shplonk_batching_challenge_powers, const Fr& shplonk_evaluation_challenge) { - // compute current power of Shplonk batching challenge taking into account the const proof size - Fr shplonk_challenge_power = Fr{ 1 }; - for (size_t j = 0; j < CONST_PROOF_SIZE_LOG_N + 2; ++j) { - shplonk_challenge_power *= shplonk_batching_challenge; - } - // add Libra commitments to the vector of commitments for (size_t idx = 0; idx < libra_commitments.size(); idx++) { commitments.push_back(libra_commitments[idx]); @@ -608,9 +632,8 @@ template class ShpleminiVerifier_ { // compute the scalars to be multiplied against the commitments [libra_concatenated], [grand_sum], [grand_sum], // and [libra_quotient] for (size_t idx = 0; idx < NUM_SMALL_IPA_EVALUATIONS; idx++) { - Fr scaling_factor = denominators[idx] * shplonk_challenge_power; + Fr scaling_factor = denominators[idx] * shplonk_batching_challenge_powers[NUM_GEMINI_CLAIMS + idx]; batching_scalars[idx] = -scaling_factor; - shplonk_challenge_power *= shplonk_batching_challenge; constant_term_accumulator += scaling_factor * libra_evaluations[idx]; } @@ -667,7 +690,7 @@ template class ShpleminiVerifier_ { std::vector& scalars, Fr& constant_term_accumulator, const std::vector& multilinear_challenge, - const Fr& shplonk_batching_challenge, + const std::vector& shplonk_batching_challenge_powers, const Fr& shplonk_evaluation_challenge, const std::vector& sumcheck_round_commitments, const std::vector>& sumcheck_round_evaluations) @@ -675,12 +698,6 @@ template class ShpleminiVerifier_ { std::vector denominators = {}; - // Compute the next power of Shplonk batching challenge \nu - Fr shplonk_challenge_power = Fr{ 1 }; - for (size_t j = 0; j < CONST_PROOF_SIZE_LOG_N + 2 + NUM_SMALL_IPA_EVALUATIONS; ++j) { - shplonk_challenge_power *= shplonk_batching_challenge; - } - // Denominators for the opening claims at 0 and 1. Need to be computed only once as opposed to the claims at the // sumcheck round challenges. std::array const_denominators; @@ -707,29 +724,27 @@ template class ShpleminiVerifier_ { // Each commitment to a sumcheck round univariate [S_i] is multiplied by the sum of three scalars corresponding // to the evaluations at 0, 1, and the round challenge u_i size_t round_idx = 0; + size_t power = NUM_GEMINI_CLAIMS + NUM_SMALL_IPA_EVALUATIONS; for (const auto& [eval_array, denominator] : zip_view(sumcheck_round_evaluations, denominators)) { // Initialize batched_scalar corresponding to 3 evaluations claims Fr batched_scalar = Fr(0); Fr const_term_contribution = Fr(0); - // Compute the contribution from the evaluations at 0 and 1 for (size_t idx = 0; idx < 2; idx++) { - Fr current_scaling_factor = const_denominators[idx] * shplonk_challenge_power; + Fr current_scaling_factor = const_denominators[idx] * shplonk_batching_challenge_powers[power++]; batched_scalar -= current_scaling_factor; - shplonk_challenge_power *= shplonk_batching_challenge; const_term_contribution += current_scaling_factor * eval_array[idx]; } // Compute the contribution from the evaluation at the challenge u_i - Fr current_scaling_factor = denominator * shplonk_challenge_power; + Fr current_scaling_factor = denominator * shplonk_batching_challenge_powers[power++]; batched_scalar -= current_scaling_factor; - shplonk_challenge_power *= shplonk_batching_challenge; const_term_contribution += current_scaling_factor * eval_array[2]; // Pad the accumulators with dummy 0 values const Fr zero = Fr(0); if constexpr (Curve::is_stdlib_type) { - auto builder = shplonk_batching_challenge.get_context(); + auto builder = shplonk_evaluation_challenge.get_context(); // TODO(https://github.com/AztecProtocol/barretenberg/issues/1114): insecure! stdlib::bool_t dummy_round = stdlib::witness_t(builder, round_idx >= log_circuit_size); const_term_contribution = Fr::conditional_assign(dummy_round, zero, const_term_contribution); 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 542526480699..9c651bb5a1c8 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.test.cpp @@ -196,6 +196,10 @@ TYPED_TEST(ShpleminiTest, CorrectnessOfGeminiClaimBatching) Fr rho = Fr::random_element(); Fr gemini_eval_challenge = Fr::random_element(); Fr shplonk_batching_challenge = Fr::random_element(); + + std::vector shplonk_batching_challenge_powers = + compute_shplonk_batching_challenge_powers(shplonk_batching_challenge); + Fr shplonk_eval_challenge = Fr::random_element(); std::vector mle_opening_point = this->random_evaluation_point(this->log_n); @@ -239,32 +243,44 @@ TYPED_TEST(ShpleminiTest, CorrectnessOfGeminiClaimBatching) std::vector r_squares = gemini::powers_of_evaluation_challenge(gemini_eval_challenge, this->log_n); GroupElement expected_result = GroupElement::zero(); - std::vector expected_inverse_vanishing_evals(this->log_n + 1); + std::vector expected_inverse_vanishing_evals; + expected_inverse_vanishing_evals.reserve(2 * this->log_n); // Compute expected inverses - expected_inverse_vanishing_evals[0] = (shplonk_eval_challenge - r_squares[0]).invert(); - for (size_t idx = 1; idx < this->log_n + 1; idx++) { - expected_inverse_vanishing_evals[idx] = (shplonk_eval_challenge + r_squares[idx - 1]).invert(); + for (size_t idx = 0; idx < this->log_n; idx++) { + expected_inverse_vanishing_evals.emplace_back((shplonk_eval_challenge - r_squares[idx]).invert()); + expected_inverse_vanishing_evals.emplace_back((shplonk_eval_challenge + r_squares[idx]).invert()); } Fr current_challenge{ shplonk_batching_challenge * shplonk_batching_challenge }; for (size_t idx = 0; idx < prover_commitments.size(); ++idx) { - expected_result -= prover_commitments[idx] * current_challenge * expected_inverse_vanishing_evals[idx + 2]; + expected_result -= prover_commitments[idx] * current_challenge * expected_inverse_vanishing_evals[2 * idx + 2]; + current_challenge *= shplonk_batching_challenge; + expected_result -= prover_commitments[idx] * current_challenge * expected_inverse_vanishing_evals[2 * idx + 3]; current_challenge *= shplonk_batching_challenge; } // Run the ShepliminiVerifier batching method std::vector inverse_vanishing_evals = - ShplonkVerifier::compute_inverted_gemini_denominators(this->log_n + 1, shplonk_eval_challenge, r_squares); + ShplonkVerifier::compute_inverted_gemini_denominators(shplonk_eval_challenge, r_squares); + Fr expected_constant_term_accumulator{ 0 }; + + std::vector gemini_fold_pos_evaluations = + GeminiVerifier_::compute_fold_pos_evaluations(this->log_n, + expected_constant_term_accumulator, + mle_opening_point, + r_squares, + prover_evaluations, + expected_constant_term_accumulator); std::vector commitments; std::vector scalars; - Fr expected_constant_term_accumulator{ 0 }; ShpleminiVerifier::batch_gemini_claims_received_from_prover(this->log_n, prover_commitments, prover_evaluations, + gemini_fold_pos_evaluations, inverse_vanishing_evals, - shplonk_batching_challenge, + shplonk_batching_challenge_powers, commitments, scalars, expected_constant_term_accumulator); diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp index f7cd6b15c02a..0bac8b1dca4c 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp @@ -41,6 +41,7 @@ template class ShplonkProver_ { */ static Polynomial compute_batched_quotient(std::span> opening_claims, const Fr& nu, + std::span gemini_fold_pos_evaluations, std::span> libra_opening_claims, std::span> sumcheck_round_claims) { @@ -61,7 +62,20 @@ template class ShplonkProver_ { Polynomial tmp(max_poly_size); Fr current_nu = Fr::one(); + + size_t fold_idx = 0; for (const auto& claim : opening_claims) { + + // Gemini Fold Polynomials have to be opened at -r^{2^j} and r^{2^j}. + if (claim.gemini_fold) { + tmp = claim.polynomial; + tmp.at(0) = tmp[0] - gemini_fold_pos_evaluations[fold_idx++]; + tmp.factor_roots(-claim.opening_pair.challenge); + // Add the claim quotient to the batched quotient polynomial + Q.add_scaled(tmp, current_nu); + current_nu *= nu; + } + // Compute individual claim quotient tmp = ( fⱼ(X) − vⱼ) / ( X − xⱼ ) tmp = claim.polynomial; tmp.at(0) = tmp[0] - claim.opening_pair.evaluation; @@ -72,10 +86,10 @@ template class ShplonkProver_ { } // We use the same batching challenge for Gemini and Libra opening claims. The number of the claims - // batched before adding Libra commitments and evaluations is bounded by CONST_PROOF_SIZE_LOG_N+2 - for (size_t idx = opening_claims.size(); idx < CONST_PROOF_SIZE_LOG_N + 2; idx++) { - current_nu *= nu; - }; + // batched before adding Libra commitments and evaluations is bounded by 2 * CONST_PROOF_SIZE_LOG_N + 2, where + // 2 * CONST_PROOF_SIZE_LOG_N is the number of fold claims including the dummy ones, and +2 is reserved for + // interleaving. + current_nu = nu.pow(NUM_GEMINI_CLAIMS); for (const auto& claim : libra_opening_claims) { // Compute individual claim quotient tmp = ( fⱼ(X) − vⱼ) / ( X − xⱼ ) @@ -118,6 +132,7 @@ template class ShplonkProver_ { Polynomial& batched_quotient_Q, const Fr& nu_challenge, const Fr& z_challenge, + std::span gemini_fold_pos_evaluations, std::span> libra_opening_claims = {}, std::span> sumcheck_opening_claims = {}) { @@ -127,6 +142,9 @@ template class ShplonkProver_ { std::vector inverse_vanishing_evals; inverse_vanishing_evals.reserve(num_opening_claims); for (const auto& claim : opening_claims) { + if (claim.gemini_fold) { + inverse_vanishing_evals.emplace_back(z_challenge + claim.opening_pair.challenge); + } inverse_vanishing_evals.emplace_back(z_challenge - claim.opening_pair.challenge); } @@ -150,7 +168,19 @@ template class ShplonkProver_ { Polynomial tmp(G.size()); size_t idx = 0; + size_t fold_idx = 0; for (const auto& claim : opening_claims) { + + if (claim.gemini_fold) { + tmp = claim.polynomial; + tmp.at(0) = tmp[0] - gemini_fold_pos_evaluations[fold_idx++]; + Fr scaling_factor = current_nu * inverse_vanishing_evals[idx]; // = νʲ / (z − xⱼ ) + // G -= νʲ ⋅ ( fⱼ(X) − vⱼ) / ( z − xⱼ ) + G.add_scaled(tmp, -scaling_factor); + + current_nu *= nu_challenge; + idx++; + } // tmp = νʲ ⋅ ( fⱼ(X) − vⱼ) / ( z − xⱼ ) tmp = claim.polynomial; tmp.at(0) = tmp[0] - claim.opening_pair.evaluation; @@ -164,9 +194,7 @@ template class ShplonkProver_ { } // Take into account the constant proof size in Gemini - for (size_t idx = opening_claims.size(); idx < CONST_PROOF_SIZE_LOG_N + 2; idx++) { - current_nu *= nu_challenge; - }; + current_nu = nu_challenge.pow(NUM_GEMINI_CLAIMS); for (const auto& claim : libra_opening_claims) { // Compute individual claim quotient tmp = ( fⱼ(X) − vⱼ) / ( X − xⱼ ) @@ -193,6 +221,30 @@ template class ShplonkProver_ { // Return opening pair (z, 0) and polynomial G(X) = Q(X) - Q_z(X) return { .polynomial = G, .opening_pair = { .challenge = z_challenge, .evaluation = Fr::zero() } }; }; + /** + * @brief Compute evaluations of fold polynomials Fold_i at r^{2^i} for i>0. + * TODO(https://github.com/AztecProtocol/barretenberg/issues/1223): Reconsider minor performance/memory + * optimizations in Gemini. + * @param opening_claims + * @return std::vector + */ + static std::vector compute_gemini_fold_pos_evaluations( + std::span> opening_claims) + { + std::vector gemini_fold_pos_evaluations; + gemini_fold_pos_evaluations.reserve(opening_claims.size()); + + for (const auto& claim : opening_claims) { + if (claim.gemini_fold) { + // -r^{2^i} is stored in the claim + const Fr evaluation_point = -claim.opening_pair.challenge; + // Compute Fold_i(r^{2^i}) + const Fr evaluation = claim.polynomial.evaluate(evaluation_point); + gemini_fold_pos_evaluations.emplace_back(evaluation); + } + } + return gemini_fold_pos_evaluations; + } /** * @brief Returns a batched opening claim equivalent to a set of opening claims consisting of polynomials, each @@ -211,13 +263,22 @@ template class ShplonkProver_ { std::span> sumcheck_round_claims = {}) { const Fr nu = transcript->template get_challenge("Shplonk:nu"); - auto batched_quotient = - compute_batched_quotient(opening_claims, nu, libra_opening_claims, sumcheck_round_claims); + + // Compute the evaluations Fold_i(r^{2^i}) for i>0. + std::vector gemini_fold_pos_evaluations = compute_gemini_fold_pos_evaluations(opening_claims); + + auto batched_quotient = compute_batched_quotient( + opening_claims, nu, gemini_fold_pos_evaluations, libra_opening_claims, sumcheck_round_claims); auto batched_quotient_commitment = commitment_key->commit(batched_quotient); transcript->send_to_verifier("Shplonk:Q", batched_quotient_commitment); const Fr z = transcript->template get_challenge("Shplonk:z"); - return compute_partially_evaluated_batched_quotient( - opening_claims, batched_quotient, nu, z, libra_opening_claims, sumcheck_round_claims); + return compute_partially_evaluated_batched_quotient(opening_claims, + batched_quotient, + nu, + z, + gemini_fold_pos_evaluations, + libra_opening_claims, + sumcheck_round_claims); } }; @@ -356,25 +417,67 @@ template class ShplonkVerifier_ { return { { z_challenge, evaluation }, G_commitment }; }; /** - * @brief Computes \f$ \frac{1}{z - r}, \frac{1}{z+r}, \ldots, \frac{1}{z+r^{2^{d-1}}} \f$. + * @brief Computes \f$ \frac{1}{z - r}, \frac{1}{z + r}, \ldots, \frac{1}{z - r^{2^{d-1}}}, \frac{1}{z + + * r^{2^{d-1}}} \f$. * - * @param num_gemini_claims \f$ d + 1 \f$ where d = log_circuit_size * @param shplonk_eval_challenge \f$ z \f$ * @param gemini_eval_challenge_powers \f$ (r , r^2, \ldots, r^{2^{d-1}}) \f$ - * @return \f[ \left( \frac{1}{z - r}, \frac{1}{z+r}, \ldots, \frac{1}{z+r^{2^{d-1}}} \right) \f] + * @return \f[ \left( \frac{1}{z - r}, \frac{1}{z + r}, \ldots, \frac{1}{z - r^{2^{d-1}}}, \frac{1}{z + + * r^{2^{d-1}}} \right) \f] */ - static std::vector compute_inverted_gemini_denominators(const size_t num_gemini_claims, - const Fr& shplonk_eval_challenge, + static std::vector compute_inverted_gemini_denominators(const Fr& shplonk_eval_challenge, const std::vector& gemini_eval_challenge_powers) { - std::vector inverted_denominators; - inverted_denominators.reserve(num_gemini_claims); - inverted_denominators.emplace_back((shplonk_eval_challenge - gemini_eval_challenge_powers[0]).invert()); + std::vector denominators; + denominators.reserve(NUM_GEMINI_CLAIMS); + for (const auto& gemini_eval_challenge_power : gemini_eval_challenge_powers) { - Fr round_inverted_denominator = (shplonk_eval_challenge + gemini_eval_challenge_power).invert(); - inverted_denominators.emplace_back(round_inverted_denominator); + // Place 1/(z - r ^ {2^j}) + denominators.emplace_back(shplonk_eval_challenge - gemini_eval_challenge_power); + // Place 1/(z + r ^ {2^j}) + denominators.emplace_back(shplonk_eval_challenge + gemini_eval_challenge_power); + } + + if constexpr (!Curve::is_stdlib_type) { + Fr::batch_invert(denominators); + } else { + for (auto& denominator : denominators) { + denominator = denominator.invert(); + } } - return inverted_denominators; + return denominators; } }; + +/** + * @brief A helper used by Shplemini Verifier. Precomputes a vector of the powers of \f$ \nu \f$ needed to batch all + * univariate claims. + * + */ +template +static std::vector compute_shplonk_batching_challenge_powers(const Fr& shplonk_batching_challenge, + bool has_zk = false, + bool committed_sumcheck = false) +{ + size_t num_powers = NUM_GEMINI_CLAIMS; + // Each round univariate is opened at 0, 1, and a round challenge. + static constexpr size_t NUM_COMMITTED_SUMCHECK_CLAIMS_PER_ROUND = 3; + + // Shplonk evaluation and batching challenges are re-used in SmallSubgroupIPA. + if (has_zk) { + num_powers += NUM_SMALL_IPA_EVALUATIONS; + } + + if (committed_sumcheck) { + num_powers += NUM_COMMITTED_SUMCHECK_CLAIMS_PER_ROUND * CONST_PROOF_SIZE_LOG_N; + } + + std::vector result; + result.reserve(num_powers); + result.emplace_back(Fr{ 1 }); + for (size_t idx = 1; idx < num_powers; idx++) { + result.emplace_back(result[idx - 1] * shplonk_batching_challenge); + } + return result; +} } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/constants.hpp b/barretenberg/cpp/src/barretenberg/constants.hpp index 698fa4df4e5b..7f8959b5e312 100644 --- a/barretenberg/cpp/src/barretenberg/constants.hpp +++ b/barretenberg/cpp/src/barretenberg/constants.hpp @@ -31,4 +31,9 @@ static constexpr uint32_t MERGE_PROOF_SIZE = 65; // used to ensure mock proofs a // There are 5 distinguished wires in ECCVM that have to be opened as univariates to establish the connection between // ECCVM and Translator static constexpr uint32_t NUM_TRANSLATION_EVALUATIONS = 5; +// Upper bound on the number of claims produced GeminiProver: +// - Each fold polynomial is opened at two points, the number of resulting claims is bounded by 2*CONST_PROOF_SIZE_LOG_N +// - The interleaving trick needed for Translator adds 2 extra claims +// TODO(https://github.com/AztecProtocol/barretenberg/issues/1293): Decouple Gemini from Interleaving +static constexpr uint32_t NUM_GEMINI_CLAIMS = 2 * CONST_PROOF_SIZE_LOG_N + 2; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp index 280743556ea0..97dfd3057212 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp @@ -1347,6 +1347,16 @@ struct ShpleminiIntermediates { Fr batchingChallenge; // Linear combination of multilinear (sumcheck) evaluations and powers of rho Fr batchedEvaluation; + // 1/(z - r^{2^i}) for i = 0, ..., logSize, dynamically updated + Fr posInvertedDenominator; + // 1/(z + r^{2^i}) for i = 0, ..., logSize, dynamically updated + Fr negInvertedDenominator; + // v^{2i} * 1/(z - r^{2^i}) + Fr scalingFactorPos; + // v^{2i+1} * 1/(z + r^{2^i}) + Fr scalingFactorNeg; + // // Fold_i(r^{2^i}) reconstructed by Verifier + // Fr[CONST_PROOF_SIZE_LOG_N] foldPosEvaluations; } library CommitmentSchemeLib { @@ -1359,49 +1369,31 @@ library CommitmentSchemeLib { } } - function computeInvertedGeminiDenominators( - Fr shplonkZ, - Fr[CONST_PROOF_SIZE_LOG_N] memory eval_challenge_powers, - uint256 logSize - ) internal view returns (Fr[CONST_PROOF_SIZE_LOG_N + 1] memory inverse_vanishing_evals) { - Fr eval_challenge = shplonkZ; - inverse_vanishing_evals[0] = (eval_challenge - eval_challenge_powers[0]).invert(); - - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; ++i) { - Fr round_inverted_denominator = Fr.wrap(0); - if (i <= logSize + 1) { - round_inverted_denominator = (eval_challenge + eval_challenge_powers[i]).invert(); - } - inverse_vanishing_evals[i + 1] = round_inverted_denominator; - } - } - - function computeGeminiBatchedUnivariateEvaluation( + // Compute the evaluations A_l(r^{2^l}) for l = 0, ..., m-1 + function computeFoldPosEvaluations( Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckUChallenges, Fr batchedEvalAccumulator, Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvaluations, Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvalChallengePowers, uint256 logSize - ) internal view returns (Fr a_0_pos) { + ) internal view returns (Fr[CONST_PROOF_SIZE_LOG_N] memory foldPosEvaluations) { for (uint256 i = CONST_PROOF_SIZE_LOG_N; i > 0; --i) { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr evalNeg = geminiEvaluations[i - 1]; Fr batchedEvalRoundAcc = ( (challengePower * batchedEvalAccumulator * Fr.wrap(2)) - - evalNeg * (challengePower * (Fr.wrap(1) - u) - u) + - geminiEvaluations[i - 1] * (challengePower * (Fr.wrap(1) - u) - u) ); // Divide by the denominator batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (Fr.wrap(1) - u) + u).invert(); - bool is_dummy_round = (i > logSize); - if (!is_dummy_round) { + if (i <= logSize) { batchedEvalAccumulator = batchedEvalRoundAcc; + foldPosEvaluations[i - 1] = batchedEvalRoundAcc; } } - a_0_pos = batchedEvalAccumulator; } } @@ -1595,12 +1587,12 @@ abstract contract BaseHonkVerifier is IVerifier { Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory scalars; Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory commitments; - Fr[CONST_PROOF_SIZE_LOG_N + 1] memory inverse_vanishing_evals = - CommitmentSchemeLib.computeInvertedGeminiDenominators(tp.shplonkZ, powers_of_evaluation_challenge, logN); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = inverse_vanishing_evals[0] + (tp.shplonkNu * inverse_vanishing_evals[1]); + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * (inverse_vanishing_evals[0] - (tp.shplonkNu * inverse_vanishing_evals[1])); + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = convertProofPoint(proof.shplonkQ); @@ -1665,39 +1657,50 @@ abstract contract BaseHonkVerifier is IVerifier { commitments[39] = convertProofPoint(proof.w4); commitments[40] = convertProofPoint(proof.zPerm); - mem.constantTermAccumulator = Fr.wrap(0); + // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: + // Compute the evaluations A_l(r^{2^l}) for l = 0, ..., logN - 1 + Fr[CONST_PROOF_SIZE_LOG_N] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + logN + ); + + // Compute the Shplonk constant term contributions from A₀(±r) + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); + // Compute Shplonk constant term contributions from Aₗ(±r^{2ˡ}) for l = 1, ..., m-1; + // Compute scalar multipliers for each fold commitment for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N - 1; ++i) { bool dummy_round = i >= (logN - 1); - Fr scalingFactor = Fr.wrap(0); if (!dummy_round) { - scalingFactor = mem.batchingChallenge * inverse_vanishing_evals[i + 2]; - scalars[NUMBER_OF_ENTITIES + 1 + i] = scalingFactor.neg(); + // Update inverted denominators + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + + // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + // [Aₗ] is multiplied by -v^{2l}/(z-r^{2^l}) - v^{2l+1} /(z+ r^{2^l}) + scalars[NUMBER_OF_ENTITIES + 1 + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + + // Accumulate the const term contribution given by + // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; + // Update the running power of v + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; } - mem.constantTermAccumulator = - mem.constantTermAccumulator + (scalingFactor * proof.geminiAEvaluations[i + 1]); - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - commitments[NUMBER_OF_ENTITIES + 1 + i] = convertProofPoint(proof.geminiFoldComms[i]); } - // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: - // Compute evaluation A₀(r) - Fr a_0_pos = CommitmentSchemeLib.computeGeminiBatchedUnivariateEvaluation( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - logN - ); - - mem.constantTermAccumulator = mem.constantTermAccumulator + (a_0_pos * inverse_vanishing_evals[0]); - mem.constantTermAccumulator = - mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * inverse_vanishing_evals[1]); - // Finalise the batch opening claim commitments[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = Honk.G1Point({x: 1, y: 2}); scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = mem.constantTermAccumulator; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_zk_contract.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_zk_contract.hpp index ccbdcbe39b71..d540e8463826 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_zk_contract.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_zk_contract.hpp @@ -1414,8 +1414,14 @@ struct ShpleminiIntermediates { Fr batchedEvaluation; Fr[4] denominators; Fr[4] batchingScalars; - Fr[CONST_PROOF_SIZE_LOG_N + 1] inverse_vanishing_denominators; - + // 1/(z - r^{2^i}) for i = 0, ..., logSize, dynamically updated + Fr posInvertedDenominator; + // 1/(z + r^{2^i}) for i = 0, ..., logSize, dynamically updated + Fr negInvertedDenominator; + // v^{2i} * 1/(z - r^{2^i}) + Fr scalingFactorPos; + // v^{2i+1} * 1/(z + r^{2^i}) + Fr scalingFactorNeg; } library CommitmentSchemeLib { @@ -1444,29 +1450,29 @@ library CommitmentSchemeLib { } } - function computeGeminiBatchedUnivariateEvaluation( + // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., m-1 + function computeFoldPosEvaluations( Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckUChallenges, Fr batchedEvalAccumulator, Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvaluations, - Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvalChallengePowers - ) internal view returns (Fr a_0_pos) { + Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvalChallengePowers, + uint256 logSize + ) internal view returns (Fr[CONST_PROOF_SIZE_LOG_N] memory foldPosEvaluations) { for (uint256 i = CONST_PROOF_SIZE_LOG_N; i > 0; --i) { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; Fr batchedEvalRoundAcc = ( (challengePower * batchedEvalAccumulator * Fr.wrap(2)) - - geminiEvaluations[i - 1] * (challengePower * (ONE - u) - u) + - geminiEvaluations[i - 1] * (challengePower * (Fr.wrap(1) - u) - u) ); // Divide by the denominator - batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); - - if (i <= LOG_N) { + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (Fr.wrap(1) - u) + u).invert(); + if (i <= logSize) { batchedEvalAccumulator = batchedEvalRoundAcc; + foldPosEvaluations[i - 1] = batchedEvalRoundAcc; } - } - - a_0_pos = batchedEvalAccumulator; + } } } @@ -1638,13 +1644,13 @@ interface IVerifier { Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 3 + 3] memory scalars; Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 3 + 3] memory commitments; - mem.inverse_vanishing_denominators = - CommitmentSchemeLib.computeInvertedGeminiDenominators(tp.shplonkZ, powers_of_evaluation_challenge, LOG_N); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); + - mem.unshiftedScalar = - mem.inverse_vanishing_denominators[0] + (tp.shplonkNu * mem.inverse_vanishing_denominators[1]); - mem.shiftedScalar = tp.geminiR.invert() - * (mem.inverse_vanishing_denominators[0] - (tp.shplonkNu * mem.inverse_vanishing_denominators[1])); + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); + mem.shiftedScalar = + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = ONE; commitments[0] = convertProofPoint(proof.shplonkQ); @@ -1711,40 +1717,53 @@ interface IVerifier { commitments[40] = convertProofPoint(proof.w4); commitments[41] = convertProofPoint(proof.zPerm); - mem.constantTermAccumulator = ZERO; + + // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: + // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., logN - 1 + Fr[CONST_PROOF_SIZE_LOG_N] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + LOG_N + ); + + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_OF_ENTITIES + 2; + // Compute Shplonk constant term contributions from Aₗ(± r^{2ˡ}) for l = 1, ..., m-1; + // Compute scalar multipliers for each fold commitment for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N - 1; ++i) { bool dummy_round = i >= (LOG_N - 1); - Fr scalingFactor = ZERO; if (!dummy_round) { - scalingFactor = mem.batchingChallenge * mem.inverse_vanishing_denominators[i + 2]; - scalars[boundary + i] = scalingFactor.neg(); + // Update inverted denominators + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + + // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + + // Accumulate the const term contribution given by + // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; } - - mem.constantTermAccumulator = - mem.constantTermAccumulator + (scalingFactor * proof.geminiAEvaluations[i + 1]); - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; + // Update the running power of v + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; commitments[boundary + i] = convertProofPoint(proof.geminiFoldComms[i]); } boundary += CONST_PROOF_SIZE_LOG_N - 1; - // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: - // Compute evaluation A₀(r) - Fr a_0_pos = CommitmentSchemeLib.computeGeminiBatchedUnivariateEvaluation( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge - ); - - mem.constantTermAccumulator = mem.constantTermAccumulator + (a_0_pos * mem.inverse_vanishing_denominators[0]); - mem.constantTermAccumulator = mem.constantTermAccumulator - + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.inverse_vanishing_denominators[1]); // Finalise the batch opening claim mem.denominators[0] = ONE.div(tp.shplonkZ - tp.geminiR); @@ -1752,7 +1771,8 @@ interface IVerifier { mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; + // Artifact of interleaving, see TODO(https://github.com/AztecProtocol/barretenberg/issues/1293): Decouple Gemini from Interleaving + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; for (uint256 i = 0; i < 4; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); diff --git a/barretenberg/cpp/src/barretenberg/honk/utils/honk_key_gen.hpp b/barretenberg/cpp/src/barretenberg/honk/utils/honk_key_gen.hpp index 8f6b6a0a26c6..878f76be5b3c 100644 --- a/barretenberg/cpp/src/barretenberg/honk/utils/honk_key_gen.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/utils/honk_key_gen.hpp @@ -103,4 +103,4 @@ inline void output_vk_sol_ultra_honk(std::ostream& os, "}\n"; os << std::flush; -} \ No newline at end of file +} diff --git a/barretenberg/cpp/src/barretenberg/solidity_helpers/honk_proof_gen.cpp b/barretenberg/cpp/src/barretenberg/solidity_helpers/honk_proof_gen.cpp index 6fc3eaf6a453..c409f0d4fd98 100644 --- a/barretenberg/cpp/src/barretenberg/solidity_helpers/honk_proof_gen.cpp +++ b/barretenberg/cpp/src/barretenberg/solidity_helpers/honk_proof_gen.cpp @@ -115,4 +115,4 @@ int main(int argc, char** argv) info("Only honk flavor allowed"); return 1; } -} \ No newline at end of file +} diff --git a/barretenberg/sol/scripts/run_fuzzer.sh b/barretenberg/sol/scripts/run_fuzzer.sh index 5565f0d3af88..9428847bff64 100755 --- a/barretenberg/sol/scripts/run_fuzzer.sh +++ b/barretenberg/sol/scripts/run_fuzzer.sh @@ -16,4 +16,4 @@ if [ "$FLAVOR" == "honk" ] || [ "$FLAVOR" == "honk_zk" ] ; then fi # @note This needs to be updated to point to the generator -$BIN $FLAVOR $CIRCUIT $SRS_PATH $INPUTS \ No newline at end of file +$BIN $FLAVOR $CIRCUIT $SRS_PATH $INPUTS diff --git a/barretenberg/sol/src/honk/BaseHonkVerifier.sol b/barretenberg/sol/src/honk/BaseHonkVerifier.sol index 2f879189793d..9ecafd76207a 100644 --- a/barretenberg/sol/src/honk/BaseHonkVerifier.sol +++ b/barretenberg/sol/src/honk/BaseHonkVerifier.sol @@ -63,12 +63,14 @@ abstract contract BaseHonkVerifier is IVerifier { // Generate the fiat shamir challenges for the whole protocol // TODO(https://github.com/AztecProtocol/barretenberg/issues/1281): Add pubInputsOffset to VK or remove entirely. - Transcript memory t = TranscriptLib.generateTranscript(p, publicInputs, vk.circuitSize, numPublicInputs, /*pubInputsOffset=*/1); + Transcript memory t = + TranscriptLib.generateTranscript(p, publicInputs, vk.circuitSize, numPublicInputs, /*pubInputsOffset=*/ 1); // Derive public input delta // TODO(https://github.com/AztecProtocol/barretenberg/issues/1281): Add pubInputsOffset to VK or remove entirely. - t.relationParameters.publicInputsDelta = - computePublicInputDelta(publicInputs, t.relationParameters.beta, t.relationParameters.gamma, /*pubInputsOffset=*/1); + t.relationParameters.publicInputsDelta = computePublicInputDelta( + publicInputs, t.relationParameters.beta, t.relationParameters.gamma, /*pubInputsOffset=*/ 1 + ); // Sumcheck bool sumcheckVerified = verifySumcheck(p, t); @@ -210,12 +212,12 @@ abstract contract BaseHonkVerifier is IVerifier { Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory scalars; Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] memory commitments; - Fr[CONST_PROOF_SIZE_LOG_N + 1] memory inverse_vanishing_evals = - PCS.computeInvertedGeminiDenominators(tp.shplonkZ, powers_of_evaluation_challenge, logN); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = inverse_vanishing_evals[0] + (tp.shplonkNu * inverse_vanishing_evals[1]); + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * (inverse_vanishing_evals[0] - (tp.shplonkNu * inverse_vanishing_evals[1])); + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = ONE; commitments[0] = convertProofPoint(proof.shplonkQ); @@ -329,39 +331,51 @@ abstract contract BaseHonkVerifier is IVerifier { * \f] * and adds them to the 'constant_term_accumulator'. */ - mem.constantTermAccumulator = ZERO; + + // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., logN - 1 + Fr[CONST_PROOF_SIZE_LOG_N] memory foldPosEvaluations = PCS.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + logN + ); + + // Compute the Shplonk constant term contributions from A₀(±r) + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + mem.batchingChallenge = tp.shplonkNu.sqr(); + // Compute Shplonk constant term contributions from Aₗ(± r^{2ˡ}) for l = 1, ..., m-1; + // Compute scalar multipliers for each fold commitment for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N - 1; ++i) { bool dummy_round = i >= (logN - 1); - Fr scalingFactor = ZERO; if (!dummy_round) { - scalingFactor = mem.batchingChallenge * inverse_vanishing_evals[i + 2]; - scalars[NUMBER_OF_ENTITIES + 1 + i] = scalingFactor.neg(); + // Update inverted denominators + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + + // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + // [Aₗ] is multiplied by -v^{2l}/(z-r^{2^l}) - v^{2l+1} /(z+ r^{2^l}) + scalars[NUMBER_OF_ENTITIES + 1 + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + + // Accumulate the const term contribution given by + // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; + // Update the running power of v + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; } - mem.constantTermAccumulator = - mem.constantTermAccumulator + (scalingFactor * proof.geminiAEvaluations[i + 1]); - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - commitments[NUMBER_OF_ENTITIES + 1 + i] = convertProofPoint(proof.geminiFoldComms[i]); } - // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: - // Compute evaluation A₀(r) - Fr a_0_pos = PCS.computeGeminiBatchedUnivariateEvaluation( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - logN - ); - - mem.constantTermAccumulator = mem.constantTermAccumulator + (a_0_pos * inverse_vanishing_evals[0]); - mem.constantTermAccumulator = - mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * inverse_vanishing_evals[1]); - // Finalise the batch opening claim commitments[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = Honk.G1Point({x: 1, y: 2}); scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = mem.constantTermAccumulator; diff --git a/barretenberg/sol/src/honk/BaseZKHonkVerifier.sol b/barretenberg/sol/src/honk/BaseZKHonkVerifier.sol index d8604c65664d..6cc6a472578b 100644 --- a/barretenberg/sol/src/honk/BaseZKHonkVerifier.sol +++ b/barretenberg/sol/src/honk/BaseZKHonkVerifier.sol @@ -73,12 +73,14 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Generate the fiat shamir challenges for the whole protocol // TODO(https://github.com/AztecProtocol/barretenberg/issues/1281): Add pubInputsOffset to VK or remove entirely. - ZKTranscript memory t = ZKTranscriptLib.generateTranscript(p, publicInputs, vk.circuitSize, numPublicInputs, /*pubInputsOffset=*/1); + ZKTranscript memory t = + ZKTranscriptLib.generateTranscript(p, publicInputs, vk.circuitSize, numPublicInputs, /*pubInputsOffset=*/ 1); // Derive public input delta // TODO(https://github.com/AztecProtocol/barretenberg/issues/1281): Add pubInputsOffset to VK or remove entirely. - t.relationParameters.publicInputsDelta = - computePublicInputDelta(publicInputs, t.relationParameters.beta, t.relationParameters.gamma, /*pubInputsOffset=*/1); + t.relationParameters.publicInputsDelta = computePublicInputDelta( + publicInputs, t.relationParameters.beta, t.relationParameters.gamma, /*pubInputsOffset=*/ 1 + ); // Sumcheck if (!verifySumcheck(p, t)) revert SumcheckFailed(); @@ -208,13 +210,12 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + LIBRA_COMMITMENTS + 3] memory scalars; Honk.G1Point[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + LIBRA_COMMITMENTS + 3] memory commitments; - mem.inverse_vanishing_denominators = - PCS.computeInvertedGeminiDenominators(tp.shplonkZ, powers_of_evaluation_challenge, logN); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = - mem.inverse_vanishing_denominators[0] + (tp.shplonkNu * mem.inverse_vanishing_denominators[1]); - mem.shiftedScalar = tp.geminiR.invert() - * (mem.inverse_vanishing_denominators[0] - (tp.shplonkNu * mem.inverse_vanishing_denominators[1])); + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); + mem.shiftedScalar = + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = convertProofPoint(proof.shplonkQ); @@ -330,49 +331,61 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \f] * and adds them to the 'constant_term_accumulator'. */ - mem.constantTermAccumulator = Fr.wrap(0); + + // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: + // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., logN - 1 + Fr[CONST_PROOF_SIZE_LOG_N] memory foldPosEvaluations = PCS.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + logN + ); + + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_OF_ENTITIES + 2; + // Compute Shplonk constant term contributions from Aₗ(± r^{2ˡ}) for l = 1, ..., m-1; + // Compute scalar multipliers for each fold commitment for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N - 1; ++i) { bool dummy_round = i >= (logN - 1); - Fr scalingFactor = Fr.wrap(0); if (!dummy_round) { - scalingFactor = mem.batchingChallenge * mem.inverse_vanishing_denominators[i + 2]; - scalars[boundary + i] = scalingFactor.neg(); + // Update inverted denominators + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + + // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + + // Accumulate the const term contribution given by + // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; } - - mem.constantTermAccumulator = - mem.constantTermAccumulator + (scalingFactor * proof.geminiAEvaluations[i + 1]); - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; + // Update the running power of v + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; commitments[boundary + i] = convertProofPoint(proof.geminiFoldComms[i]); } boundary += CONST_PROOF_SIZE_LOG_N - 1; - // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: - // Compute evaluation A₀(r) - Fr a_0_pos = PCS.computeGeminiBatchedUnivariateEvaluation( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - logN - ); - - mem.constantTermAccumulator = mem.constantTermAccumulator + (a_0_pos * mem.inverse_vanishing_denominators[0]); - mem.constantTermAccumulator = mem.constantTermAccumulator - + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.inverse_vanishing_denominators[1]); - // Finalise the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; + // Artifact of interleaving, see TODO(https://github.com/AztecProtocol/barretenberg/issues/1293): Decouple Gemini from Interleaving + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); diff --git a/barretenberg/sol/src/honk/CommitmentScheme.sol b/barretenberg/sol/src/honk/CommitmentScheme.sol index 1be6ca8ab1b0..4ff835271693 100644 --- a/barretenberg/sol/src/honk/CommitmentScheme.sol +++ b/barretenberg/sol/src/honk/CommitmentScheme.sol @@ -32,7 +32,16 @@ library CommitmentSchemeLib { Fr batchedEvaluation; Fr[4] denominators; Fr[4] batchingScalars; - Fr[CONST_PROOF_SIZE_LOG_N + 1] inverse_vanishing_denominators; + // 1/(z - r^{2^i}) for i = 0, ..., logSize, dynamically updated + Fr posInvertedDenominator; + // 1/(z + r^{2^i}) for i = 0, ..., logSize, dynamically updated + Fr negInvertedDenominator; + // ν^{2i} * 1/(z - r^{2^i}) + Fr scalingFactorPos; + // ν^{2i+1} * 1/(z + r^{2^i}) + Fr scalingFactorNeg; + // Fold_i(r^{2^i}) reconstructed by Verifier + Fr[CONST_PROOF_SIZE_LOG_N] foldPosEvaluations; } function computeSquares(Fr r) internal pure returns (Fr[CONST_PROOF_SIZE_LOG_N] memory squares) { @@ -41,30 +50,15 @@ library CommitmentSchemeLib { squares[i] = squares[i - 1].sqr(); } } + // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., m-1 - function computeInvertedGeminiDenominators( - Fr shplonkZ, - Fr[CONST_PROOF_SIZE_LOG_N] memory eval_challenge_powers, - uint256 logSize - ) internal view returns (Fr[CONST_PROOF_SIZE_LOG_N + 1] memory inverse_vanishing_evals) { - inverse_vanishing_evals[0] = (shplonkZ - eval_challenge_powers[0]).invert(); - - for (uint256 i = 0; i < CONST_PROOF_SIZE_LOG_N; ++i) { - Fr round_inverted_denominator = ZERO; - if (i <= logSize + 1) { - round_inverted_denominator = (shplonkZ + eval_challenge_powers[i]).invert(); - } - inverse_vanishing_evals[i + 1] = round_inverted_denominator; - } - } - - function computeGeminiBatchedUnivariateEvaluation( + function computeFoldPosEvaluations( Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckUChallenges, Fr batchedEvalAccumulator, Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvaluations, Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvalChallengePowers, uint256 logSize - ) internal view returns (Fr a_0_pos) { + ) internal view returns (Fr[CONST_PROOF_SIZE_LOG_N] memory foldPosEvaluations) { for (uint256 i = CONST_PROOF_SIZE_LOG_N; i > 0; --i) { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; @@ -75,12 +69,10 @@ library CommitmentSchemeLib { ); // Divide by the denominator batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); - if (i <= logSize) { batchedEvalAccumulator = batchedEvalRoundAcc; + foldPosEvaluations[i - 1] = batchedEvalRoundAcc; } } - - a_0_pos = batchedEvalAccumulator; } } diff --git a/barretenberg/sol/src/ultra/keys/BlakeUltraVerificationKey.sol b/barretenberg/sol/src/ultra/keys/BlakeUltraVerificationKey.sol index d81948a7761b..6bf25e6238fb 100644 --- a/barretenberg/sol/src/ultra/keys/BlakeUltraVerificationKey.sol +++ b/barretenberg/sol/src/ultra/keys/BlakeUltraVerificationKey.sol @@ -1,11 +1,11 @@ -// Verification Key Hash: 4dd3c85ec6b387fe6e6b3dc9b4eb16c522fec0c8b0e6bdc6b34bf32af822b2d8 +// Verification Key Hash: 6c68aa62aa452a4dbb84e1246b10262e6fb6ff860b59d906db6fb0807d11b872 // SPDX-License-Identifier: Apache-2.0 // Copyright 2022 Aztec pragma solidity >=0.8.4; library BlakeUltraVerificationKey { function verificationKeyHash() internal pure returns (bytes32) { - return 0x4dd3c85ec6b387fe6e6b3dc9b4eb16c522fec0c8b0e6bdc6b34bf32af822b2d8; + return 0x6c68aa62aa452a4dbb84e1246b10262e6fb6ff860b59d906db6fb0807d11b872; } function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure { diff --git a/barretenberg/sol/src/ultra/keys/EcdsaUltraVerificationKey.sol b/barretenberg/sol/src/ultra/keys/EcdsaUltraVerificationKey.sol index eeffbdec7523..29b3cd73f1a9 100644 --- a/barretenberg/sol/src/ultra/keys/EcdsaUltraVerificationKey.sol +++ b/barretenberg/sol/src/ultra/keys/EcdsaUltraVerificationKey.sol @@ -1,11 +1,11 @@ -// Verification Key Hash: 051da9b5f32346e9d0892040b55aa70862447630aa29cecc3abe3cb74517137c +// Verification Key Hash: 543ba4bb3b055949d65bec2da67f0c096105b2fbde342418408e48a04c96b193 // SPDX-License-Identifier: Apache-2.0 // Copyright 2022 Aztec pragma solidity >=0.8.4; library EcdsaUltraVerificationKey { function verificationKeyHash() internal pure returns (bytes32) { - return 0x051da9b5f32346e9d0892040b55aa70862447630aa29cecc3abe3cb74517137c; + return 0x543ba4bb3b055949d65bec2da67f0c096105b2fbde342418408e48a04c96b193; } function loadVerificationKey(uint256 _vk, uint256 _omegaInverseLoc) internal pure {