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 4b951685549..813f612ff60 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp @@ -41,6 +41,42 @@ TYPED_TEST(KZGTest, single) EXPECT_EQ(this->vk()->pairing_check(pairing_points[0], pairing_points[1]), true); } +/** + * @brief Test opening proof of a polynomial given by its evaluations at \f$ i = 0, \ldots, n \f$. Should only be used + * for small values of \f$ n \f$. + * + */ +TYPED_TEST(KZGTest, SingleInLagrangeBasis) +{ + const size_t n = 4; + + using KZG = KZG; + using Fr = typename TypeParam::ScalarField; + + // create a random univariate (coefficients are in Lagrange basis) + auto witness = bb::Univariate::get_random(); + // define the interpolation domain + std::array eval_points = { Fr(0), Fr(1), Fr(2), Fr(3) }; + // compute the monomial coefficients + Polynomial witness_polynomial(std::span(eval_points), std::span(witness), n); + // commit to the polynomial in the monomial form + g1::element commitment = this->commit(witness_polynomial); + + auto challenge = Fr::random_element(); + // evaluate the original univariate + auto evaluation = witness.evaluate(challenge); + auto opening_pair = OpeningPair{ challenge, evaluation }; + auto opening_claim = OpeningClaim{ opening_pair, commitment }; + + auto prover_transcript = NativeTranscript::prover_init_empty(); + + KZG::compute_opening_proof(this->ck(), { witness_polynomial, opening_pair }, prover_transcript); + + auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript); + auto pairing_points = KZG::reduce_verify(opening_claim, verifier_transcript); + + EXPECT_EQ(this->vk()->pairing_check(pairing_points[0], pairing_points[1]), true); +} /** * @brief Test full PCS protocol: Gemini, Shplonk, KZG and pairing check * @details Demonstrates the full PCS protocol as it is used in the construction and verification diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp index 2f247d8d1ee..59688cdf2e1 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp @@ -20,7 +20,7 @@ template class ShpleminiProver_ { using ShplonkProver = ShplonkProver_; using GeminiProver = GeminiProver_; - template + template static OpeningClaim prove(const FF circuit_size, RefSpan f_polynomials, RefSpan g_polynomials, @@ -28,7 +28,9 @@ template class ShpleminiProver_ { const std::shared_ptr>& commitment_key, const std::shared_ptr& transcript, RefSpan concatenated_polynomials = {}, - const std::vector>& groups_to_be_concatenated = {}) + const std::vector>& groups_to_be_concatenated = {}, + const std::vector>& libra_univariates = {}, + const std::vector& libra_evaluations = {}) { std::vector opening_claims = GeminiProver::prove(circuit_size, f_polynomials, @@ -38,8 +40,19 @@ template class ShpleminiProver_ { transcript, concatenated_polynomials, groups_to_be_concatenated); - - OpeningClaim batched_claim = ShplonkProver::prove(commitment_key, opening_claims, transcript); + // Create opening claims for Libra masking univariates + std::vector libra_opening_claims; + size_t idx = 0; + for (auto [libra_univariate, libra_evaluation] : zip_view(libra_univariates, libra_evaluations)) { + OpeningClaim new_claim; + new_claim.polynomial = Polynomial(libra_univariate); + new_claim.opening_pair.challenge = multilinear_challenge[idx]; + new_claim.opening_pair.evaluation = libra_evaluation; + libra_opening_claims.push_back(new_claim); + idx++; + } + OpeningClaim batched_claim = + ShplonkProver::prove(commitment_key, opening_claims, transcript, libra_opening_claims); return batched_claim; }; }; @@ -117,7 +130,9 @@ template class ShpleminiVerifier_ { const Commitment& g1_identity, const std::shared_ptr& transcript, const std::vector>& concatenation_group_commitments = {}, - RefSpan concatenated_evaluations = {}) + RefSpan concatenated_evaluations = {}, + RefSpan libra_univariate_commitments = {}, + const std::vector& libra_univariate_evaluations = {}) { @@ -254,6 +269,18 @@ template class ShpleminiVerifier_ { commitments.emplace_back(g1_identity); scalars.emplace_back(constant_term_accumulator); + // For ZK flavors, the sumcheck output contains the evaluations of Libra univariates that submitted to the + // ShpleminiVerifier, otherwise this argument is set to be empty + if (!libra_univariate_evaluations.empty()) { + add_zk_data(commitments, + scalars, + libra_univariate_commitments, + libra_univariate_evaluations, + multivariate_challenge, + shplonk_batching_challenge, + shplonk_evaluation_challenge); + } + return { commitments, scalars, shplonk_evaluation_challenge }; }; /** @@ -439,5 +466,66 @@ template class ShpleminiVerifier_ { commitments.emplace_back(std::move(fold_commitments[j])); } } + + /** + * @brief Add the opening data corresponding to Libra masking univariates to the batched opening claim + * + * @details After verifying ZK Sumcheck, the verifier has to validate the claims about the evaluations of Libra + * univariates used to mask Sumcheck round univariates. To minimize the overhead of such openings, we continue the + * Shplonk batching started in Gemini, i.e. we add new claims multiplied by a suitable power of the Shplonk batching + * challenge and re-use the evaluation challenge sampled to prove the evaluations of Gemini polynomials. + * + * @param commitments + * @param scalars + * @param libra_univariate_commitments + * @param libra_univariate_evaluations + * @param multivariate_challenge + * @param shplonk_batching_challenge + * @param shplonk_evaluation_challenge + */ + static void add_zk_data(std::vector& commitments, + std::vector& scalars, + RefSpan libra_univariate_commitments, + const std::vector& libra_univariate_evaluations, + const std::vector& multivariate_challenge, + const Fr& shplonk_batching_challenge, + 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; + } + + // need to keep track of the contribution to the constant term + Fr& constant_term = scalars.back(); + // compute shplonk denominators and batch invert them + std::vector denominators; + size_t num_libra_univariates = libra_univariate_commitments.size(); + + // compute Shplonk denominators and invert them + for (size_t idx = 0; idx < num_libra_univariates; idx++) { + if constexpr (Curve::is_stdlib_type) { + denominators.push_back(Fr(1) / (shplonk_evaluation_challenge - multivariate_challenge[idx])); + } else { + denominators.push_back(shplonk_evaluation_challenge - multivariate_challenge[idx]); + } + }; + if constexpr (!Curve::is_stdlib_type) { + Fr::batch_invert(denominators); + } + // add Libra commitments to the vector of commitments; compute corresponding scalars and the correction to the + // constant term + for (const auto [libra_univariate_commitment, denominator, libra_univariate_evaluation] : + zip_view(libra_univariate_commitments, denominators, libra_univariate_evaluations)) { + commitments.push_back(std::move(libra_univariate_commitment)); + Fr scaling_factor = denominator * shplonk_challenge_power; + scalars.push_back((-scaling_factor)); + shplonk_challenge_power *= shplonk_batching_challenge; + // update the constant term of the Shplonk batched claim + constant_term += scaling_factor * libra_univariate_evaluation; + } + } }; } // namespace bb \ No newline at end of file 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 55739360179..9914b2e092c 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.test.cpp @@ -6,6 +6,7 @@ #include "../shplonk/shplonk.hpp" #include "../utils/batch_mul_native.hpp" #include "barretenberg/commitment_schemes/claim.hpp" +#include "barretenberg/commitment_schemes/ipa/ipa.hpp" #include "barretenberg/ecc/curves/bn254/g1.hpp" #include @@ -221,4 +222,123 @@ TYPED_TEST(ShpleminiTest, CorrectnessOfGeminiClaimBatching) EXPECT_EQ(shplemini_result, expected_result); } + +/** + * @brief Libra masking univariates are used in sumcheck to prevent the leakage of witness data through the evaluations + * of round univariates. Here we test the opening of log_n Libra masking univariates batched with the opening of several + * prover polynomials and their shifts. + * + */ +TYPED_TEST(ShpleminiTest, ShpleminiWithMaskingLibraUnivariates) +{ + using ShpleminiProver = ShpleminiProver_; + using ShpleminiVerifier = ShpleminiVerifier_; + using KZG = KZG; + using IPA = IPA; + 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; + // In practice, the length of Libra univariates is equal to FLAVOR::BATCHED_RELATION_PARTIAL_LENGTH + const size_t LIBRA_UNIVARIATE_LENGTH = 12; + + std::array interpolation_domain; + for (size_t idx = 0; idx < LIBRA_UNIVARIATE_LENGTH; idx++) { + interpolation_domain[idx] = Fr(idx); + } + // 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); + auto poly2 = Polynomial::random(n, 1); + auto poly3 = Polynomial::random(n, 1); + auto poly4 = Polynomial::random(n); + + std::vector> libra_univariates; + std::vector libra_commitments; + std::vector libra_evaluations; + for (size_t idx = 0; idx < log_n; idx++) { + // generate random polynomial + Polynomial libra_polynomial = Polynomial::random(LIBRA_UNIVARIATE_LENGTH); + // create a univariate with the same coefficients (to store an array instead of a vector) + bb::Univariate libra_univariate; + for (size_t i = 0; i < LIBRA_UNIVARIATE_LENGTH; i++) { + libra_univariate.value_at(i) = libra_polynomial[i]; + } + libra_univariates.push_back(libra_univariate); + + // commit to libra polynomial and populate the vector of libra commitments + Commitment libra_commitment = this->commit(libra_polynomial); + libra_commitments.push_back(libra_commitment); + + // evaluate current libra univariate at the corresponding challenge and store the value in libra evaluations + libra_evaluations.push_back(libra_polynomial.evaluate(mle_opening_point[idx])); + } + + 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 = { commitment2, 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 eval2_shift = poly2.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, eval4, eval2_shift, eval3_shift }; + + auto prover_transcript = NativeTranscript::prover_init_empty(); + + // Run the full prover PCS protocol: + auto opening_claim = ShpleminiProver::prove(Fr{ n }, + RefArray{ poly1, poly2, poly3, poly4 }, + RefArray{ poly2, poly3 }, + mle_opening_point, + this->ck(), + prover_transcript, + /* concatenated_polynomials = */ {}, + /* groups_to_be_concatenated = */ {}, + libra_univariates, + libra_evaluations); + if constexpr (std::is_same_v) { + IPA::compute_opening_proof(this->ck(), opening_claim, prover_transcript); + } else { + 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 + auto batch_opening_claim = + ShpleminiVerifier::compute_batch_opening_claim(n, + RefVector(unshifted_commitments), + RefVector(shifted_commitments), + RefArray{ eval1, eval2, eval3, eval4 }, + RefArray{ eval2_shift, eval3_shift }, + mle_opening_point, + this->vk()->get_g1_identity(), + verifier_transcript, + /* concatenation_group_commitments = */ {}, + /* concatenated_evaluations = */ {}, + RefVector(libra_commitments), + libra_evaluations); + + if constexpr (std::is_same_v) { + auto result = IPA::reduce_verify_batch_opening_claim(batch_opening_claim, this->vk(), verifier_transcript); + EXPECT_EQ(result, true); + } else { + 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/shplonk.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp index 1322fb34c3d..527e1d0b73e 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp @@ -38,7 +38,9 @@ template class ShplonkProver_ { * @param nu batching challenge * @return Polynomial Q(X) */ - static Polynomial compute_batched_quotient(std::span> opening_claims, const Fr& nu) + static Polynomial compute_batched_quotient(std::span> opening_claims, + const Fr& nu, + std::span> libra_opening_claims) { // Find n, the maximum size of all polynomials fⱼ(X) size_t max_poly_size{ 0 }; @@ -51,7 +53,22 @@ template class ShplonkProver_ { Fr current_nu = Fr::one(); for (const auto& claim : opening_claims) { + // Compute individual claim quotient tmp = ( fⱼ(X) − vⱼ) / ( X − xⱼ ) + tmp = claim.polynomial; + tmp.at(0) = tmp[0] - claim.opening_pair.evaluation; + 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; + } + + // 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; + }; + for (const auto& claim : libra_opening_claims) { // Compute individual claim quotient tmp = ( fⱼ(X) − vⱼ) / ( X − xⱼ ) tmp = claim.polynomial; tmp.at(0) = tmp[0] - claim.opening_pair.evaluation; @@ -61,7 +78,6 @@ template class ShplonkProver_ { Q.add_scaled(tmp, current_nu); current_nu *= nu; } - // Return batched quotient polynomial Q(X) return Q; }; @@ -80,16 +96,22 @@ template class ShplonkProver_ { std::span> opening_claims, Polynomial& batched_quotient_Q, const Fr& nu_challenge, - const Fr& z_challenge) + const Fr& z_challenge, + std::span> libra_opening_claims = {}) { const size_t num_opening_claims = opening_claims.size(); - // {ẑⱼ(r)}ⱼ , where ẑⱼ(r) = 1/zⱼ(r) = 1/(r - xⱼ) + // {ẑⱼ(z)}ⱼ , where ẑⱼ(r) = 1/zⱼ(z) = 1/(z - xⱼ) std::vector inverse_vanishing_evals; inverse_vanishing_evals.reserve(num_opening_claims); for (const auto& claim : opening_claims) { inverse_vanishing_evals.emplace_back(z_challenge - claim.opening_pair.challenge); } + + // Add the terms (z - uₖ) for k = 0, …, d−1 where d is the number of rounds in Sumcheck + for (const auto& claim : libra_opening_claims) { + inverse_vanishing_evals.emplace_back(z_challenge - claim.opening_pair.challenge); + } Fr::batch_invert(inverse_vanishing_evals); // G(X) = Q(X) - Q_z(X) = Q(X) - ∑ⱼ νʲ ⋅ ( fⱼ(X) − vⱼ) / ( z − xⱼ ), @@ -100,6 +122,7 @@ template class ShplonkProver_ { Fr current_nu = Fr::one(); Polynomial tmp(G.size()); size_t idx = 0; + for (const auto& claim : opening_claims) { // tmp = νʲ ⋅ ( fⱼ(X) − vⱼ) / ( z − xⱼ ) tmp = claim.polynomial; @@ -113,6 +136,22 @@ template class ShplonkProver_ { idx++; } + // 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; + }; + + for (const auto& claim : libra_opening_claims) { + // Compute individual claim quotient tmp = ( fⱼ(X) − vⱼ) / ( X − xⱼ ) + tmp = claim.polynomial; + tmp.at(0) = tmp[0] - claim.opening_pair.evaluation; + Fr scaling_factor = current_nu * inverse_vanishing_evals[idx]; // = νʲ / (z − xⱼ ) + + // Add the claim quotient to the batched quotient polynomial + G.add_scaled(tmp, -scaling_factor); + idx++; + current_nu *= nu_challenge; + } // 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() } }; }; @@ -129,14 +168,16 @@ template class ShplonkProver_ { template static ProverOpeningClaim prove(const std::shared_ptr>& commitment_key, std::span> opening_claims, - const std::shared_ptr& transcript) + const std::shared_ptr& transcript, + std::span> libra_opening_claims = {}) { const Fr nu = transcript->template get_challenge("Shplonk:nu"); - auto batched_quotient = compute_batched_quotient(opening_claims, nu); + auto batched_quotient = compute_batched_quotient(opening_claims, nu, libra_opening_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); + return compute_partially_evaluated_batched_quotient( + opening_claims, batched_quotient, nu, z, libra_opening_claims); } }; diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp index e20938d5ff9..a9db4d2e895 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp @@ -68,7 +68,7 @@ class ECCVMFlavor { using Relations = Relations_; using LookupRelation = ECCVMLookupRelation; - static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); + static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation @@ -79,8 +79,7 @@ class ECCVMFlavor { // Instantiate the BarycentricData needed to extend each Relation Univariate // define the containers for storing the contributions from each relation in Sumcheck - using SumcheckTupleOfTuplesOfUnivariates = - decltype(create_sumcheck_tuple_of_tuples_of_univariates()); + using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); @@ -936,6 +935,7 @@ class ECCVMFlavor { Commitment transcript_msm_count_at_transition_inverse_comm; Commitment z_perm_comm; Commitment lookup_inverses_comm; + std::vector libra_commitments; FF libra_sum; std::vector> sumcheck_univariates; std::vector libra_evaluations; @@ -1142,7 +1142,11 @@ class ECCVMFlavor { NativeTranscript::proof_data, num_frs_read); z_perm_comm = NativeTranscript::template deserialize_from_buffer(NativeTranscript::proof_data, num_frs_read); - + size_t log_circuit_size = static_cast(numeric::get_msb(circuit_size)); + for (size_t i = 0; i < log_circuit_size; i++) { + libra_commitments.emplace_back(NativeTranscript::template deserialize_from_buffer( + NativeTranscript::proof_data, num_frs_read)); + }; libra_sum = NativeTranscript::template deserialize_from_buffer(NativeTranscript::proof_data, num_frs_read); for (size_t i = 0; i < CONST_PROOF_SIZE_LOG_N; ++i) { @@ -1151,7 +1155,6 @@ class ECCVMFlavor { NativeTranscript::proof_data, num_frs_read)); } - size_t log_circuit_size = static_cast(numeric::get_msb(circuit_size)); for (size_t i = 0; i < log_circuit_size; i++) { libra_evaluations.emplace_back( NativeTranscript::template deserialize_from_buffer(NativeTranscript::proof_data, num_frs_read)); @@ -1301,6 +1304,10 @@ class ECCVMFlavor { NativeTranscript::template serialize_to_buffer(lookup_inverses_comm, NativeTranscript::proof_data); NativeTranscript::template serialize_to_buffer(z_perm_comm, NativeTranscript::proof_data); + for (size_t i = 0; i < log_circuit_size; ++i) { + NativeTranscript::template serialize_to_buffer(libra_commitments[i], NativeTranscript::proof_data); + } + NativeTranscript::template serialize_to_buffer(libra_sum, NativeTranscript::proof_data); for (size_t i = 0; i < CONST_PROOF_SIZE_LOG_N; ++i) { diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp index c7f9ff738b8..174e2f917e9 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp @@ -100,7 +100,9 @@ void ECCVMProver::execute_relation_check_rounds() for (size_t idx = 0; idx < CONST_PROOF_SIZE_LOG_N; idx++) { gate_challenges[idx] = transcript->template get_challenge("Sumcheck:gate_challenge_" + std::to_string(idx)); } - sumcheck_output = sumcheck.prove(key->polynomials, relation_parameters, alpha, gate_challenges); + zk_sumcheck_data = ZKSumcheckData(key->log_circuit_size, transcript, key); + + sumcheck_output = sumcheck.prove(key->polynomials, relation_parameters, alpha, gate_challenges, zk_sumcheck_data); } /** @@ -118,12 +120,17 @@ void ECCVMProver::execute_pcs_rounds() // Execute the Shplemini (Gemini + Shplonk) protocol to produce a univariate opening claim for the multilinear // evaluations produced by Sumcheck - const OpeningClaim multivariate_to_univariate_opening_claim = Shplemini::prove(key->circuit_size, - key->polynomials.get_unshifted(), - key->polynomials.get_to_be_shifted(), - sumcheck_output.challenge, - key->commitment_key, - transcript); + const OpeningClaim multivariate_to_univariate_opening_claim = + Shplemini::prove(key->circuit_size, + key->polynomials.get_unshifted(), + key->polynomials.get_to_be_shifted(), + sumcheck_output.challenge, + key->commitment_key, + transcript, + /* concatenated_polynomials = */ {}, + /* groups_to_be_concatenated = */ {}, + zk_sumcheck_data.libra_univariates_monomial, + sumcheck_output.claimed_libra_evaluations); // Get the challenge at which we evaluate all transcript polynomials as univariates evaluation_challenge_x = transcript->template get_challenge("Translation:evaluation_challenge_x"); diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp index 8ce5c467d0e..88b58903803 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp @@ -5,6 +5,7 @@ #include "barretenberg/plonk_honk_shared/library/grand_product_library.hpp" #include "barretenberg/relations/relation_parameters.hpp" #include "barretenberg/sumcheck/sumcheck_output.hpp" +#include "barretenberg/sumcheck/zk_sumcheck_data.hpp" #include "barretenberg/transcript/transcript.hpp" namespace bb { @@ -50,6 +51,7 @@ class ECCVMProver { std::shared_ptr key; CommitmentLabels commitment_labels; + ZKSumcheckData zk_sumcheck_data; Polynomial batched_quotient_Q; // batched quotient poly computed by Shplonk FF nu_challenge; // needed in both Shplonk rounds diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_transcript.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_transcript.test.cpp index 2a18dae18ea..382f1508a3e 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_transcript.test.cpp @@ -139,6 +139,10 @@ class ECCVMTranscriptTests : public ::testing::Test { } round++; + for (size_t i = 0; i < log_n; i++) { + std::string idx = std::to_string(i); + manifest_expected.add_entry(round, "Libra:commitment_" + idx, frs_per_G); + } manifest_expected.add_entry(round, "Libra:Sum", frs_per_Fr); // get the challenge for the ZK Sumcheck claim manifest_expected.add_challenge(round, "Libra:Challenge"); diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp index 301cca65997..707d6a60142 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp @@ -53,17 +53,23 @@ bool ECCVMVerifier::verify_proof(const HonkProof& proof) gate_challenges[idx] = transcript->template get_challenge("Sumcheck:gate_challenge_" + std::to_string(idx)); } + // Receive commitments to Libra masking polynomials + std::vector libra_commitments; + for (size_t idx = 0; idx < log_circuit_size; idx++) { + Commitment libra_commitment = + transcript->receive_from_prover("Libra:commitment_" + std::to_string(idx)); + libra_commitments.push_back(libra_commitment); + } + auto [multivariate_challenge, claimed_evaluations, libra_evaluations, sumcheck_verified] = sumcheck.verify(relation_parameters, alpha, gate_challenges); - // If Sumcheck did not verify, return false if (sumcheck_verified.has_value() && !sumcheck_verified.value()) { return false; } - // Compute the Shplemini accumulator consisting of the Shplonk evaluation and the commitments and scalars vector // produced by the unified protocol - const BatchOpeningClaim sumcheck_batch_opening_claims = + BatchOpeningClaim sumcheck_batch_opening_claims = Shplemini::compute_batch_opening_claim(circuit_size, commitments.get_unshifted(), commitments.get_to_be_shifted(), @@ -71,7 +77,11 @@ bool ECCVMVerifier::verify_proof(const HonkProof& proof) claimed_evaluations.get_shifted(), multivariate_challenge, key->pcs_verification_key->get_g1_identity(), - transcript); + transcript, + /* concatenation_group_commitments = */ {}, + /* concatenated_evaluations = */ {}, + RefVector(libra_commitments), + libra_evaluations); // Reduce the accumulator to a single opening claim const OpeningClaim multivariate_to_univariate_opening_claim = diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_bools_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_bools_relation.cpp index 6416f9c5de5..539b6e6cd6d 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_bools_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_bools_relation.cpp @@ -5,5 +5,5 @@ namespace bb { template class ECCVMBoolsRelationImpl; -DEFINE_ZK_SUMCHECK_RELATION_CLASS(ECCVMBoolsRelationImpl, ECCVMFlavor); +DEFINE_SUMCHECK_RELATION_CLASS(ECCVMBoolsRelationImpl, ECCVMFlavor); } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation.cpp index 69dcc7122e1..4afdd9c0898 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation.cpp @@ -4,5 +4,5 @@ namespace bb { template class ECCVMLookupRelationImpl; -DEFINE_ZK_SUMCHECK_RELATION_CLASS(ECCVMLookupRelationImpl, ECCVMFlavor); +DEFINE_SUMCHECK_RELATION_CLASS(ECCVMLookupRelationImpl, ECCVMFlavor); } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation.cpp index 41af3083e12..bb7c3738b6f 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation.cpp @@ -5,6 +5,6 @@ namespace bb { template class ECCVMMSMRelationImpl; -DEFINE_ZK_SUMCHECK_RELATION_CLASS(ECCVMMSMRelationImpl, ECCVMFlavor); +DEFINE_SUMCHECK_RELATION_CLASS(ECCVMMSMRelationImpl, ECCVMFlavor); } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation.cpp index 2abf1ac3a22..ce12ce062ac 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation.cpp @@ -4,6 +4,6 @@ namespace bb { template class ECCVMPointTableRelationImpl; -DEFINE_ZK_SUMCHECK_RELATION_CLASS(ECCVMPointTableRelationImpl, ECCVMFlavor); +DEFINE_SUMCHECK_RELATION_CLASS(ECCVMPointTableRelationImpl, ECCVMFlavor); } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation.cpp index 495c60c59be..03e8565fcb4 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation.cpp @@ -4,7 +4,7 @@ namespace bb { template class ECCVMSetRelationImpl; -DEFINE_ZK_SUMCHECK_RELATION_CLASS(ECCVMSetRelationImpl, ECCVMFlavor); +DEFINE_SUMCHECK_RELATION_CLASS(ECCVMSetRelationImpl, ECCVMFlavor); DEFINE_SUMCHECK_PERMUTATION_CLASS(ECCVMSetRelationImpl, ECCVMFlavor); } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation.cpp index 498133147a8..79834907a99 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation.cpp @@ -7,5 +7,5 @@ namespace bb { template class ECCVMTranscriptRelationImpl; -DEFINE_ZK_SUMCHECK_RELATION_CLASS(ECCVMTranscriptRelationImpl, ECCVMFlavor); +DEFINE_SUMCHECK_RELATION_CLASS(ECCVMTranscriptRelationImpl, ECCVMFlavor); } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation.cpp index 06cc795e012..2ab7a00a381 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation.cpp @@ -5,6 +5,6 @@ namespace bb { template class ECCVMWnafRelationImpl; -DEFINE_ZK_SUMCHECK_RELATION_CLASS(ECCVMWnafRelationImpl, ECCVMFlavor); +DEFINE_SUMCHECK_RELATION_CLASS(ECCVMWnafRelationImpl, ECCVMFlavor); } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_decomposition_relation_2.cpp b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_decomposition_relation_2.cpp index 93220028f21..095bdc164d2 100644 --- a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_decomposition_relation_2.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_decomposition_relation_2.cpp @@ -10,10 +10,9 @@ template void TranslatorDecompositionRelationImpl::accumul RelationParameters const&, TranslatorFlavor::FF const&); template void TranslatorDecompositionRelationImpl::accumulate< - bb::Relation< - TranslatorDecompositionRelationImpl>::ZKSumcheckTupleOfUnivariatesOverSubrelations, + bb::Relation>::SumcheckTupleOfUnivariatesOverSubrelations, TranslatorFlavor::ExtendedEdges>(bb::Relation>:: - ZKSumcheckTupleOfUnivariatesOverSubrelations&, + SumcheckTupleOfUnivariatesOverSubrelations&, TranslatorFlavor::ExtendedEdges const&, RelationParameters const&, TranslatorFlavor::FF const&); diff --git a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_delta_range_constraint_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_delta_range_constraint_relation.cpp index 2aef52a29bb..fd651d31f2e 100644 --- a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_delta_range_constraint_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_delta_range_constraint_relation.cpp @@ -2,5 +2,5 @@ #include "barretenberg/translator_vm/translator_flavor.hpp" namespace bb { template class TranslatorDeltaRangeConstraintRelationImpl; -DEFINE_ZK_SUMCHECK_RELATION_CLASS(TranslatorDeltaRangeConstraintRelationImpl, TranslatorFlavor); +DEFINE_SUMCHECK_RELATION_CLASS(TranslatorDeltaRangeConstraintRelationImpl, TranslatorFlavor); } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_extra_relations.cpp b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_extra_relations.cpp index b0055bf1107..eafbafc88b0 100644 --- a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_extra_relations.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_extra_relations.cpp @@ -6,7 +6,7 @@ template class TranslatorOpcodeConstraintRelationImpl; template class TranslatorAccumulatorTransferRelationImpl; template class TranslatorZeroConstraintsRelationImpl; -DEFINE_ZK_SUMCHECK_RELATION_CLASS(TranslatorOpcodeConstraintRelationImpl, TranslatorFlavor); -DEFINE_ZK_SUMCHECK_RELATION_CLASS(TranslatorAccumulatorTransferRelationImpl, TranslatorFlavor); -DEFINE_ZK_SUMCHECK_RELATION_CLASS(TranslatorZeroConstraintsRelationImpl, TranslatorFlavor); +DEFINE_SUMCHECK_RELATION_CLASS(TranslatorOpcodeConstraintRelationImpl, TranslatorFlavor); +DEFINE_SUMCHECK_RELATION_CLASS(TranslatorAccumulatorTransferRelationImpl, TranslatorFlavor); +DEFINE_SUMCHECK_RELATION_CLASS(TranslatorZeroConstraintsRelationImpl, TranslatorFlavor); } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_non_native_field_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_non_native_field_relation.cpp index 480c3debd44..f80ddfe235c 100644 --- a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_non_native_field_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_non_native_field_relation.cpp @@ -2,5 +2,5 @@ #include "barretenberg/translator_vm/translator_flavor.hpp" namespace bb { template class TranslatorNonNativeFieldRelationImpl; -DEFINE_ZK_SUMCHECK_RELATION_CLASS(TranslatorNonNativeFieldRelationImpl, TranslatorFlavor); +DEFINE_SUMCHECK_RELATION_CLASS(TranslatorNonNativeFieldRelationImpl, TranslatorFlavor); } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_permutation_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_permutation_relation.cpp index da62ffb604d..81f96d8f8ff 100644 --- a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_permutation_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_permutation_relation.cpp @@ -2,5 +2,5 @@ #include "barretenberg/translator_vm/translator_flavor.hpp" namespace bb { template class TranslatorPermutationRelationImpl; -DEFINE_ZK_SUMCHECK_RELATION_CLASS(TranslatorPermutationRelationImpl, TranslatorFlavor); +DEFINE_SUMCHECK_RELATION_CLASS(TranslatorPermutationRelationImpl, TranslatorFlavor); } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_flavor.hpp index 088ff2979f7..f486752a9cb 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_flavor.hpp @@ -54,7 +54,7 @@ template class ECCVMRecursiveFlavor_ { // think these two are not needed for recursive verifier land // using GrandProductRelations = std::tuple>; // using LookupRelation = ECCVMLookupRelation; - static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); + static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation diff --git a/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.cpp index e00ffd9416d..844f45d840f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.cpp @@ -63,19 +63,27 @@ template void ECCVMRecursiveVerifier_::verify_proof(co // maximum possible size of an ECCVM circuit otherwise we might run into problem because the number of rounds of // sumcheck is dependent on circuit size. const size_t log_circuit_size = numeric::get_msb(static_cast(circuit_size.get_value())); - auto sumcheck = SumcheckVerifier(log_circuit_size, transcript, FF(0)); + auto sumcheck = SumcheckVerifier(log_circuit_size, transcript); const FF alpha = transcript->template get_challenge("Sumcheck:alpha"); std::vector gate_challenges(CONST_PROOF_SIZE_LOG_N); for (size_t idx = 0; idx < gate_challenges.size(); idx++) { gate_challenges[idx] = transcript->template get_challenge("Sumcheck:gate_challenge_" + std::to_string(idx)); } + // Receive commitments to Libra masking polynomials + std::vector libra_commitments; + for (size_t idx = 0; idx < log_circuit_size; idx++) { + Commitment libra_commitment = + transcript->template receive_from_prover("Libra:commitment_" + std::to_string(idx)); + libra_commitments.push_back(libra_commitment); + } + auto [multivariate_challenge, claimed_evaluations, libra_evaluations, sumcheck_verified] = sumcheck.verify(relation_parameters, alpha, gate_challenges); // Compute the Shplemini accumulator consisting of the Shplonk evaluation and the commitments and scalars vector // produced by the unified protocol - const BatchOpeningClaim sumcheck_batch_opening_claims = + BatchOpeningClaim sumcheck_batch_opening_claims = Shplemini::compute_batch_opening_claim(circuit_size, commitments.get_unshifted(), commitments.get_to_be_shifted(), @@ -83,7 +91,11 @@ template void ECCVMRecursiveVerifier_::verify_proof(co claimed_evaluations.get_shifted(), multivariate_challenge, key->pcs_verification_key->get_g1_identity(), - transcript); + transcript, + /* concatenation_group_commitments = */ {}, + /* concatenated_evaluations = */ {}, + RefVector(libra_commitments), + libra_evaluations); // Reduce the accumulator to a single opening claim const OpeningClaim multivariate_to_univariate_opening_claim = PCS::reduce_batch_opening_claim(sumcheck_batch_opening_claims); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.test.cpp index 77325bad7e6..1c2d82719aa 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_verifier.test.cpp @@ -95,7 +95,6 @@ template class ECCVMRecursiveTests : public ::testing InnerVerifier native_verifier(prover.key); bool native_result = native_verifier.verify_proof(proof); EXPECT_TRUE(native_result); - auto recursive_manifest = verifier.transcript->get_manifest(); auto native_manifest = native_verifier.transcript->get_manifest(); for (size_t i = 0; i < recursive_manifest.size(); ++i) { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp index a40cae2ad3b..63b3c56eddf 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp @@ -89,7 +89,7 @@ template class TranslatorRecursiveFlavor_ { using Relations = TranslatorFlavor::Relations_; - static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); + static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); static constexpr size_t MAX_TOTAL_RELATION_LENGTH = compute_max_total_relation_length(); // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` 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 949257935d6..52180676b95 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 @@ -111,6 +111,12 @@ std::array TranslatorRecursiveVerifier_template get_challenge("Sumcheck:gate_challenge_" + std::to_string(idx)); } + std::vector libra_commitments; + for (size_t idx = 0; idx < log_circuit_size; idx++) { + Commitment libra_commitment = + transcript->template receive_from_prover("Libra:commitment_" + std::to_string(idx)); + libra_commitments.push_back(libra_commitment); + } auto [multivariate_challenge, claimed_evaluations, libra_evaluations, sumcheck_verified] = sumcheck.verify(relation_parameters, alpha, gate_challenges); @@ -124,7 +130,10 @@ std::array TranslatorRecursiveVerifier_ class SumcheckProver { std::shared_ptr transcript; SumcheckProverRound round; - // Declare a container for ZK Sumcheck data - ZKSumcheckData zk_sumcheck_data; /** * @@ -190,13 +188,11 @@ template class SumcheckProver { SumcheckOutput prove(ProverPolynomials& full_polynomials, const bb::RelationParameters& relation_parameters, const RelationSeparator alpha, - const std::vector& gate_challenges) + const std::vector& gate_challenges, + ZKSumcheckData zk_sumcheck_data = ZKSumcheckData()) { // In case the Flavor has ZK, we populate sumcheck data structure with randomness, compute correcting term for // the total sum, etc. - if constexpr (Flavor::HasZK) { - setup_zk_sumcheck_data(zk_sumcheck_data); - }; bb::GateSeparatorPolynomial gate_separators(gate_challenges, multivariate_d); @@ -368,180 +364,13 @@ polynomials that are sent in clear. ClaimedEvaluations extract_claimed_evaluations(PartiallyEvaluatedMultivariates& partially_evaluated_polynomials) { ClaimedEvaluations multivariate_evaluations; - if constexpr (!Flavor::HasZK) { - for (auto [eval, poly] : - zip_view(multivariate_evaluations.get_all(), partially_evaluated_polynomials.get_all())) { - eval = poly[0]; - }; - } else { - // Extract claimed evaluations of non-witness polynomials - for (auto [eval, poly] : zip_view(multivariate_evaluations.get_non_witnesses(), - partially_evaluated_polynomials.get_non_witnesses())) { - eval = poly[0]; - }; - // Extract claimed evaluations of all witness polynomials - for (auto [eval, poly, masking_term] : zip_view(multivariate_evaluations.get_all_witnesses(), - partially_evaluated_polynomials.get_all_witnesses(), - zk_sumcheck_data.masking_terms_evaluations)) { - eval = poly[0] + masking_term.value_at(0); - } - } - return multivariate_evaluations; - }; - - /** - * @brief Create and populate the structure required for the ZK Sumcheck. - - * @details This method creates an array of random field elements \f$ \rho_1,\ldots, \rho_{N_w}\f$ aimed to mask the - evaluations of witness polynomials, these are contained in \f$ \texttt{eval_masking_scalars} \f$. In order to - optimize the computation of Sumcheck Round Univariates, it populates a table of univariates \f$ - \texttt{masking_terms_evaluations} \f$ which contains at the beginning the evaluations of polynomials \f$ \rho_j - \cdot (1-X)\cdot X \f$ at \f$ 0,\ldots, \text{MAX_PARTIAL_RELATION_LENGTH} - 1\f$. This method also creates Libra - univariates, computes the Libra total sum and adds it to the transcript, and sets up all auxiliary objects. - * - * @param zk_sumcheck_data - */ - void setup_zk_sumcheck_data(ZKSumcheckData& zk_sumcheck_data) - { - - EvalMaskingScalars eval_masking_scalars; - - for (size_t k = 0; k < NUM_ALL_WITNESS_ENTITIES; ++k) { - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1136): Once Shplemini supports ZK, these - // constants must be generated in Oink - eval_masking_scalars[k] = FF(0); - }; - // Generate random scalars \f$ \rho_1,\ldots, \rho_{N_w}\f$ to mask the evaluations of witness polynomials and - // populate the table masking_terms_evaluations with the terms \f$ \rho_j \cdot (1-k) \cdot k \f$ - auto masking_terms_evaluations = create_evaluation_masking_table(eval_masking_scalars); - // Generate random Libra Polynomials to mask Round Univariates. - LibraUnivariates libra_univariates = generate_libra_polynomials(multivariate_d); - // have to commit to libra_univariates here - auto libra_scaling_factor = FF(1); - FF libra_total_sum = compute_libra_total_sum(libra_univariates, libra_scaling_factor); - transcript->send_to_verifier("Libra:Sum", libra_total_sum); - // get the challenge for the zk-sumcheck claim \sigma + \rho \cdot libra_total_sum - FF libra_challenge = transcript->template get_challenge("Libra:Challenge"); - // Initialize Libra running sum by multiplpying it by Libra challenge \f$\rho\f$; - auto libra_running_sum = libra_total_sum * libra_challenge; - // Multiply the column-univariates of the array of libra polynomials by libra challenge and power of \f$ 2\f$, - // modify libra running_sum subtracting the contribution from the first univariate - setup_libra_data(libra_univariates, libra_scaling_factor, libra_challenge, libra_running_sum); - - std::vector libra_evaluations; - libra_evaluations.reserve(multivariate_d); - zk_sumcheck_data = ZKSumcheckData{ eval_masking_scalars, masking_terms_evaluations, libra_univariates, - libra_scaling_factor, libra_challenge, libra_running_sum, - libra_evaluations }; - }; - - /** - * @brief Given number of univariate polynomials and the number of their evaluations meant to be hidden, this method - * produces a vector of univariate polynomials of degree \ref ZK_BATCHED_LENGTH "ZK_BATCHED_LENGTH - 1" with - * independent uniformly random coefficients. - * - */ - static LibraUnivariates generate_libra_polynomials(size_t number_of_polynomials) - { - LibraUnivariates libra_full_polynomials(number_of_polynomials); - for (auto& libra_polynomial : libra_full_polynomials) { - // generate random polynomial of required size - libra_polynomial = bb::Univariate::get_random(); + for (auto [eval, poly] : + zip_view(multivariate_evaluations.get_all(), partially_evaluated_polynomials.get_all())) { + eval = poly[0]; }; - - return libra_full_polynomials; - }; - /** - * @brief Generate an array of random scalars of size equal to the number of all witness polynomials and populate a - * table of evaluations of the quadratic terms needed for masking evaluations of witnesses. - * - * @param evaluations - */ - static EvaluationMaskingTable create_evaluation_masking_table(EvalMaskingScalars eval_masking_scalars) - { - EvaluationMaskingTable output_table; - for (size_t column_idx = 0; column_idx < NUM_ALL_WITNESS_ENTITIES; ++column_idx) { - for (size_t row_idx = 0; row_idx < MAX_PARTIAL_RELATION_LENGTH; ++row_idx) { - auto scalar = FF(row_idx); - output_table[column_idx].value_at(row_idx) = - scalar * (FF(1) - scalar) * eval_masking_scalars[column_idx]; - }; - }; - return output_table; + return multivariate_evaluations; }; - /** - * @brief Update the table of masking quadratic terms by adding a contribution from a current challenge. - * - @details At initialization, \f$j\f$'th column of the masking terms evaluations table is a vector \f$(0, 0, \rho_2 - \cdot 2, \ldots, \rho_j \cdot k (1-k), \ldots, \rho_j \cdot (D-1) (1-(D-1)))\f$. Upon getting current round - challenge, the prover adds the term \f$ \rho_j \cdot u_i \cdot (1-u_i)\f$ to each entry in the table. - - It is useful at the stage of evaluating the relation \f$ \tilde{F} \f$ at the arguments given by the values of - \f$(\widehat{P}_1, \ldots, \widehat{P}_{N_w})\f$ at the points \f$u_0,\ldots, u_{i}, k, \vec \ell)\f$. - * @param evaluations - * @param masking_scalars - * @param round_challenge - */ - void update_masking_terms_evaluations(ZKSumcheckData& zk_sumcheck_data, FF round_challenge) - { - for (auto [masking_term, masking_scalar] : - zip_view(zk_sumcheck_data.masking_terms_evaluations, zk_sumcheck_data.eval_masking_scalars)) { - for (size_t k = 0; k < MAX_PARTIAL_RELATION_LENGTH; ++k) { - masking_term.value_at(k) += round_challenge * (FF(1) - round_challenge) * masking_scalar; - } - } - } - /** - * @brief Compute the sum of the randomly sampled multivariate polynomial \f$ G = \sum_{i=0}^{n-1} g_i(X_i) \f$ over - * the Boolean hypercube. - * - * @param libra_univariates - * @param scaling_factor - * @return FF - */ - static FF compute_libra_total_sum(auto libra_univariates, FF& scaling_factor) - { - FF total_sum = 0; - scaling_factor = scaling_factor / 2; - - for (auto univariate : libra_univariates) { - total_sum += univariate.value_at(0) + univariate.value_at(1); - scaling_factor *= 2; - } - total_sum *= scaling_factor; - - return total_sum; - } - /** - * @brief Set up Libra book-keeping table that simplifies the computation of Libra Round Univariates - * - * @details The array of Libra univariates is getting scaled - * \f{align}{ - \texttt{libra_univariates} \gets \texttt{libra_univariates}\cdot \rho \cdot 2^{d-1} - \f} - * We also initialize - * \f{align}{ - \texttt{libra_running_sum} \gets \texttt{libra_total_sum} - \texttt{libra_univariates}_{0,0} - - \texttt{libra_univariates}_{0,1} \f}. - * @param libra_table - * @param libra_round_factor - * @param libra_challenge - */ - void setup_libra_data(auto& libra_univariates, - FF& libra_scaling_factor, - const FF libra_challenge, - FF& libra_running_sum) - { - libra_scaling_factor *= libra_challenge; // \rho * 2^{d-1} - for (auto& univariate : libra_univariates) { - univariate *= libra_scaling_factor; - }; - // subtract the contribution of the first libra univariate from libra total sum - libra_running_sum += -libra_univariates[0].value_at(0) - libra_univariates[0].value_at(1); - libra_running_sum *= FF(1) / FF(2); - } - /** * @brief Upon receiving the challenge \f$u_i\f$, the prover updates Libra data. If \f$ i < d-1\f$ @@ -567,7 +396,7 @@ polynomials that are sent in clear. * @param libra_running_sum * @param libra_evaluations */ - void update_libra_data(ZKSumcheckData& zk_sumcheck_data, const FF round_challenge, size_t round_idx) + void update_zk_sumcheck_data(ZKSumcheckData& zk_sumcheck_data, const FF round_challenge, size_t round_idx) { // when round_idx = d - 1, the update is not needed if (round_idx < zk_sumcheck_data.libra_univariates.size() - 1) { @@ -598,11 +427,6 @@ polynomials that are sent in clear. }; } - void update_zk_sumcheck_data(ZKSumcheckData& zk_sumcheck_data, FF round_challenge, size_t round_idx) - { - update_libra_data(zk_sumcheck_data, round_challenge, round_idx); - update_masking_terms_evaluations(zk_sumcheck_data, round_challenge); - } /** * @brief By the design of ZK Sumcheck, instead of claimed evaluations of witness polynomials \f$ P_1, \ldots, P_{N_w} \f$, the prover sends the evaluations of the witness polynomials masked by the terms \f$ \rho_j diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.test.cpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.test.cpp index eac46840fe8..174f62fa598 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.test.cpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.test.cpp @@ -142,7 +142,14 @@ template class SumcheckTests : public ::testing::Test { gate_challenges[idx] = transcript->template get_challenge("Sumcheck:gate_challenge_" + std::to_string(idx)); } - auto output = sumcheck.prove(full_polynomials, {}, alpha, gate_challenges); + SumcheckOutput output; + + if constexpr (Flavor::HasZK) { + ZKSumcheckData zk_sumcheck_data(multivariate_d, transcript); + output = sumcheck.prove(full_polynomials, {}, alpha, gate_challenges, zk_sumcheck_data); + } else { + output = sumcheck.prove(full_polynomials, {}, alpha, gate_challenges); + } FF u_0 = output.challenge[0]; FF u_1 = output.challenge[1]; std::vector expected_values; @@ -220,8 +227,14 @@ template class SumcheckTests : public ::testing::Test { prover_gate_challenges[idx] = prover_transcript->template get_challenge("Sumcheck:gate_challenge_" + std::to_string(idx)); } - auto output = - sumcheck_prover.prove(full_polynomials, relation_parameters, prover_alpha, prover_gate_challenges); + SumcheckOutput output; + if constexpr (Flavor::HasZK) { + ZKSumcheckData zk_sumcheck_data(multivariate_d, prover_transcript); + output = sumcheck_prover.prove( + full_polynomials, relation_parameters, prover_alpha, prover_gate_challenges, zk_sumcheck_data); + } else { + output = sumcheck_prover.prove(full_polynomials, relation_parameters, prover_alpha, prover_gate_challenges); + } auto verifier_transcript = Flavor::Transcript::verifier_init_empty(prover_transcript); @@ -301,8 +314,15 @@ template class SumcheckTests : public ::testing::Test { prover_gate_challenges[idx] = prover_transcript->template get_challenge("Sumcheck:gate_challenge_" + std::to_string(idx)); } - auto output = - sumcheck_prover.prove(full_polynomials, relation_parameters, prover_alpha, prover_gate_challenges); + SumcheckOutput output; + if constexpr (Flavor::HasZK) { + // construct libra masking polynomials and compute auxiliary data + ZKSumcheckData zk_sumcheck_data(multivariate_d, prover_transcript); + output = sumcheck_prover.prove( + full_polynomials, relation_parameters, prover_alpha, prover_gate_challenges, zk_sumcheck_data); + } else { + output = sumcheck_prover.prove(full_polynomials, relation_parameters, prover_alpha, prover_gate_challenges); + } auto verifier_transcript = Flavor::Transcript::verifier_init_empty(prover_transcript); diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp index 7aaf9e8d793..758797b8bcc 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp @@ -112,29 +112,6 @@ template class SumcheckProverRound { } } - template - void extend_edges_with_masking(ExtendedEdges& extended_edges, - ProverPolynomialsOrPartiallyEvaluatedMultivariates& multivariates, - const size_t edge_idx, - const ZKSumcheckData& zk_sumcheck_data) - { - // extend edges of witness polynomials and add correcting terms - for (auto [extended_edge, multivariate, masking_univariate] : - zip_view(extended_edges.get_all_witnesses(), - multivariates.get_all_witnesses(), - zk_sumcheck_data.masking_terms_evaluations)) { - bb::Univariate edge({ multivariate[edge_idx], multivariate[edge_idx + 1] }); - extended_edge = edge.template extend_to(); - extended_edge += masking_univariate; - }; - // extend edges of public polynomials - for (auto [extended_edge, multivariate] : - zip_view(extended_edges.get_non_witnesses(), multivariates.get_non_witnesses())) { - bb::Univariate edge({ multivariate[edge_idx], multivariate[edge_idx + 1] }); - extended_edge = edge.template extend_to(); - }; - }; - /** * @brief Return the evaluations of the univariate round polynomials \f$ \tilde{S}_{i} (X_{i}) \f$ at \f$ X_{i } = 0,\ldots, D \f$. Most likely, \f$ D \f$ is around \f$ 12 \f$. At the @@ -192,11 +169,7 @@ template class SumcheckProverRound { size_t end = (thread_idx + 1) * iterations_per_thread; for (size_t edge_idx = start; edge_idx < end; edge_idx += 2) { - if constexpr (!Flavor::HasZK) { - extend_edges(extended_edges[thread_idx], polynomials, edge_idx); - } else { - extend_edges_with_masking(extended_edges[thread_idx], polynomials, edge_idx, zk_sumcheck_data); - } + extend_edges(extended_edges[thread_idx], polynomials, edge_idx); // Compute the \f$ \ell \f$-th edge's univariate contribution, // scale it by the corresponding \f$ pow_{\beta} \f$ contribution and add it to the accumulators for \f$ // \tilde{S}^i(X_i) \f$. If \f$ \ell \f$'s binary representation is given by \f$ (\ell_{i+1},\ldots, @@ -322,7 +295,7 @@ template class SumcheckProverRound { { SumcheckRoundUnivariate libra_round_univariate; // select the i'th column of Libra book-keeping table - auto current_column = zk_sumcheck_data.libra_univariates[round_idx]; + const auto& current_column = zk_sumcheck_data.libra_univariates[round_idx]; // the evaluation of Libra round univariate at k=0...D are equal to \f$\texttt{libra_univariates}_{i}(k)\f$ // corrected by the Libra running sum for (size_t idx = 0; idx < BATCHED_RELATION_PARTIAL_LENGTH; ++idx) { @@ -535,6 +508,9 @@ template class SumcheckVerifierRound { Utils::scale_and_batch_elements(relation_evaluations, alpha, running_challenge, output); if constexpr (Flavor::HasZK) { output += full_libra_purported_value.value(); + if constexpr (IsECCVMRecursiveFlavor) { + output.self_reduce(); + } }; return output; } diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/zk_sumcheck_data.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/zk_sumcheck_data.hpp index 57f2db7f4f7..41d4e245a3c 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/zk_sumcheck_data.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/zk_sumcheck_data.hpp @@ -1,5 +1,6 @@ #pragma once +#include "barretenberg/polynomials/polynomial.hpp" #include "barretenberg/polynomials/univariate.hpp" #include #include @@ -18,37 +19,183 @@ template struct ZKSumcheckData { * \f$P_1,\ldots, P_N\f$. */ static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = Flavor::MAX_PARTIAL_RELATION_LENGTH; - // The number of all witnesses including shifts and derived witnesses from flavors that have ZK, - // otherwise, set this constant to 0. + /** * @brief The total algebraic degree of the Sumcheck relation \f$ F \f$ as a polynomial in Prover Polynomials * \f$P_1,\ldots, P_N\f$ incremented by 1, i.e. it is equal \ref MAX_PARTIAL_RELATION_LENGTH * "MAX_PARTIAL_RELATION_LENGTH + 1". */ static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = Flavor::BATCHED_RELATION_PARTIAL_LENGTH; - // Initialize the length of the array of evaluation masking scalars as 0 for non-ZK Flavors and as - // NUM_ALL_WITNESS_ENTITIES for ZK FLavors - static constexpr size_t MASKING_SCALARS_LENGTH = Flavor::HasZK ? Flavor::NUM_ALL_WITNESS_ENTITIES : 0; - // Array of random scalars used to hide the witness info from leaking through the claimed evaluations - using EvalMaskingScalars = std::array; - // Auxiliary table that represents the evaluations of quadratic polynomials r_j * X(1-X) at 0,..., - // MAX_PARTIAL_RELATION_LENGTH - 1 - using EvaluationMaskingTable = std::array, MASKING_SCALARS_LENGTH>; - // The size of the LibraUnivariates. We ensure that they do not take extra space when Flavor runs non-ZK - // Sumcheck. + // The size of the LibraUnivariates. We ensure that they do not take extra space when Flavor runs non-ZK Sumcheck. static constexpr size_t LIBRA_UNIVARIATES_LENGTH = Flavor::HasZK ? Flavor::BATCHED_RELATION_PARTIAL_LENGTH : 0; // Container for the Libra Univariates. Their number depends on the size of the circuit. using LibraUnivariates = std::vector>; // Container for the evaluations of Libra Univariates that have to be proven. using ClaimedLibraEvaluations = std::vector; - EvalMaskingScalars eval_masking_scalars; - EvaluationMaskingTable masking_terms_evaluations; LibraUnivariates libra_univariates; + LibraUnivariates libra_univariates_monomial; FF libra_scaling_factor{ 1 }; FF libra_challenge; FF libra_running_sum; ClaimedLibraEvaluations libra_evaluations; -}; + // Default constructor + ZKSumcheckData() = default; + + // Constructor + ZKSumcheckData(const size_t multivariate_d, + std::shared_ptr transcript, + std::shared_ptr proving_key = nullptr) + { + setup_zk_sumcheck_data(multivariate_d, transcript, proving_key); + } + + public: + /** + * @brief Create and populate the structure required for the ZK Sumcheck. + * + * @details This method creates an array of random field elements \f$ \rho_1,\ldots, \rho_{N_w}\f$ aimed to mask + * the evaluations of witness polynomials, these are contained in \f$ \texttt{eval_masking_scalars} \f$. In order to + * optimize the computation of Sumcheck Round Univariates, it populates a table of univariates \f$ + * \texttt{masking_terms_evaluations} \f$ which contains at the beginning the evaluations of polynomials \f$ \rho_j + * \cdot (1-X)\cdot X \f$ at \f$ 0,\ldots, \text{MAX_PARTIAL_RELATION_LENGTH} - 1\f$. This method also creates Libra + * univariates, computes the Libra total sum and adds it to the transcript, and sets up all auxiliary objects. + * + * @param zk_sumcheck_data + */ + void setup_zk_sumcheck_data(const size_t multivariate_d, + std::shared_ptr transcript, + std::shared_ptr proving_key = nullptr) + { + + // Generate random Libra polynomials in the Lagrange basis + libra_univariates = generate_libra_univariates(multivariate_d); + // To commit to libra_univariates and open them later, need to get their coefficients in the monomial basis + libra_univariates_monomial = transform_to_monomial(libra_univariates); + + // If proving_key is provided, commit to libra_univariates + if (proving_key != nullptr) { + size_t idx = 0; + for (auto& libra_univariate_monomial : libra_univariates_monomial) { + auto libra_commitment = proving_key->commitment_key->commit(Polynomial(libra_univariate_monomial)); + transcript->send_to_verifier("Libra:commitment_" + std::to_string(idx), libra_commitment); + idx++; + } + } + // Compute the total sum of the Libra polynomials + libra_scaling_factor = FF(1); + FF libra_total_sum = compute_libra_total_sum(libra_univariates, libra_scaling_factor); + + // Send the Libra total sum to the transcript + transcript->send_to_verifier("Libra:Sum", libra_total_sum); + + // Receive the Libra challenge from the transcript + libra_challenge = transcript->template get_challenge("Libra:Challenge"); + + // Initialize the Libra running sum + libra_running_sum = libra_total_sum * libra_challenge; + + // Setup the Libra data + setup_auxiliary_data(libra_univariates, libra_scaling_factor, libra_challenge, libra_running_sum); + } + + /** + * @brief Given number of univariate polynomials and the number of their evaluations meant to be hidden, this method + * produces a vector of univariate polynomials of degree \ref ZK_BATCHED_LENGTH "ZK_BATCHED_LENGTH - 1" with + * independent uniformly random coefficients. + * + */ + static LibraUnivariates generate_libra_univariates(const size_t number_of_polynomials) + { + LibraUnivariates libra_full_polynomials(number_of_polynomials); + + for (auto& libra_polynomial : libra_full_polynomials) { + libra_polynomial = bb::Univariate::get_random(); + }; + return libra_full_polynomials; + }; + + /** + * @brief Transform Libra univariates from Lagrange to monomial form + * + * @param libra_full_polynomials + * @return LibraUnivariates + */ + static LibraUnivariates transform_to_monomial(LibraUnivariates& libra_full_polynomials) + { + std::array interpolation_domain; + LibraUnivariates libra_univariates_monomial; + libra_univariates_monomial.reserve(libra_full_polynomials.size()); + + for (size_t idx = 0; idx < LIBRA_UNIVARIATES_LENGTH; idx++) { + interpolation_domain[idx] = FF(idx); + } + + for (auto& libra_polynomial : libra_full_polynomials) { + + // Use the efficient Lagrange interpolation + Polynomial libra_polynomial_monomial(std::span(interpolation_domain), + std::span(libra_polynomial.evaluations), + LIBRA_UNIVARIATES_LENGTH); + + // To avoid storing Polynomials (coefficients are vectors), we define a univariate with the coefficients + // interpolated above + bb::Univariate libra_univariate; + for (size_t idx = 0; idx < LIBRA_UNIVARIATES_LENGTH; idx++) { + libra_univariate.value_at(idx) = libra_polynomial_monomial[idx]; + } + libra_univariates_monomial.push_back(libra_univariate); + }; + return libra_univariates_monomial; + }; + + /** + * @brief Compute the sum of the randomly sampled multivariate polynomial \f$ G = \sum_{i=0}^{n-1} g_i(X_i) \f$ over + * the Boolean hypercube. + * + * @param libra_univariates + * @param scaling_factor + * @return FF + */ + static FF compute_libra_total_sum(const LibraUnivariates& libra_univariates, FF& scaling_factor) + { + FF total_sum = 0; + scaling_factor = scaling_factor / 2; + + for (auto& univariate : libra_univariates) { + total_sum += univariate.value_at(0) + univariate.value_at(1); + scaling_factor *= 2; + } + total_sum *= scaling_factor; + + return total_sum; + } + + /** + * @brief Set up Libra book-keeping table that simplifies the computation of Libra Round Univariates + * + * @details The array of Libra univariates is getting scaled + * \f{align}{\texttt{libra_univariates} \gets \texttt{libra_univariates}\cdot \rho \cdot 2^{d-1}\f} + * We also initialize + * \f{align}{ \texttt{libra_running_sum} \gets \texttt{libra_total_sum} - \texttt{libra_univariates}_{0,0} - + * \texttt{libra_univariates}_{0,1} \f}. + * @param libra_table + * @param libra_round_factor + * @param libra_challenge + */ + static void setup_auxiliary_data(auto& libra_univariates, + FF& libra_scaling_factor, + const FF libra_challenge, + FF& libra_running_sum) + { + libra_scaling_factor *= libra_challenge; // \rho * 2^{d-1} + for (auto& univariate : libra_univariates) { + univariate *= libra_scaling_factor; + }; + // subtract the contribution of the first libra univariate from libra total sum + libra_running_sum += -libra_univariates[0].value_at(0) - libra_univariates[0].value_at(1); + libra_running_sum *= FF(1) / FF(2); + } +}; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp index 0a3d495b890..dced2b66d4f 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp @@ -94,7 +94,7 @@ class TranslatorFlavor { TranslatorZeroConstraintsRelation>; using Relations = Relations_; - static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); + static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); static constexpr size_t MAX_TOTAL_RELATION_LENGTH = compute_max_total_relation_length(); // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` @@ -105,13 +105,13 @@ class TranslatorFlavor { // define the containers for storing the contributions from each relation in Sumcheck using SumcheckTupleOfTuplesOfUnivariates = - std::tuple::ZKSumcheckTupleOfUnivariatesOverSubrelations, - typename TranslatorDeltaRangeConstraintRelation::ZKSumcheckTupleOfUnivariatesOverSubrelations, - typename TranslatorOpcodeConstraintRelation::ZKSumcheckTupleOfUnivariatesOverSubrelations, - typename TranslatorAccumulatorTransferRelation::ZKSumcheckTupleOfUnivariatesOverSubrelations, - typename TranslatorDecompositionRelation::ZKSumcheckTupleOfUnivariatesOverSubrelations, - typename TranslatorNonNativeFieldRelation::ZKSumcheckTupleOfUnivariatesOverSubrelations, - typename TranslatorZeroConstraintsRelation::ZKSumcheckTupleOfUnivariatesOverSubrelations>; + std::tuple::SumcheckTupleOfUnivariatesOverSubrelations, + typename TranslatorDeltaRangeConstraintRelation::SumcheckTupleOfUnivariatesOverSubrelations, + typename TranslatorOpcodeConstraintRelation::SumcheckTupleOfUnivariatesOverSubrelations, + typename TranslatorAccumulatorTransferRelation::SumcheckTupleOfUnivariatesOverSubrelations, + typename TranslatorDecompositionRelation::SumcheckTupleOfUnivariatesOverSubrelations, + typename TranslatorNonNativeFieldRelation::SumcheckTupleOfUnivariatesOverSubrelations, + typename TranslatorZeroConstraintsRelation::SumcheckTupleOfUnivariatesOverSubrelations>; using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); /** diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_prover.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_prover.cpp index 08593f055f3..79f3c7220b1 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_prover.cpp @@ -158,7 +158,10 @@ void TranslatorProver::execute_relation_check_rounds() for (size_t idx = 0; idx < gate_challenges.size(); idx++) { gate_challenges[idx] = transcript->template get_challenge("Sumcheck:gate_challenge_" + std::to_string(idx)); } - sumcheck_output = sumcheck.prove(key->polynomials, relation_parameters, alpha, gate_challenges); + // create masking polynomials for sumcheck round univariates and auxiliary data + zk_sumcheck_data = ZKSumcheckData(key->log_circuit_size, transcript, key); + + sumcheck_output = sumcheck.prove(key->polynomials, relation_parameters, alpha, gate_challenges, zk_sumcheck_data); } /** @@ -181,7 +184,9 @@ void TranslatorProver::execute_pcs_rounds() key->commitment_key, transcript, key->polynomials.get_concatenated(), - key->polynomials.get_groups_to_be_concatenated()); + key->polynomials.get_groups_to_be_concatenated(), + zk_sumcheck_data.libra_univariates_monomial, + sumcheck_output.claimed_libra_evaluations); PCS::compute_opening_proof(key->commitment_key, prover_opening_claim, transcript); } diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_prover.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_prover.hpp index 82e66c5c021..a8a19591ef8 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_prover.hpp @@ -2,6 +2,7 @@ #include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/relations/relation_parameters.hpp" #include "barretenberg/sumcheck/sumcheck_output.hpp" +#include "barretenberg/sumcheck/zk_sumcheck_data.hpp" #include "barretenberg/translator_vm/translator_flavor.hpp" namespace bb { @@ -48,6 +49,8 @@ class TranslatorProver { CommitmentLabels commitment_labels; + ZKSumcheckData zk_sumcheck_data; + SumcheckOutput sumcheck_output; private: diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp index e53020e62a2..363346ee2ab 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_verifier.cpp @@ -102,6 +102,14 @@ bool TranslatorVerifier::verify_proof(const HonkProof& proof) gate_challenges[idx] = transcript->template get_challenge("Sumcheck:gate_challenge_" + std::to_string(idx)); } + // Receive commitments to Libra masking polynomials + std::vector libra_commitments; + for (size_t idx = 0; idx < log_circuit_size; idx++) { + Commitment libra_commitment = + transcript->receive_from_prover("Libra:commitment_" + std::to_string(idx)); + libra_commitments.push_back(libra_commitment); + } + auto [multivariate_challenge, claimed_evaluations, libra_evaluations, sumcheck_verified] = sumcheck.verify(relation_parameters, alpha, gate_challenges); @@ -120,7 +128,9 @@ bool TranslatorVerifier::verify_proof(const HonkProof& proof) Commitment::one(), transcript, commitments.get_groups_to_be_concatenated(), - claimed_evaluations.get_concatenated()); + claimed_evaluations.get_concatenated(), + RefVector(libra_commitments), + libra_evaluations); 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]);