diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp index d8fa2ab6e54c..aa9b6c8d7874 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.hpp @@ -103,11 +103,14 @@ template class GeminiProver_ { static std::vector compute_fold_polynomials(const size_t log_N, std::span multilinear_challenge, Polynomial&& batched_unshifted, - Polynomial&& batched_to_be_shifted); + Polynomial&& batched_to_be_shifted, + Polynomial&& batched_concatenated = {}); - static std::vector compute_fold_polynomial_evaluations(const size_t log_N, - std::vector&& fold_polynomials, - const Fr& r_challenge); + static std::vector compute_fold_polynomial_evaluations( + const size_t log_N, + std::vector&& fold_polynomials, + const Fr& r_challenge, + std::vector&& batched_groups_to_be_concatenated = {}); template static std::vector prove(const Fr circuit_size, @@ -115,7 +118,10 @@ template class GeminiProver_ { RefSpan g_polynomials, std::span multilinear_challenge, const std::shared_ptr>& commitment_key, - const std::shared_ptr& transcript); + const std::shared_ptr& transcript, + RefSpan concatenated_polynomials = {}, + const std::vector>& groups_to_be_concatenated = {}); + }; // namespace bb template class GeminiVerifier_ { @@ -136,32 +142,38 @@ template class GeminiVerifier_ { * @return Fold polynomial opening claims: (r, A₀(r), C₀₊), (-r, A₀(-r), C₀₋), and * (Cⱼ, Aⱼ(-r^{2ʲ}), -r^{2}), j = [1, ..., m-1] */ - static std::vector> reduce_verification(std::span multilinear_challenge, - std::span multilinear_evaluations, - RefSpan unshifted_commitments, - RefSpan to_be_shifted_commitments, - auto& transcript) + static std::vector> reduce_verification( + std::span multilinear_challenge, + RefSpan unshifted_evaluations, + RefSpan shifted_evaluations, + RefSpan unshifted_commitments, + RefSpan to_be_shifted_commitments, + auto& transcript, + const std::vector>& concatenation_group_commitments = {}, + RefSpan concatenated_evaluations = {}) + { const size_t num_variables = multilinear_challenge.size(); + const size_t N = 1 << num_variables; + Fr rho = transcript->template get_challenge("rho"); - std::vector rhos = gemini::powers_of_rho(rho, multilinear_evaluations.size()); GroupElement batched_commitment_unshifted = GroupElement::zero(); GroupElement batched_commitment_to_be_shifted = GroupElement::zero(); - Fr batched_evaluation = Fr::zero(); - for (size_t i = 0; i < multilinear_evaluations.size(); ++i) { - batched_evaluation += multilinear_evaluations[i] * rhos[i]; + Fr batched_evaluation = Fr(0); + Fr batching_scalar = Fr(1); + for (auto [eval, comm] : zip_view(unshifted_evaluations, unshifted_commitments)) { + batched_evaluation += eval * batching_scalar; + batched_commitment_unshifted += comm * batching_scalar; + batching_scalar *= rho; } - const size_t num_unshifted = unshifted_commitments.size(); - const size_t num_to_be_shifted = to_be_shifted_commitments.size(); - for (size_t i = 0; i < num_unshifted; i++) { - batched_commitment_unshifted += unshifted_commitments[i] * rhos[i]; - } - for (size_t i = 0; i < num_to_be_shifted; i++) { - batched_commitment_to_be_shifted += to_be_shifted_commitments[i] * rhos[num_unshifted + i]; + for (auto [eval, comm] : zip_view(shifted_evaluations, to_be_shifted_commitments)) { + batched_evaluation += eval * batching_scalar; + batched_commitment_to_be_shifted += comm * batching_scalar; + batching_scalar *= rho; } // Get polynomials Fold_i, i = 1,...,m-1 from transcript @@ -173,22 +185,71 @@ template class GeminiVerifier_ { // Get evaluations a_i, i = 0,...,m-1 from transcript const std::vector evaluations = get_gemini_evaluations(num_variables, transcript); + + // C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] + r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ], the commitment to A₀₊ + // C₀_r_neg = ∑ⱼ ρʲ⋅[fⱼ] - r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ], the commitment to A₀₋ + GroupElement C0_r_pos = batched_commitment_unshifted; + GroupElement C0_r_neg = batched_commitment_unshifted; + + Fr r_inv = r.invert(); + if (!batched_commitment_to_be_shifted.is_point_at_infinity()) { + batched_commitment_to_be_shifted = batched_commitment_to_be_shifted * r_inv; + C0_r_pos += batched_commitment_to_be_shifted; + C0_r_neg -= batched_commitment_to_be_shifted; + } + + // If verifying the opening for the translator VM, we reconstruct the commitment of the batched concatenated + // polynomials, partially evaluated in r and -r, using the commitments in the concatenation groups and add their + // contribution as well to to C₀_r_pos and C₀_r_neg + ASSERT(concatenated_evaluations.size() == concatenation_group_commitments.size()); + if (!concatenation_group_commitments.empty()) { + size_t concatenation_group_size = concatenation_group_commitments[0].size(); + // The "real" size of polynomials in concatenation groups (i.e. the number of non-zero values) + const size_t mini_circuit_size = N / concatenation_group_size; + Fr current_r_shift_pos = Fr(1); + Fr current_r_shift_neg = Fr(1); + const Fr r_pow_minicircuit = r.pow(mini_circuit_size); + const Fr r_neg_pow_minicircuit = (-r).pow(mini_circuit_size); + std::vector r_shifts_pos; + std::vector r_shifts_neg; + for (size_t i = 0; i < concatenation_group_size; ++i) { + r_shifts_pos.emplace_back(current_r_shift_pos); + r_shifts_neg.emplace_back(current_r_shift_neg); + current_r_shift_pos *= r_pow_minicircuit; + current_r_shift_neg *= r_neg_pow_minicircuit; + } + size_t j = 0; + GroupElement batched_concatenated_pos = GroupElement::zero(); + GroupElement batched_concatenated_neg = GroupElement::zero(); + for (auto& concatenation_group_commitment : concatenation_group_commitments) { + // Compute the contribution from each group j of commitments Gⱼ = {C₀, C₁, C₂, C₃, ...} + // where s = mini_circuit_size as + // C₀_r_pos += ∑ᵢ ρᵏ⁺ᵐ⁺ʲ⋅ rⁱˢ ⋅ Cᵢ + // C₀_r_neg += ∑ᵢ ρᵏ⁺ᵐ⁺ʲ⋅ (-r)ⁱˢ ⋅ Cᵢ + for (size_t i = 0; i < concatenation_group_size; ++i) { + batched_concatenated_pos += concatenation_group_commitment[i] * batching_scalar * r_shifts_pos[i]; + batched_concatenated_neg += concatenation_group_commitment[i] * batching_scalar * r_shifts_neg[i]; + } + batched_evaluation += concatenated_evaluations[j] * batching_scalar; + batching_scalar *= rho; + j++; + } + + // Add the contributions from concatenation groups to get the final [A₀₊] and [A₀₋] + C0_r_pos += batched_concatenated_pos; + C0_r_neg += batched_concatenated_neg; + } + // Compute evaluation A₀(r) auto a_0_pos = compute_gemini_batched_univariate_evaluation( num_variables, batched_evaluation, multilinear_challenge, r_squares, evaluations); - - // C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] + r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ] - // C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] - r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ] - auto [c0_r_pos, c0_r_neg] = - compute_simulated_commitments(batched_commitment_unshifted, batched_commitment_to_be_shifted, r); - std::vector> fold_polynomial_opening_claims; fold_polynomial_opening_claims.reserve(num_variables + 1); // ( [A₀₊], r, A₀(r) ) - fold_polynomial_opening_claims.emplace_back(OpeningClaim{ { r, a_0_pos }, c0_r_pos }); + fold_polynomial_opening_claims.emplace_back(OpeningClaim{ { r, a_0_pos }, C0_r_pos }); // ( [A₀₋], -r, A₀(-r) ) - fold_polynomial_opening_claims.emplace_back(OpeningClaim{ { -r, evaluations[0] }, c0_r_neg }); + 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ˡ}) ) fold_polynomial_opening_claims.emplace_back( @@ -283,48 +344,6 @@ template class GeminiVerifier_ { return batched_eval_accumulator; } - - /** - * @brief Computes two commitments to A₀ partially evaluated in r and -r. - * - * @param batched_commitment_unshifted batched commitment to non-shifted polynomials - * @param batched_commitment_to_be_shifted batched commitment to to-be-shifted polynomials - * @param r evaluation point at which we have partially evaluated A₀ at r and -r. - * @return std::pair c0_r_pos, c0_r_neg - */ - static std::pair compute_simulated_commitments( - GroupElement& batched_commitment_unshifted, GroupElement& batched_commitment_to_be_shifted, Fr r) - { - // C₀ᵣ₊ = [F] + r⁻¹⋅[G] - GroupElement C0_r_pos; - // C₀ᵣ₋ = [F] - r⁻¹⋅[G] - GroupElement C0_r_neg; - Fr r_inv = r.invert(); // r⁻¹ - - // If in a recursive setting, perform a batch mul. Otherwise, accumulate directly. - // TODO(#673): The following if-else represents the stldib/native code paths. Once the "native" verifier is - // achieved through a builder Simulator, the stdlib codepath should become the only codepath. - if constexpr (Curve::is_stdlib_type) { - std::vector commitments = { batched_commitment_unshifted, batched_commitment_to_be_shifted }; - auto builder = r.get_context(); - auto one = Fr(builder, 1); - // TODO(#707): these batch muls include the use of 1 as a scalar. This is handled appropriately as a non-mul - // (add-accumulate) in the goblin batch_mul but is done inefficiently as a scalar mul in the conventional - // emulated batch mul. - C0_r_pos = GroupElement::batch_mul(commitments, { one, r_inv }); - C0_r_neg = GroupElement::batch_mul(commitments, { one, -r_inv }); - } else { - C0_r_pos = batched_commitment_unshifted; - C0_r_neg = batched_commitment_unshifted; - if (!batched_commitment_to_be_shifted.is_point_at_infinity()) { - batched_commitment_to_be_shifted = batched_commitment_to_be_shifted * r_inv; - C0_r_pos += batched_commitment_to_be_shifted; - C0_r_neg -= batched_commitment_to_be_shifted; - } - } - - return { C0_r_pos, C0_r_neg }; - } }; } // namespace bb 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 ebb8e2ccb2e2..af478b9743f5 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.test.cpp @@ -1,8 +1,7 @@ #include "gemini_impl.hpp" #include "../commitment_key.test.hpp" -#include "barretenberg/polynomials/polynomial.hpp" -#include "barretenberg/transcript/transcript.hpp" +#include "barretenberg/commitment_schemes/utils/test_utils.hpp" using namespace bb; @@ -10,15 +9,16 @@ template class GeminiTest : public CommitmentTest { using GeminiProver = GeminiProver_; using GeminiVerifier = GeminiVerifier_; using Fr = typename Curve::ScalarField; - using GroupElement = typename Curve::Element; + using Commitment = typename Curve::AffineElement; public: void execute_gemini_and_verify_claims(std::vector& multilinear_evaluation_point, - std::vector& multilinear_evaluations, + RefVector multilinear_evaluations_unshifted, + RefVector multilinear_evaluations_shifted, RefSpan> multilinear_polynomials, RefSpan> multilinear_polynomials_to_be_shifted, - RefVector multilinear_commitments, - RefVector multilinear_commitments_to_be_shifted) + RefVector multilinear_commitments, + RefVector multilinear_commitments_to_be_shifted) { auto prover_transcript = NativeTranscript::prover_init_empty(); @@ -42,15 +42,69 @@ template class GeminiTest : public CommitmentTest { // - 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 auto verifier_claims = GeminiVerifier::reduce_verification(multilinear_evaluation_point, - multilinear_evaluations, - RefVector(multilinear_commitments), - RefVector(multilinear_commitments_to_be_shifted), + multilinear_evaluations_unshifted, + multilinear_evaluations_shifted, + multilinear_commitments, + multilinear_commitments_to_be_shifted, 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)) { + this->verify_opening_claim(verifier_claim, prover_claim.polynomial); ASSERT_EQ(prover_claim.opening_pair, verifier_claim.opening_pair); + } + } + + void execute_gemini_and_verify_claims_with_concatenation( + std::vector& multilinear_evaluation_point, + RefVector multilinear_evaluations_unshifted, + RefVector multilinear_evaluations_shifted, + RefSpan> multilinear_polynomials, + RefSpan> multilinear_polynomials_to_be_shifted, + RefVector multilinear_commitments, + RefVector multilinear_commitments_to_be_shifted, + RefSpan> concatenated_polynomials = {}, + RefSpan concatenated_evaluations = {}, + const std::vector>>& groups_to_be_concatenated = {}, + const std::vector>& concatenation_group_commitments = {}) + + { + auto prover_transcript = NativeTranscript::prover_init_empty(); + + // Compute: + // - (d+1) opening pairs: {r, \hat{a}_0}, {-r^{2^i}, a_i}, i = 0, ..., d-1 + // - (d+1) Fold polynomials Fold_{r}^(0), Fold_{-r}^(0), and Fold^(i), i = 0, ..., d-1 + auto prover_output = GeminiProver::prove(1 << multilinear_evaluation_point.size(), + multilinear_polynomials, + multilinear_polynomials_to_be_shifted, + multilinear_evaluation_point, + this->commitment_key, + prover_transcript, + concatenated_polynomials, + groups_to_be_concatenated); + + // Check that the Fold polynomials have been evaluated correctly in the prover + this->verify_batch_opening_pair(prover_output); + + auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript); + + // Compute: + // - Single opening pair: {r, \hat{a}_0} + // - 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 + auto verifier_claims = GeminiVerifier::reduce_verification(multilinear_evaluation_point, + multilinear_evaluations_unshifted, + multilinear_evaluations_shifted, + multilinear_commitments, + multilinear_commitments_to_be_shifted, + verifier_transcript, + concatenation_group_commitments, + concatenated_evaluations); + + // Check equality of the opening pairs computed by prover and verifier + for (auto [prover_claim, verifier_claim] : zip_view(prover_output, verifier_claims)) { this->verify_opening_claim(verifier_claim, prover_claim.polynomial); + ASSERT_EQ(prover_claim.opening_pair, verifier_claim.opening_pair); } } }; @@ -61,7 +115,7 @@ TYPED_TEST_SUITE(GeminiTest, ParamsTypes); TYPED_TEST(GeminiTest, Single) { using Fr = typename TypeParam::ScalarField; - using GroupElement = typename TypeParam::Element; + using Commitment = typename TypeParam::AffineElement; const size_t n = 16; const size_t log_n = 4; @@ -72,14 +126,16 @@ TYPED_TEST(GeminiTest, Single) auto eval = poly.evaluate_mle(u); // Collect multilinear polynomials evaluations, and commitments for input to prover/verifier - std::vector multilinear_evaluations = { eval }; + std::vector multilinear_evaluations_unshifted = { eval }; + std::vector multilinear_evaluations_shifted = {}; std::vector> multilinear_polynomials = { poly.share() }; std::vector> multilinear_polynomials_to_be_shifted = {}; - std::vector multilinear_commitments = { commitment }; - std::vector multilinear_commitments_to_be_shifted = {}; + std::vector multilinear_commitments = { commitment }; + std::vector multilinear_commitments_to_be_shifted = {}; this->execute_gemini_and_verify_claims(u, - multilinear_evaluations, + RefVector(multilinear_evaluations_unshifted), + RefVector(multilinear_evaluations_shifted), RefVector(multilinear_polynomials), RefVector(multilinear_polynomials_to_be_shifted), RefVector(multilinear_commitments), @@ -89,7 +145,7 @@ TYPED_TEST(GeminiTest, Single) TYPED_TEST(GeminiTest, SingleShift) { using Fr = typename TypeParam::ScalarField; - using GroupElement = typename TypeParam::Element; + using Commitment = typename TypeParam::AffineElement; const size_t n = 16; const size_t log_n = 4; @@ -103,14 +159,16 @@ TYPED_TEST(GeminiTest, SingleShift) auto eval_shift = poly.evaluate_mle(u, true); // Collect multilinear polynomials evaluations, and commitments for input to prover/verifier - std::vector multilinear_evaluations = { eval_shift }; + std::vector multilinear_evaluations_unshifted = {}; + std::vector multilinear_evaluations_shifted = { eval_shift }; std::vector> multilinear_polynomials = {}; std::vector> multilinear_polynomials_to_be_shifted = { poly.share() }; - std::vector multilinear_commitments = {}; - std::vector multilinear_commitments_to_be_shifted = { commitment }; + std::vector multilinear_commitments = {}; + std::vector multilinear_commitments_to_be_shifted = { commitment }; this->execute_gemini_and_verify_claims(u, - multilinear_evaluations, + RefVector(multilinear_evaluations_unshifted), + RefVector(multilinear_evaluations_shifted), RefVector(multilinear_polynomials), RefVector(multilinear_polynomials_to_be_shifted), RefVector(multilinear_commitments), @@ -120,7 +178,7 @@ TYPED_TEST(GeminiTest, SingleShift) TYPED_TEST(GeminiTest, Double) { using Fr = typename TypeParam::ScalarField; - using GroupElement = typename TypeParam::Element; + using Commitment = typename TypeParam::AffineElement; const size_t n = 16; const size_t log_n = 4; @@ -137,14 +195,16 @@ TYPED_TEST(GeminiTest, Double) auto eval2 = poly2.evaluate_mle(u); // Collect multilinear polynomials evaluations, and commitments for input to prover/verifier - std::vector multilinear_evaluations = { eval1, eval2 }; + std::vector multilinear_evaluations_unshifted = { eval1, eval2 }; + std::vector multilinear_evaluations_shifted = {}; std::vector> multilinear_polynomials = { poly1.share(), poly2.share() }; std::vector> multilinear_polynomials_to_be_shifted = {}; - std::vector multilinear_commitments = { commitment1, commitment2 }; - std::vector multilinear_commitments_to_be_shifted = {}; + std::vector multilinear_commitments = { commitment1, commitment2 }; + std::vector multilinear_commitments_to_be_shifted = {}; this->execute_gemini_and_verify_claims(u, - multilinear_evaluations, + RefVector(multilinear_evaluations_unshifted), + RefVector(multilinear_evaluations_shifted), RefVector(multilinear_polynomials), RefVector(multilinear_polynomials_to_be_shifted), RefVector(multilinear_commitments), @@ -154,7 +214,7 @@ TYPED_TEST(GeminiTest, Double) TYPED_TEST(GeminiTest, DoubleWithShift) { using Fr = typename TypeParam::ScalarField; - using GroupElement = typename TypeParam::Element; + using Commitment = typename TypeParam::AffineElement; const size_t n = 16; const size_t log_n = 4; @@ -172,16 +232,64 @@ TYPED_TEST(GeminiTest, DoubleWithShift) auto eval2_shift = poly2.evaluate_mle(u, true); // Collect multilinear polynomials evaluations, and commitments for input to prover/verifier - std::vector multilinear_evaluations = { eval1, eval2, eval2_shift }; + std::vector multilinear_evaluations_unshifted = { eval1, eval2 }; + std::vector multilinear_evaluations_shifted = { eval2_shift }; std::vector> multilinear_polynomials = { poly1.share(), poly2.share() }; std::vector> multilinear_polynomials_to_be_shifted = { poly2.share() }; - std::vector multilinear_commitments = { commitment1, commitment2 }; - std::vector multilinear_commitments_to_be_shifted = { commitment2 }; + std::vector multilinear_commitments = { commitment1, commitment2 }; + std::vector multilinear_commitments_to_be_shifted = { commitment2 }; this->execute_gemini_and_verify_claims(u, - multilinear_evaluations, + RefVector(multilinear_evaluations_unshifted), + RefVector(multilinear_evaluations_shifted), RefVector(multilinear_polynomials), RefVector(multilinear_polynomials_to_be_shifted), RefVector(multilinear_commitments), RefVector(multilinear_commitments_to_be_shifted)); } + +TYPED_TEST(GeminiTest, DoubleWithShiftAndConcatenation) +{ + using Fr = typename TypeParam::ScalarField; + using Commitment = typename TypeParam::AffineElement; + using Polynomial = bb::Polynomial; + + const size_t n = 16; + const size_t log_n = 4; + + auto u = this->random_evaluation_point(log_n); + + auto poly1 = Polynomial::random(n); + auto poly2 = Polynomial::random(n, 1); // make 'shiftable' + + auto commitment1 = this->commit(poly1); + auto commitment2 = this->commit(poly2); + + auto eval1 = poly1.evaluate_mle(u); + auto eval2 = poly2.evaluate_mle(u); + auto eval2_shift = poly2.evaluate_mle(u, true); + + // Collect multilinear polynomials evaluations, and commitments for input to prover/verifier + std::vector multilinear_evaluations_unshifted = { eval1, eval2 }; + std::vector multilinear_evaluations_shifted = { eval2_shift }; + std::vector multilinear_polynomials = { poly1.share(), poly2.share() }; + std::vector multilinear_polynomials_to_be_shifted = { poly2.share() }; + std::vector multilinear_commitments = { commitment1, commitment2 }; + std::vector multilinear_commitments_to_be_shifted = { commitment2 }; + + auto [concatenation_groups, concatenated_polynomials, c_evaluations, concatenation_groups_commitments] = + generate_concatenation_inputs(u, /*mun_concatenated=*/3, /*concatenation_index=*/2, this->ck()); + + this->execute_gemini_and_verify_claims_with_concatenation( + u, + RefVector(multilinear_evaluations_unshifted), + RefVector(multilinear_evaluations_shifted), + RefVector(multilinear_polynomials), + RefVector(multilinear_polynomials_to_be_shifted), + RefVector(multilinear_commitments), + RefVector(multilinear_commitments_to_be_shifted), + RefVector(concatenated_polynomials), + RefVector(c_evaluations), + to_vector_of_ref_vectors(concatenation_groups), + to_vector_of_ref_vectors(concatenation_groups_commitments)); +} 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 fde94fdcdb13..8ffc2d32902d 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini_impl.hpp @@ -47,31 +47,53 @@ std::vector::Claim> GeminiProver_::prove( RefSpan g_polynomials, // to-be-shifted std::span multilinear_challenge, const std::shared_ptr>& commitment_key, - const std::shared_ptr& transcript) + const std::shared_ptr& transcript, + RefSpan concatenated_polynomials, + const std::vector>& groups_to_be_concatenated) + { size_t log_n = numeric::get_msb(static_cast(circuit_size)); size_t n = 1 << log_n; Fr rho = transcript->template get_challenge("rho"); - std::vector rhos = gemini::powers_of_rho(rho, f_polynomials.size() + g_polynomials.size()); // Compute batched polynomials Polynomial batched_unshifted(n); Polynomial batched_to_be_shifted = Polynomial::shiftable(1 << log_n); - const size_t num_unshifted = f_polynomials.size(); - const size_t num_to_be_shifted = g_polynomials.size(); - for (size_t i = 0; i < num_unshifted; i++) { - Fr rho_challenge = rhos[i]; + Fr rho_challenge{ 1 }; + for (size_t i = 0; i < f_polynomials.size(); i++) { batched_unshifted.add_scaled(f_polynomials[i], rho_challenge); + rho_challenge *= rho; } - for (size_t i = 0; i < num_to_be_shifted; i++) { - Fr rho_challenge = rhos[num_unshifted + i]; + for (size_t i = 0; i < g_polynomials.size(); i++) { batched_to_be_shifted.add_scaled(g_polynomials[i], rho_challenge); + rho_challenge *= rho; + } + + size_t num_groups = groups_to_be_concatenated.size(); + size_t num_chunks_per_group = groups_to_be_concatenated.empty() ? 0 : groups_to_be_concatenated[0].size(); + + // Allocate space for the groups to be concatenated and for the concatenated polynomials + Polynomial batched_concatenated(n); + std::vector batched_group; + for (size_t i = 0; i < num_chunks_per_group; ++i) { + batched_group.push_back(Polynomial(n)); } - auto fold_polynomials = compute_fold_polynomials( - log_n, multilinear_challenge, std::move(batched_unshifted), std::move(batched_to_be_shifted)); + for (size_t i = 0; i < num_groups; ++i) { + batched_concatenated.add_scaled(concatenated_polynomials[i], rho_challenge); + for (size_t j = 0; j < num_chunks_per_group; ++j) { + batched_group[j].add_scaled(groups_to_be_concatenated[i][j], rho_challenge); + } + rho_challenge *= rho; + } + + auto fold_polynomials = compute_fold_polynomials(log_n, + multilinear_challenge, + std::move(batched_unshifted), + std::move(batched_to_be_shifted), + std::move(batched_concatenated)); for (size_t l = 0; l < CONST_PROOF_SIZE_LOG_N - 1; l++) { if (l < log_n - 1) { @@ -82,7 +104,9 @@ std::vector::Claim> GeminiProver_::prove( } } const Fr r_challenge = transcript->template get_challenge("Gemini:r"); - std::vector claims = compute_fold_polynomial_evaluations(log_n, std::move(fold_polynomials), r_challenge); + + std::vector claims = + compute_fold_polynomial_evaluations(log_n, std::move(fold_polynomials), r_challenge, std::move(batched_group)); for (size_t l = 1; l <= CONST_PROOF_SIZE_LOG_N; l++) { if (l <= log_n) { @@ -99,19 +123,19 @@ std::vector::Claim> GeminiProver_::prove( * @brief Computes d-1 fold polynomials Fold_i, i = 1, ..., d-1 * * @param mle_opening_point multilinear opening point 'u' - * @param batched_unshifted F(X) = ∑ⱼ ρʲ fⱼ(X) + * @param batched_unshifted F(X) = ∑ⱼ ρʲ fⱼ(X) . * @param batched_to_be_shifted G(X) = ∑ⱼ ρᵏ⁺ʲ gⱼ(X) + * @param batched_concatenated The sum of batched concatenated polynomial, * @return std::vector */ template std::vector::Polynomial> GeminiProver_::compute_fold_polynomials( - const size_t log_N, + const size_t num_variables, std::span mle_opening_point, Polynomial&& batched_unshifted, - Polynomial&& batched_to_be_shifted) + Polynomial&& batched_to_be_shifted, + Polynomial&& batched_concatenated) { - const size_t num_variables = log_N; - const size_t num_threads = get_num_cpus_pow2(); constexpr size_t efficient_operations_per_thread = 64; // A guess of the number of operation for which there // would be a point in sending them to a separate thread @@ -130,6 +154,10 @@ std::vector::Polynomial> GeminiProver_::com constexpr size_t offset_to_folded = 2; // Offset because of F an G // A₀(X) = F(X) + G↺(X) = F(X) + G(X)/X. Polynomial A_0 = batched_F; + + // If proving the opening for translator, add a non-zero contribution of the batched concatenation polynomials + A_0 += batched_concatenated; + A_0 += batched_G.shifted(); // Allocate everything before parallel computation @@ -196,11 +224,14 @@ std::vector::Polynomial> GeminiProver_::com */ template std::vector::Claim> GeminiProver_::compute_fold_polynomial_evaluations( - const size_t log_N, std::vector&& fold_polynomials, const Fr& r_challenge) + const size_t num_variables, + std::vector&& fold_polynomials, + const Fr& r_challenge, + std::vector&& batched_groups_to_be_concatenated) { - const size_t num_variables = log_N; Polynomial& batched_F = fold_polynomials[0]; // F(X) = ∑ⱼ ρʲ fⱼ(X) + Polynomial& batched_G = fold_polynomials[1]; // G(X) = ∑ⱼ ρᵏ⁺ʲ gⱼ(X) // Compute univariate opening queries rₗ = r^{2ˡ} for l = 0, 1, ..., m-1 @@ -224,13 +255,39 @@ std::vector::Claim> GeminiProver_::compute_ // A₀₋(X) = F(X) - G(X)/r, s.t. A₀₋(-r) = A₀(-r) A_0_neg -= tmp; + // Reconstruct the batched concatenated polynomial from the batched groups, partially evaluated at r and -r and add + // the result to A₀₊(X) and A₀₋(X). Explanation (for simplification assume a single concatenated polynomial): + // Let P be the concatenated polynomial formed from group G = {p₀, p₁, p₂, p₃} then + // P(x) = p₀(x)+ xˢ p₁(x) + x²ˢ p₂(x) + x³ˢp₃(x) where s is the mini_circuit_size i.e. the number of non-zero values + // in the polynomials part of G. Then P_r(x) = p₀(x) + rˢ p₁(x) + r²ˢ p₂(x) + r³ˢp₃(x) is the + // partial evaluation of P(x) at a value r. We follow this technique rather than simply adding the contribution of P + // to A₀₊(X) an A₀₋(X) because, on the verifier side, when constructing the commitments [A₀₊] an [A₀₋], this + // enables us to reconstruct [P_r] from [p₀], [p₁], [p₂], [p₃], hence removing the need for the prover to commit to + // P + if (!batched_groups_to_be_concatenated.empty()) { + // The "real" size of polynomials in concatenation groups (i.e. the number of non-zero values) + const size_t mini_circuit_size = (1 << num_variables) / batched_groups_to_be_concatenated.size(); + Fr current_r_shift_pos = Fr(1); + Fr current_r_shift_neg = Fr(1); + + const Fr r_pow_minicircuit = r_challenge.pow(mini_circuit_size); + const Fr r_neg_pow_minicircuit = (-r_challenge).pow(mini_circuit_size); + for (size_t i = 0; i < batched_groups_to_be_concatenated.size(); i++) { + // Reconstruct the batched concationation polynomial partially evaluated at r and -r from the polynomials + // in the batched concatenation group. + A_0_pos.add_scaled(batched_groups_to_be_concatenated[i], current_r_shift_pos); + A_0_neg.add_scaled(batched_groups_to_be_concatenated[i], current_r_shift_neg); + current_r_shift_pos *= r_pow_minicircuit; + current_r_shift_neg *= r_neg_pow_minicircuit; + } + } + std::vector opening_claims; opening_claims.reserve(num_variables + 1); // Compute first opening pair {r, A₀(r)} Fr evaluation = fold_polynomials[0].evaluate(r_challenge); - opening_claims.emplace_back( - Claim{ fold_polynomials[0], { r_challenge, fold_polynomials[0].evaluate(r_challenge) } }); + opening_claims.emplace_back(Claim{ fold_polynomials[0], { r_challenge, evaluation } }); // Compute the remaining m opening pairs {−r^{2ˡ}, Aₗ(−r^{2ˡ})}, l = 0, ..., m-1. for (size_t l = 0; l < num_variables; ++l) { evaluation = fold_polynomials[l + 1].evaluate(-r_squares[l]); 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 7db3ea01e3b0..07d22793c0f6 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -1,13 +1,7 @@ -#include "../gemini/gemini.hpp" -#include "../shplonk/shplemini.hpp" -#include "../shplonk/shplonk.hpp" #include "./mock_transcript.hpp" #include "barretenberg/commitment_schemes/commitment_key.test.hpp" -#include "barretenberg/common/mem.hpp" -#include "barretenberg/ecc/curves/bn254/fq12.hpp" -#include "barretenberg/ecc/curves/types.hpp" -#include "barretenberg/polynomials/polynomial_arithmetic.hpp" +#include "barretenberg/commitment_schemes/shplonk/shplemini.hpp" using namespace bb; namespace { @@ -55,7 +49,7 @@ TEST_F(IPATest, OpenZeroPolynomial) constexpr size_t n = 4; Polynomial poly(n); // Commit to a zero polynomial - GroupElement commitment = this->commit(poly); + Commitment commitment = this->commit(poly); EXPECT_TRUE(commitment.is_point_at_infinity()); auto [x, eval] = this->random_eval(poly); @@ -247,8 +241,8 @@ TEST_F(IPATest, GeminiShplonkIPAWithShift) auto poly1 = Polynomial::random(n); auto poly2 = Polynomial::random(n, /*shiftable*/ 1); - GroupElement commitment1 = this->commit(poly1); - GroupElement commitment2 = this->commit(poly2); + Commitment commitment1 = this->commit(poly1); + Commitment commitment2 = this->commit(poly2); auto eval1 = poly1.evaluate_mle(mle_opening_point); auto eval2 = poly2.evaluate_mle(mle_opening_point); @@ -272,7 +266,8 @@ TEST_F(IPATest, GeminiShplonkIPAWithShift) auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript); auto gemini_verifier_claim = GeminiVerifier::reduce_verification(mle_opening_point, - multilinear_evaluations, + RefArray{ eval1, eval2 }, + RefArray{ eval2_shift }, RefArray{ commitment1, commitment2 }, RefArray{ commitment2 }, verifier_transcript); 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 e23a6d90c774..4b951685549b 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp @@ -1,11 +1,9 @@ #include "kzg.hpp" -#include "../gemini/gemini.hpp" -#include "../shplonk/shplemini.hpp" -#include "../shplonk/shplonk.hpp" - #include "../commitment_key.test.hpp" #include "barretenberg/commitment_schemes/claim.hpp" +#include "barretenberg/commitment_schemes/shplonk/shplemini.hpp" +#include "barretenberg/commitment_schemes/utils/test_utils.hpp" namespace bb { @@ -13,7 +11,6 @@ template class KZGTest : public CommitmentTest { public: using Fr = typename Curve::ScalarField; using Commitment = typename Curve::AffineElement; - using GroupElement = typename Curve::Element; using Polynomial = bb::Polynomial; }; @@ -58,7 +55,7 @@ TYPED_TEST(KZGTest, GeminiShplonkKzgWithShift) using GeminiVerifier = GeminiVerifier_; using KZG = KZG; using Fr = typename TypeParam::ScalarField; - using GroupElement = typename TypeParam::Element; + using Commitment = typename TypeParam::AffineElement; using Polynomial = typename bb::Polynomial; const size_t n = 16; @@ -70,8 +67,8 @@ TYPED_TEST(KZGTest, GeminiShplonkKzgWithShift) auto poly1 = Polynomial::random(n); auto poly2 = Polynomial::random(n, 1); // make 'shiftable' - GroupElement commitment1 = this->commit(poly1); - GroupElement commitment2 = this->commit(poly2); + Commitment commitment1 = this->commit(poly1); + Commitment commitment2 = this->commit(poly2); auto eval1 = poly1.evaluate_mle(mle_opening_point); auto eval2 = poly2.evaluate_mle(mle_opening_point); @@ -106,7 +103,8 @@ TYPED_TEST(KZGTest, GeminiShplonkKzgWithShift) // 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 auto gemini_verifier_claim = GeminiVerifier::reduce_verification(mle_opening_point, - multilinear_evaluations, + RefArray{ eval1, eval2 }, + RefArray{ eval2_shift }, RefArray{ commitment1, commitment2 }, RefArray{ commitment2 }, verifier_transcript); @@ -204,4 +202,92 @@ TYPED_TEST(KZGTest, ShpleminiKzgWithShift) EXPECT_EQ(this->vk()->pairing_check(pairing_points[0], pairing_points[1]), true); } +TYPED_TEST(KZGTest, ShpleminiKzgWithShiftAndConcatenation) +{ + using ShplonkProver = ShplonkProver_; + using GeminiProver = GeminiProver_; + using ShpleminiVerifier = ShpleminiVerifier_; + using KZG = KZG; + using Fr = typename TypeParam::ScalarField; + using Commitment = typename TypeParam::AffineElement; + using Polynomial = typename bb::Polynomial; + + const size_t n = 16; + const size_t log_n = 4; + // Generate multilinear polynomials, their commitments (genuine and mocked) and evaluations (genuine) at a random + // point. + auto mle_opening_point = this->random_evaluation_point(log_n); // sometimes denoted 'u' + auto poly1 = Polynomial::random(n, 1); + auto poly2 = Polynomial::random(n); + auto poly3 = Polynomial::random(n, 1); + auto poly4 = Polynomial::random(n); + + Commitment commitment1 = this->commit(poly1); + Commitment commitment2 = this->commit(poly2); + Commitment commitment3 = this->commit(poly3); + Commitment commitment4 = this->commit(poly4); + std::vector unshifted_commitments = { commitment1, commitment2, commitment3, commitment4 }; + std::vector shifted_commitments = { commitment1, commitment3 }; + auto eval1 = poly1.evaluate_mle(mle_opening_point); + auto eval2 = poly2.evaluate_mle(mle_opening_point); + auto eval3 = poly3.evaluate_mle(mle_opening_point); + auto eval4 = poly4.evaluate_mle(mle_opening_point); + auto eval1_shift = poly1.evaluate_mle(mle_opening_point, true); + auto eval3_shift = poly3.evaluate_mle(mle_opening_point, true); + + // Collect multilinear evaluations for input to prover + std::vector multilinear_evaluations = { eval1, eval2, eval3, eval1_shift, eval3_shift }; + + auto [concatenation_groups, concatenated_polynomials, c_evaluations, concatenation_groups_commitments] = + generate_concatenation_inputs( + mle_opening_point, /*num_concatenated=*/3, /*concatenation_index=*/2, this->ck()); + + auto prover_transcript = NativeTranscript::prover_init_empty(); + + // Run the full prover PCS protocol: + + // Compute: + // - (d+1) opening pairs: {r, \hat{a}_0}, {-r^{2^i}, a_i}, i = 0, ..., d-1 + // - (d+1) Fold polynomials Fold_{r}^(0), Fold_{-r}^(0), and Fold^(i), i = 0, ..., d-1 + auto prover_opening_claims = GeminiProver::prove(n, + RefArray{ poly1, poly2, poly3, poly4 }, + RefArray{ poly1, poly3 }, + mle_opening_point, + this->ck(), + prover_transcript, + RefVector(concatenated_polynomials), + to_vector_of_ref_vectors(concatenation_groups)); + + // Shplonk prover output: + // - opening pair: (z_challenge, 0) + // - witness: polynomial Q - Q_z + const auto opening_claim = ShplonkProver::prove(this->ck(), prover_opening_claims, prover_transcript); + + // KZG prover: + // - Adds commitment [W] to transcript + KZG::compute_opening_proof(this->ck(), opening_claim, prover_transcript); + + // Run the full verifier PCS protocol with genuine opening claims (genuine commitment, genuine evaluation) + + auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript); + + // 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(n, + RefVector(unshifted_commitments), + RefVector(shifted_commitments), + RefArray{ eval1, eval2, eval3, eval4 }, + RefArray{ eval1_shift, eval3_shift }, + mle_opening_point, + this->vk()->get_g1_identity(), + verifier_transcript, + to_vector_of_ref_vectors(concatenation_groups_commitments), + RefVector(c_evaluations)); + const auto pairing_points = KZG::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) + + EXPECT_EQ(this->vk()->pairing_check(pairing_points[0], pairing_points[1]), true); +} + } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp index 1d5aae300ce3..2f247d8d1eee 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp @@ -26,10 +26,18 @@ template class ShpleminiProver_ { RefSpan g_polynomials, std::span multilinear_challenge, const std::shared_ptr>& commitment_key, - const std::shared_ptr& transcript) + const std::shared_ptr& transcript, + RefSpan concatenated_polynomials = {}, + const std::vector>& groups_to_be_concatenated = {}) { - std::vector opening_claims = GeminiProver::prove( - circuit_size, f_polynomials, g_polynomials, multilinear_challenge, commitment_key, transcript); + std::vector opening_claims = GeminiProver::prove(circuit_size, + f_polynomials, + g_polynomials, + multilinear_challenge, + commitment_key, + transcript, + concatenated_polynomials, + groups_to_be_concatenated); OpeningClaim batched_claim = ShplonkProver::prove(commitment_key, opening_claims, transcript); return batched_claim; @@ -99,14 +107,18 @@ template class ShpleminiVerifier_ { public: template - static BatchOpeningClaim compute_batch_opening_claim(const Fr N, - RefSpan unshifted_commitments, - RefSpan shifted_commitments, - RefSpan unshifted_evaluations, - RefSpan shifted_evaluations, - const std::vector& multivariate_challenge, - const Commitment& g1_identity, - const std::shared_ptr& transcript) + static BatchOpeningClaim compute_batch_opening_claim( + const Fr N, + RefSpan unshifted_commitments, + RefSpan shifted_commitments, + RefSpan unshifted_evaluations, + RefSpan shifted_evaluations, + const std::vector& multivariate_challenge, + const Commitment& g1_identity, + const std::shared_ptr& transcript, + const std::vector>& concatenation_group_commitments = {}, + RefSpan concatenated_evaluations = {}) + { // Extract log_circuit_size @@ -126,9 +138,10 @@ template class ShpleminiVerifier_ { GeminiVerifier::get_fold_commitments(log_circuit_size, transcript); // - Get Gemini evaluation challenge for Aᵢ, i = 0, … , d−1 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(log_circuit_size, transcript); - // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size, I think this should be CONST_PROOF_SIZE + // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size const std::vector gemini_eval_challenge_powers = gemini::powers_of_evaluation_challenge(gemini_evaluation_challenge, CONST_PROOF_SIZE_LOG_N); @@ -138,10 +151,13 @@ template class ShpleminiVerifier_ { // - Get the quotient commitment for the Shplonk batching of Gemini opening claims const auto Q_commitment = transcript->template receive_from_prover("Shplonk:Q"); - // Start populating the vector (Q, f₀, ... , fₖ₋₁, g₀, ... , gₘ₋₁, com(A₁), ... , com(Aₙ₋₁), [1]₁) + // Start populating the vector (Q, f₀, ... , fₖ₋₁, g₀, ... , gₘ₋₁, com(A₁), ... , com(Aₙ₋₁), [1]₁) where fᵢ are + // the k commitments to unshifted polynomials and gⱼ are the m commitments to shifted polynomials std::vector commitments{ Q_commitment }; + // Get Shplonk opening point z const Fr shplonk_evaluation_challenge = transcript->template get_challenge("Shplonk:z"); + // Start computing the scalar to be multiplied by [1]₁ Fr constant_term_accumulator = Fr(0); @@ -153,18 +169,46 @@ template class ShpleminiVerifier_ { } else { scalars.emplace_back(Fr(1)); } - // Compute 1/(z − r), 1/(z + r), 1/(z + r²), … , 1/(z + r²⁽ⁿ⁻¹⁾) needed for Shplonk batching + + // Compute 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); + // Compute the additional factors to be multiplied with unshifted and shifted commitments when lazily + // reconstructing thec commitment of Q_z + // i-th unshifted commitment is multiplied by −ρⁱ and the unshifted_scalar ( 1/(z−r) + ν/(z+r) ) const Fr unshifted_scalar = inverse_vanishing_evals[0] + shplonk_batching_challenge * inverse_vanishing_evals[1]; - // i-th shifted commitment is multiplied by −ρⁱ⁺ᵏ and the shifted_scalar r⁻¹ ⋅ (1/(z−r) − ν/(z+r)) + + // j-th shifted commitment is multiplied by −ρᵏ⁺ʲ⁻¹ and the shifted_scalar r⁻¹ ⋅ (1/(z−r) − ν/(z+r)) const Fr shifted_scalar = gemini_evaluation_challenge.invert() * (inverse_vanishing_evals[0] - shplonk_batching_challenge * inverse_vanishing_evals[1]); + std::vector concatenation_scalars; + if (!concatenation_group_commitments.empty()) { + const size_t concatenation_group_size = concatenation_group_commitments[0].size(); + // The "real" size of polynomials in concatenation groups (i.e. the number of non-zero values) + const size_t mini_circuit_size = (1 << log_circuit_size) / concatenation_group_size; + Fr r_shift_pos = Fr(1); + Fr r_shift_neg = Fr(1); + const Fr r_pow_minicircuit = gemini_evaluation_challenge.pow(mini_circuit_size); + const Fr r_neg_pow_minicircuit = (-gemini_evaluation_challenge).pow(mini_circuit_size); + + for (size_t i = 0; i < concatenation_group_size; ++i) { + // The l-th commitment in each concatenation group will be multiplied by -ρᵏ⁺ᵐ⁺ˡ and + // ( rˡˢ /(z−r) + ν ⋅ (-r)ˡˢ /(z+r) ) where s is the mini circuit size + concatenation_scalars.emplace_back(r_shift_pos * inverse_vanishing_evals[0] + + r_shift_neg * shplonk_batching_challenge * + inverse_vanishing_evals[1]); + + r_shift_pos *= r_pow_minicircuit; + r_shift_neg *= r_neg_pow_minicircuit; + } + } + // Place the commitments to prover polynomials in the commitments vector. Compute the evaluation of the // batched multilinear polynomial. Populate the vector of scalars for the final batch mul Fr batched_evaluation = Fr(0); @@ -177,7 +221,10 @@ template class ShpleminiVerifier_ { shifted_scalar, commitments, scalars, - batched_evaluation); + batched_evaluation, + concatenation_scalars, + concatenation_group_commitments, + concatenated_evaluations); // Place the commitments to Gemini Aᵢ to the vector of commitments, compute the contributions from // Aᵢ(−r²ⁱ) for i=1, … , n−1 to the constant term accumulator, add corresponding scalars @@ -250,22 +297,30 @@ template class ShpleminiVerifier_ { * @param shifted_commitments Commitments to shifted polynomials. * @param claimed_evaluations Claimed evaluations of the corresponding polynomials. * @param multivariate_batching_challenge Random challenge used for batching of multivariate evaluation claims. - * @param unshifted_scalar Scaling factor for unshifted polynomials. - * @param shifted_scalar Scaling factor for shifted polynomials. + * @param unshifted_scalar Scaling factor for commitments to unshifted polynomials. + * @param shifted_scalar Scaling factor for commitments to shifted polynomials. * @param commitments The vector of commitments to be populated. * @param scalars The vector of scalars to be populated. * @param batched_evaluation The evaluation of the batched multilinear polynomial. + * @param concatenated_scalars Scaling factors for the commitments to polynomials in concatenation groups, one for + * each group. + * @param concatenation_group_commitments Commitments to polynomials to be concatenated. + * @param concatenated_evaluations Evaluations of the full concatenated polynomials. */ - static void batch_multivariate_opening_claims(RefSpan unshifted_commitments, - RefSpan shifted_commitments, - RefSpan unshifted_evaluations, - RefSpan shifted_evaluations, - const Fr& multivariate_batching_challenge, - const Fr& unshifted_scalar, - const Fr& shifted_scalar, - std::vector& commitments, - std::vector& scalars, - Fr& batched_evaluation) + static void batch_multivariate_opening_claims( + RefSpan unshifted_commitments, + RefSpan shifted_commitments, + RefSpan unshifted_evaluations, + RefSpan shifted_evaluations, + const Fr& multivariate_batching_challenge, + const Fr& unshifted_scalar, + const Fr& shifted_scalar, + std::vector& commitments, + std::vector& scalars, + Fr& batched_evaluation, + std::vector concatenated_scalars = {}, + const std::vector>& concatenation_group_commitments = {}, + RefSpan concatenated_evaluations = {}) { Fr current_batching_challenge = Fr(1); for (auto [unshifted_commitment, unshifted_evaluation] : @@ -282,20 +337,40 @@ template class ShpleminiVerifier_ { for (auto [shifted_commitment, shifted_evaluation] : zip_view(shifted_commitments, shifted_evaluations)) { // Move shifted commitments to the 'commitments' vector commitments.emplace_back(std::move(shifted_commitment)); - // Compute −ρ⁽ⁱ⁺ᵏ⁾ ⋅ r⁻¹ ⋅ (1/(z−r) − ν/(z+r)) and place into 'scalars' + // Compute −ρ⁽ᵏ⁺ʲ⁾ ⋅ r⁻¹ ⋅ (1/(z−r) − ν/(z+r)) and place into 'scalars' scalars.emplace_back(-shifted_scalar * current_batching_challenge); - // Accumulate the evaluation of ∑ ρ⁽ⁱ⁺ᵏ⁾ ⋅ f_shift, i at the sumcheck challenge + // Accumulate the evaluation of ∑ ρ⁽ᵏ⁺ʲ⁾ ⋅ f_shift at the sumcheck challenge batched_evaluation += shifted_evaluation * current_batching_challenge; - // Update the batching challenge + // Update the batching challenge ρ current_batching_challenge *= multivariate_batching_challenge; } + + // If we are performing an opening verification for the translator, add the contributions from the concatenation + // commitments and evaluations to the result + ASSERT(concatenated_evaluations.size() == concatenation_group_commitments.size()); + if (!concatenation_group_commitments.empty()) { + size_t concatenation_group_size = concatenation_group_commitments[0].size(); + size_t group_idx = 0; + for (auto concatenation_group_commitment : concatenation_group_commitments) { + for (size_t i = 0; i < concatenation_group_size; ++i) { + commitments.emplace_back(std::move(concatenation_group_commitment[i])); + scalars.emplace_back(-current_batching_challenge * concatenated_scalars[i]); + } + // Accumulate the batched evaluations of concatenated polynomials + batched_evaluation += concatenated_evaluations[group_idx] * current_batching_challenge; + // Update the batching challenge ρ + current_batching_challenge *= multivariate_batching_challenge; + group_idx++; + } + } } + /** - * @brief Populates the 'commitments' and 'scalars' vectors with the commitments to Gemini fold polynomials \f$ A_i - * \f$. + * @brief Populates the 'commitments' and 'scalars' vectors with the commitments to Gemini fold polynomials \f$ + * A_i \f$. * - * @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 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: * * 1. Moves the vector * \f[ diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/test_utils.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/test_utils.hpp new file mode 100644 index 000000000000..edc227037fd0 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/utils/test_utils.hpp @@ -0,0 +1,76 @@ +#pragma once +#include "barretenberg/commitment_schemes/commitment_key.hpp" +#include "barretenberg/polynomials/polynomial.hpp" +#include + +namespace bb { +/** + * @brief Generate the tuple of concatenation inputs used for testing the opening protocol when used for Translator VM. + */ +template +static std::tuple>>, + std::vector>, + std::vector, + std::vector>> +generate_concatenation_inputs(std::vector& u_challenge, + const size_t num_concatenated, + const size_t concatenation_index, + const std::shared_ptr> ck) +{ + using Fr = typename Curve::ScalarField; + using Commitment = typename Curve::AffineElement; + using Polynomial = bb::Polynomial; + + size_t N = 1 << u_challenge.size(); + size_t MINI_CIRCUIT_N = N / concatenation_index; + + // Polynomials "chunks" that are concatenated in the PCS + std::vector> concatenation_groups; + + // Concatenated polynomials + std::vector concatenated_polynomials; + + // Evaluations of concatenated polynomials + std::vector c_evaluations; + + // For each polynomial to be concatenated + for (size_t i = 0; i < num_concatenated; ++i) { + std::vector concatenation_group; + Polynomial concatenated_polynomial(N); + // For each chunk + for (size_t j = 0; j < concatenation_index; j++) { + Polynomial chunk_polynomial(N); + // Fill the chunk polynomial with random values and appropriately fill the space in + // concatenated_polynomial + for (size_t k = 0; k < MINI_CIRCUIT_N; k++) { + // Chunks should be shiftable + auto tmp = Fr(0); + if (k > 0) { + tmp = Fr::random_element(); + } + chunk_polynomial.at(k) = tmp; + concatenated_polynomial.at(j * MINI_CIRCUIT_N + k) = tmp; + } + concatenation_group.emplace_back(chunk_polynomial); + } + // Store chunks + concatenation_groups.emplace_back(concatenation_group); + // Store concatenated polynomial + concatenated_polynomials.emplace_back(concatenated_polynomial); + // Get evaluation + c_evaluations.emplace_back(concatenated_polynomial.evaluate_mle(u_challenge)); + } + + // Compute commitments of all polynomial chunks + std::vector> concatenation_groups_commitments; + for (size_t i = 0; i < num_concatenated; ++i) { + std::vector concatenation_group_commitment; + for (size_t j = 0; j < concatenation_index; j++) { + concatenation_group_commitment.emplace_back(ck->commit(concatenation_groups[i][j])); + } + concatenation_groups_commitments.emplace_back(concatenation_group_commitment); + } + + return { concatenation_groups, concatenated_polynomials, c_evaluations, concatenation_groups_commitments }; +}; +} // namespace bb \ No newline at end of file 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 9604db9eb928..fb5a030844fe 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 @@ -1,5 +1,5 @@ #include "./translator_recursive_verifier.hpp" -#include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" +#include "barretenberg/commitment_schemes/shplonk/shplemini.hpp" #include "barretenberg/common/throw_or_abort.hpp" #include "barretenberg/relations/translator_vm/translator_decomposition_relation_impl.hpp" #include "barretenberg/relations/translator_vm/translator_delta_range_constraint_relation_impl.hpp" @@ -62,7 +62,7 @@ std::array TranslatorRecursiveVerifier_; using PCS = typename Flavor::PCS; using Curve = typename Flavor::Curve; - using ZeroMorph = ::bb::ZeroMorphVerifier_; + using Shplemini = ::bb::ShpleminiVerifier_; using VerifierCommitments = typename Flavor::VerifierCommitments; using CommitmentLabels = typename Flavor::CommitmentLabels; @@ -117,17 +117,18 @@ std::array TranslatorRecursiveVerifier_ opening_claim = + Shplemini::compute_batch_opening_claim(circuit_size, + commitments.get_unshifted_without_concatenated(), + commitments.get_to_be_shifted(), + claimed_evaluations.get_unshifted_without_concatenated(), + claimed_evaluations.get_shifted(), + multivariate_challenge, + Commitment::one(builder), + transcript, + commitments.get_groups_to_be_concatenated(), + claimed_evaluations.get_concatenated()); + const auto pairing_points = PCS::reduce_verify_batch_opening_claim(opening_claim, transcript); return pairing_points; } diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_prover.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_prover.cpp index 136168282e38..a12ba9855d09 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_prover.cpp @@ -1,7 +1,7 @@ #include "translator_prover.hpp" #include "barretenberg/commitment_schemes/claim.hpp" #include "barretenberg/commitment_schemes/commitment_key.hpp" -#include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" +#include "barretenberg/commitment_schemes/shplonk/shplemini.hpp" #include "barretenberg/honk/proof_system/permutation_library.hpp" #include "barretenberg/plonk_honk_shared/library/grand_product_library.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" @@ -170,19 +170,18 @@ void TranslatorProver::execute_relation_check_rounds() void TranslatorProver::execute_pcs_rounds() { using Curve = typename Flavor::Curve; - using ZeroMorph = ZeroMorphProver_; - auto prover_opening_claim = - ZeroMorph::prove(key->circuit_size, - key->polynomials.get_unshifted_without_concatenated(), - key->polynomials.get_to_be_shifted(), - sumcheck_output.claimed_evaluations.get_unshifted_without_concatenated(), - sumcheck_output.claimed_evaluations.get_shifted(), - sumcheck_output.challenge, - key->commitment_key, - transcript, - key->polynomials.get_concatenated(), - sumcheck_output.claimed_evaluations.get_concatenated(), - key->polynomials.get_groups_to_be_concatenated()); + + using OpeningClaim = ProverOpeningClaim; + + const OpeningClaim prover_opening_claim = + ShpleminiProver_::prove(key->circuit_size, + key->polynomials.get_unshifted_without_concatenated(), + key->polynomials.get_to_be_shifted(), + sumcheck_output.challenge, + key->commitment_key, + transcript, + key->polynomials.get_concatenated(), + key->polynomials.get_groups_to_be_concatenated()); PCS::compute_opening_proof(key->commitment_key, prover_opening_claim, transcript); } diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp index f217e62bb6e5..715d799b5c2f 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp @@ -1,5 +1,5 @@ #include "./translator_verifier.hpp" -#include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" +#include "barretenberg/commitment_schemes/shplonk/shplemini.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" #include "barretenberg/transcript/transcript.hpp" @@ -55,7 +55,7 @@ bool TranslatorVerifier::verify_proof(const HonkProof& proof) { using Curve = typename Flavor::Curve; using PCS = typename Flavor::PCS; - using ZeroMorph = ::bb::ZeroMorphVerifier_; + using Shplemini = ShpleminiVerifier_; batching_challenge_v = transcript->template get_challenge("Translation:batching_challenge"); @@ -113,17 +113,18 @@ bool TranslatorVerifier::verify_proof(const HonkProof& proof) // Execute ZeroMorph rounds. See https://hackmd.io/dlf9xEwhTQyE3hiGbq4FsA?view for a complete description ofthe // unrolled protocol. - auto opening_claim = ZeroMorph::verify(circuit_size, - commitments.get_unshifted_without_concatenated(), - commitments.get_to_be_shifted(), - claimed_evaluations.get_unshifted_without_concatenated(), - claimed_evaluations.get_shifted(), - multivariate_challenge, - Commitment::one(), - transcript, - commitments.get_groups_to_be_concatenated(), - claimed_evaluations.get_concatenated()); - auto pairing_points = PCS::reduce_verify(opening_claim, transcript); + const BatchOpeningClaim opening_claim = + Shplemini::compute_batch_opening_claim(circuit_size, + commitments.get_unshifted_without_concatenated(), + commitments.get_to_be_shifted(), + claimed_evaluations.get_unshifted_without_concatenated(), + claimed_evaluations.get_shifted(), + multivariate_challenge, + Commitment::one(), + transcript, + commitments.get_groups_to_be_concatenated(), + claimed_evaluations.get_concatenated()); + const auto pairing_points = PCS::reduce_verify_batch_opening_claim(opening_claim, transcript); auto verified = key->pcs_verification_key->pairing_check(pairing_points[0], pairing_points[1]);