diff --git a/barretenberg/cpp/src/aztec/honk/pcs/claim.hpp b/barretenberg/cpp/src/aztec/honk/pcs/claim.hpp index 67ab233c0623..eb162fbe0757 100644 --- a/barretenberg/cpp/src/aztec/honk/pcs/claim.hpp +++ b/barretenberg/cpp/src/aztec/honk/pcs/claim.hpp @@ -3,6 +3,21 @@ #include "polynomials/polynomial.hpp" namespace honk::pcs { +/** + * @brief Opening pair (r,v) for some witness polynomial p(X) such that p(r) = v + * + * @tparam Params for the given commitment scheme + */ +template class OpeningPair { + using Fr = typename Params::Fr; + + public: + Fr query; // r + Fr evaluation; // v = p(r) + + bool operator==(const OpeningPair& other) const = default; +}; + /** * @brief Unverified claim (C,r,v) for some witness polynomial p(X) such that * - C = Commit(p(X)) @@ -16,12 +31,10 @@ template class OpeningClaim { using Fr = typename Params::Fr; public: + // (query r, evaluation v = p(r)) + OpeningPair opening_pair; // commitment to univariate polynomial p(X) Commitment commitment; - // query r - Fr opening_point; - // evaluation v = p(r) - Fr eval; /** * @brief inefficiently check that the claim is correct by recomputing the commitment @@ -33,8 +46,8 @@ template class OpeningClaim { */ bool verify(CK* ck, const barretenberg::Polynomial& polynomial) const { - Fr real_eval = polynomial.evaluate(opening_point); - if (real_eval != eval) { + Fr real_eval = polynomial.evaluate(opening_pair.query); + if (real_eval != opening_pair.evaluation) { return false; } // Note: real_commitment is a raw type, while commitment may be a linear combination. @@ -49,61 +62,6 @@ template class OpeningClaim { bool operator==(const OpeningClaim& other) const = default; }; -/** - * @brief A claim for multiple polynomials opened at various points. - * - * @details Each polynomial pⱼ(X) is opened at mⱼ points. - * This gives a triple (Cⱼ, {xʲ₁, …, xʲₘⱼ}, {yʲ₁, …, yʲₘⱼ}) for each j, - * where yʲᵢ = pⱼ(xʲᵢ). - * We refer to 'queries' the set of opening points Ωⱼ = {xʲ₁, …, xʲₘⱼ}, - * and 'evals' is Yⱼ = {yʲ₁, …, yʲₘⱼ}. - * - * This structure groups all the triples by their common opening points sets. - * A 'SubClaim' is indexed by k, and is defined by Ωₖ = {xᵏ₁, …, xᵏₘₖ}, - * and a vector of pairs [(Cⱼ, Yⱼ)]ⱼ for all j such that Ωₖ = Ωⱼ. - * We refer to the latter as an 'Opening' of a 'SubClaim'. - * For Shplonk, it is also necessary to include a vector 'all_queries' which we - * define as Ω = ⋃ₖ Ωₖ. - * - * @invariant the following conditions are assumed to hold when BatchOpeningClaim - * is consumed by functions: - * - Each opening query set 'queries' Ωₖ is unique among all 'SubClaims' - * - Each commitment Cⱼ belongs to a single 'SubClaim' - * - The set 'all_queries' Ω is exactly the union of all 'queries' Ωₖ, - * and does not contain additional elements. - * The order is not important. - * - All 'evals' vectors must follow the same order defined by Ωₖ. - * - * SubClaim is a pair (Ωₖ, [(Cⱼ, Yⱼ)]ⱼ) where - * - 'queries' = Ωₖ = {xᵏ₁, …, xᵏₘₖ} - * - 'openings' = [(Cⱼ, Yⱼ)]ⱼ is a vector of pairs - * - 'commitment' = Cⱼ = Commit(pⱼ(X)) for each j - * - 'evals' = Yⱼ = {yʲ₁, …, yʲₘₖ}, and yʲᵢ = pⱼ(xʲᵢ) - * and follows the same order as 'queries' - * - * @tparam Params for the given commitment scheme - */ -template class MultiOpeningClaim { - using Fr = typename Params::Fr; - using Commitment = typename Params::Commitment; - - public: - struct Opening { - // Cⱼ = Commit(pⱼ(X)) for each j - Commitment commitment; - // Yⱼ = {yʲ₁, …, yʲₘₖ} - std::vector evals; - - bool operator==(const Opening& other) const = default; - }; - // Ωₖ = {xᵏ₁, …, xᵏₘₖ} - std::vector queries; - // [(Cⱼ, Yⱼ)]ⱼ - std::vector openings; - - bool operator==(const MultiOpeningClaim&) const = default; -}; - /** * @brief stores a claim of the form (C, v) for u=(u₀,…,uₘ₋₁) * where C is a univariate commitment to a polynomial diff --git a/barretenberg/cpp/src/aztec/honk/pcs/commitment_key.test.hpp b/barretenberg/cpp/src/aztec/honk/pcs/commitment_key.test.hpp index 7d8fafb77be2..e662ce402a43 100644 --- a/barretenberg/cpp/src/aztec/honk/pcs/commitment_key.test.hpp +++ b/barretenberg/cpp/src/aztec/honk/pcs/commitment_key.test.hpp @@ -38,17 +38,17 @@ template inline std::shared_ptr CreateCommitmentKey() return std::make_shared(); } -template inline VK* CreateVerificationKey(); +template inline std::shared_ptr CreateVerificationKey(); -template <> inline kzg::VerificationKey* CreateVerificationKey() +template <> inline std::shared_ptr CreateVerificationKey() { - return new kzg::VerificationKey(kzg_srs_path); + return std::make_shared(kzg_srs_path); } -template inline VK* CreateVerificationKey() +template inline std::shared_ptr CreateVerificationKey() // requires std::default_initializable { - return new VK(); + return std::make_shared(); } template class CommitmentTest : public ::testing::Test { @@ -66,7 +66,7 @@ template class CommitmentTest : public ::testing::Test { {} std::shared_ptr ck() { return commitment_key; } - VK* vk() { return verification_key; } + std::shared_ptr vk() { return verification_key; } Commitment commit(const Polynomial& polynomial) { return commitment_key->commit(polynomial); } @@ -81,7 +81,7 @@ template class CommitmentTest : public ::testing::Test { Fr random_element() { return Fr::random_element(engine); } - std::pair random_eval(const Polynomial& polynomial) + OpeningPair random_eval(const Polynomial& polynomial) { Fr x{ random_element() }; Fr y{ polynomial.evaluate(x) }; @@ -90,10 +90,11 @@ template class CommitmentTest : public ::testing::Test { std::pair, Polynomial> random_claim(const size_t n) { - auto p = random_polynomial(n); - auto [x, y] = random_eval(p); - auto c = commit(p); - return { { c, x, y }, p }; + auto polynomial = random_polynomial(n); + auto opening_pair = random_eval(polynomial); + auto commitment = commit(polynomial); + auto opening_claim = OpeningClaim{ opening_pair, commitment }; + return { opening_claim, polynomial }; }; std::vector random_evaluation_point(const size_t num_variables) @@ -107,48 +108,19 @@ template class CommitmentTest : public ::testing::Test { void verify_opening_claim(const OpeningClaim& claim, const Polynomial& witness) { - auto& [c, x, y] = claim; + auto& commitment = claim.commitment; + auto& [x, y] = claim.opening_pair; Fr y_expected = witness.evaluate(x); EXPECT_EQ(y, y_expected) << "OpeningClaim: evaluations mismatch"; - Commitment c_expected = commit(witness); - EXPECT_EQ(c, c_expected) << "OpeningClaim: commitment mismatch"; + Commitment commitment_expected = commit(witness); + EXPECT_EQ(commitment, commitment_expected) << "OpeningClaim: commitment mismatch"; } - /** - * @brief Ensures that a 'BatchOpeningClaim' is correct by checking that - * - all evaluations are correct by recomputing them from each witness polynomial. - * - commitments are correct by recomputing a commitment from each witness polynomial. - * - each 'queries' is a subset of 'all_queries' and 'all_queries' is the union of all 'queries' - * - each 'commitment' of each 'SubClaim' appears only once. - */ - void verify_batch_opening_claim(std::span> multi_claims, - std::span witnesses) + void verify_opening_pair(const OpeningPair& opening_pair, const Polynomial& witness) { - size_t idx = 0; - - for (const auto& [queries, openings] : multi_claims) { - const size_t num_queries = queries.size(); - - for (const auto& [commitment, evals] : openings) { - // compare commitment against recomputed commitment from witness - Commitment commitment_expected = commit(witnesses[idx]); - EXPECT_EQ(commitment, commitment_expected) - << "BatchOpeningClaim idx=" << idx << ": commitment mismatch"; - EXPECT_EQ(evals.size(), num_queries) - << "BatchOpeningClaim idx=" << idx << ": evaluation/query size mismatch"; - - // check evaluations for each point in queries - for (size_t i = 0; i < num_queries; ++i) { - - // check evaluation - Fr eval_expected = witnesses[idx].evaluate(queries[i]); - EXPECT_EQ(evals[i], eval_expected) - << "BatchOpeningClaim idx=" << idx << ": evaluation " << i << " mismatch"; - } - - ++idx; - } - } + auto& [x, y] = opening_pair; + Fr y_expected = witness.evaluate(x); + EXPECT_EQ(y, y_expected) << "OpeningPair: evaluations mismatch"; } /** @@ -169,6 +141,21 @@ template class CommitmentTest : public ::testing::Test { } } + /** + * @brief Ensures that a set of opening pairs is correct by checking that evaluations are + * correct by recomputing them from each witness polynomial. + */ + void verify_batch_opening_pair(std::span> opening_pairs, + std::span witnesses) + { + const size_t num_pairs = opening_pairs.size(); + ASSERT_EQ(witnesses.size(), num_pairs); + + for (size_t j = 0; j < num_pairs; ++j) { + this->verify_opening_pair(opening_pairs[j], witnesses[j]); + } + } + numeric::random::Engine* engine; // Per-test-suite set-up. @@ -188,19 +175,16 @@ template class CommitmentTest : public ::testing::Test { // Per-test-suite tear-down. // Called after the last test in this test suite. // Can be omitted if not needed. - static void TearDownTestSuite() - { - delete verification_key; - verification_key = nullptr; - } + static void TearDownTestSuite() {} static typename std::shared_ptr commitment_key; - static typename Params::VK* verification_key; + static typename std::shared_ptr verification_key; }; template typename std::shared_ptr CommitmentTest::commitment_key = nullptr; -template typename Params::VK* CommitmentTest::verification_key = nullptr; +template +typename std::shared_ptr CommitmentTest::verification_key = nullptr; using CommitmentSchemeParams = ::testing::Types; // IMPROVEMENT: reinstate typed-tests for multiple field types, i.e.: diff --git a/barretenberg/cpp/src/aztec/honk/pcs/gemini/gemini.hpp b/barretenberg/cpp/src/aztec/honk/pcs/gemini/gemini.hpp index a0804fdd8845..2b7fe3092448 100644 --- a/barretenberg/cpp/src/aztec/honk/pcs/gemini/gemini.hpp +++ b/barretenberg/cpp/src/aztec/honk/pcs/gemini/gemini.hpp @@ -68,31 +68,13 @@ template struct Proof { * * [A₀(-r) , ..., Aₘ₋₁(-r^{2ᵐ⁻¹})] */ - std::vector evals; + std::vector evaluations; }; /** - * @brief Univariate opening claims for multiple polynomials, - * each opened at a single different point (size = m+1). - * - * [ - * (C₀₊ , A₀ ( r) , r ) - * (C₀₋ , A₀ (-r) , -r ) - * (C₁ , A₁ (-r²) , -r²) - * ... - * (Cₘ₋₁, Aₘ₋₁(-r^{2ᵐ⁻¹}), -r^{2ᵐ⁻¹}) - * ] - * where - * C₀₊ is a simulated commitment to A₀ partially evaluated at r - * C₀₋ is a simulated commitment to A₀ partially evaluated at -r - * - * @tparam Params CommitmentScheme parameters - */ -template using OutputClaim = std::vector>; - -/** - * @brief Univariate witness polynomials for opening all the - * + * @brief Prover output (evalutation pair, witness) that can be passed on to Shplonk batch opening. + * @details Evaluation pairs {r, A₀₊(r)}, {-r, A₀₋(-r)}, {-r^{2^j}, Aⱼ(-r^{2^j)}, j = [1, ..., m-1] + * and witness (Fold) polynomials * [ * A₀₊(X) = F(X) + r⁻¹⋅G(X) * A₀₋(X) = F(X) - r⁻¹⋅G(X) @@ -100,31 +82,11 @@ template using OutputClaim = std::vector> * ... * Aₘ₋₁(X) = (1-uₘ₋₂)⋅even(Aₘ₋₂)(X) + uₘ₋₂⋅odd(Aₘ₋₂)(X) * ] - * where - * / r ⁻¹ ⋅ fⱼ(X) if fⱼ is a shift - * fⱼ₊(X) = | - * \ fⱼ(X) otherwise - * - * / (-r)⁻¹ ⋅ fⱼ(X) if fⱼ is a shift - * fⱼ₋(X) = | - * \ fⱼ(X) otherwise - * - * - * @tparam Params CommitmentScheme parameters - */ -template using OutputWitness = std::vector>; - -/** - * @brief Prover output (claim, witness, proof) that can be passed on to Shplonk batch opening. - * * @tparam Params CommitmentScheme parameters */ template struct ProverOutput { - OutputClaim claim; - - OutputWitness witness; - - Proof proof; + std::vector> opening_pairs; + std::vector> witnesses; }; template class MultilinearReductionScheme { @@ -140,83 +102,32 @@ template class MultilinearReductionScheme { * @brief reduces claims about multiple (shifted) MLE evaluation * * @param ck is the commitment key for creating the new commitments - * @param mle_opening_point = u =(u₀,...,uₘ₋₁) is the MLE opening point - * @param claims a set of MLE claims for the same point u - * @param mle_witness_polynomials the MLE polynomials for each evaluation. - * Internally, it contains a reference to the non-shifted polynomial. + * @param mle_opening_point u = (u₀,...,uₘ₋₁) is the MLE opening point + * @param batched_shifted batch polynomial constructed from the unshifted multivariates + * @param batched_to_be_shifted batch polynomial constructed from the to-be-shifted multivariates * @param transcript - * @return Output (result_claims, proof, folded_witness_polynomials) + * @return Output (opening pairs, folded_witness_polynomials) * - * Note: Only the proof and witness produced by this function are needed - * in the simple construction and verification of a single Honk proof. The - * result_claims constructed in this function are only relevant in a - * recursion setting. */ static ProverOutput reduce_prove(std::shared_ptr ck, std::span mle_opening_point, - std::span> claims, - std::span> claims_shifted, - const std::vector>& mle_witness_polynomials, - const std::vector>& mle_witness_polynomials_shifted, + const Polynomial&& batched_shifted, /* unshifted */ + const Polynomial&& batched_to_be_shifted, /* to-be-shifted */ const auto& transcript) { - // Relabel inputs to be consistent with the comments - auto& claims_f = claims; - auto& claims_g = claims_shifted; - auto& polys_f = mle_witness_polynomials; - auto& polys_g = mle_witness_polynomials_shifted; - const size_t num_variables = mle_opening_point.size(); // m - const size_t n = 1 << num_variables; - const size_t num_polys_f = polys_f.size(); - const size_t num_polys_g = polys_g.size(); - const size_t num_polys = num_polys_f + num_polys_g; - ASSERT(claims_f.size() == num_polys_f); - ASSERT(claims_g.size() == num_polys_g); - - // Generate batching challenge ρ and powers 1,ρ,…,ρᵐ⁻¹ - transcript->apply_fiat_shamir("rho"); - Fr rho = Fr::serialize_from_buffer(transcript->get_challenge("rho").begin()); - const std::vector rhos = powers_of_rho(rho, num_polys); - std::span rhos_span{ rhos }; - std::span rhos_f = rhos_span.subspan(0, num_polys_f); - std::span rhos_g = rhos_span.subspan(num_polys_f, num_polys_g); - - // Allocate m+1 witness polynomials + + // Allocate space for m+1 Fold polynomials // // At the end, the first two will contain the batched polynomial // partially evaluated at the challenges r,-r. // The other m-1 polynomials correspond to the foldings of A₀ - std::vector witness_polynomials; - witness_polynomials.reserve(num_variables + 1); - - // Create the batched polynomials + std::vector fold_polynomials; + fold_polynomials.reserve(num_variables + 1); // F(X) = ∑ⱼ ρʲ fⱼ(X) + Polynomial& batched_F = fold_polynomials.emplace_back(batched_shifted); // G(X) = ∑ⱼ ρᵏ⁺ʲ gⱼ(X) - // using powers of the challenge ρ. - // - // In what follows, we use indices j, k for non-shifted and shifted polynomials respectively. - // - // We separate A₀(X) into two polynomials F(X), G↺(X) - // such that A₀(X) = F(X) + G↺(X) = F(X) + G(X)/X. - - // F(X) = ∑ⱼ ρʲ fⱼ(X) - Polynomial& batched_F = witness_polynomials.emplace_back(Polynomial(n)); - for (size_t j = 0; j < num_polys_f; ++j) { - const size_t n_j = polys_f[j].size(); - ASSERT(n_j <= n); - // F(X) += ρʲ fⱼ(X) - batched_F.add_scaled(polys_f[j], rhos_f[j]); - } - - // G(X) = ∑ⱼ ρʲ gⱼ(X) - Polynomial& batched_G = witness_polynomials.emplace_back(Polynomial(n)); - for (size_t j = 0; j < num_polys_g; ++j) { - const size_t n_j = polys_g[j].size(); - ASSERT(n_j <= n); - // G(X) += ρʲ gⱼ(X) - batched_G.add_scaled(polys_g[j], rhos_g[j]); - } + Polynomial& batched_G = fold_polynomials.emplace_back(batched_to_be_shifted); // A₀(X) = F(X) + G↺(X) = F(X) + G(X)/X. Polynomial A_0(batched_F); @@ -235,7 +146,7 @@ template class MultilinearReductionScheme { const size_t n_l = 1 << (num_variables - l - 1); // A_l_fold = Aₗ₊₁(X) = (1-uₗ)⋅even(Aₗ)(X) + uₗ⋅odd(Aₗ)(X) - Fr* A_l_fold = witness_polynomials.emplace_back(Polynomial(n_l)).get_coefficients(); + Fr* A_l_fold = fold_polynomials.emplace_back(Polynomial(n_l)).get_coefficients(); // fold the previous polynomial with odd and even parts for (size_t i = 0; i < n_l; ++i) { @@ -252,23 +163,22 @@ template class MultilinearReductionScheme { } /* - * Create commitments C₁,…,Cₘ₋₁ + * Create commitments C₁,…,Cₘ₋₁ to polynomials FOLD_i, i = 1,...,d-1 and add to transcript */ std::vector commitments; commitments.reserve(num_variables - 1); for (size_t l = 0; l < num_variables - 1; ++l) { - commitments.emplace_back(ck->commit(witness_polynomials[l + 2])); + commitments.emplace_back(ck->commit(fold_polynomials[l + 2])); + transcript->add_element("FOLD_" + std::to_string(l + 1), + static_cast(commitments[l]).to_buffer()); } /* - * Add commitments FOLD_i, i = 1,...,d-1 to transcript and generate evaluation challenge r, and derive -r, r² + * Generate evaluation challenge r, and compute rₗ = r^{2ˡ} for l = 0, 1, ..., m-1 */ - for (size_t i = 0; i < commitments.size(); ++i) { - std::string label = "FOLD_" + std::to_string(i + 1); - transcript->add_element(label, static_cast(commitments[i]).to_buffer()); - } transcript->apply_fiat_shamir("r"); - const Fr r = Fr::serialize_from_buffer(transcript->get_challenge("r").begin()); + const Fr r_challenge = Fr::serialize_from_buffer(transcript->get_challenge("r").begin()); + std::vector r_squares = squares_of_r(r_challenge, num_variables); /* * Compute the witness polynomials for the resulting claim @@ -279,7 +189,7 @@ template class MultilinearReductionScheme { */ // 2 simulated polynomials and (m-1) polynomials from this round - Fr r_inv = r.invert(); + Fr r_inv = r_challenge.invert(); // G(X) *= r⁻¹ batched_G *= r_inv; @@ -288,7 +198,7 @@ template class MultilinearReductionScheme { // tmp = A₀(X) (&tmp == &A_0) // A_0_pos = F(X) (&A_0_pos == &batched_F) Polynomial& tmp = A_0; - Polynomial& A_0_pos = witness_polynomials[0]; + Polynomial& A_0_pos = fold_polynomials[0]; tmp = batched_F; // A₀₊(X) = F(X) + G(X)/r, s.t. A₀₊(r) = A₀(r) @@ -298,135 +208,145 @@ template class MultilinearReductionScheme { // After the swap, we have // tmp = G(X)/r // A_0_neg = F(X) (since &batched_F == &A_0_neg) - Polynomial& A_0_neg = witness_polynomials[1]; + Polynomial& A_0_neg = fold_polynomials[1]; // A₀₋(X) = F(X) - G(X)/r, s.t. A₀₋(-r) = A₀(-r) A_0_neg -= tmp; /* - * compute rₗ = r^{2ˡ} for l = 0, 1, ..., m-1 + * Compute the m+1 evaluations Aₗ(−r^{2ˡ}), l = 0, ..., m-1. + * Add them to the transcript */ - std::vector r_squares = squares_of_r(r, num_variables); - - // evaluate all new polynomials Aₗ at -rₗ - std::vector evals; - evals.reserve(num_variables); + std::vector fold_polynomial_evals; + fold_polynomial_evals.reserve(num_variables); for (size_t l = 0; l < num_variables; ++l) { - const Polynomial& A_l = witness_polynomials[l + 1]; - const Fr r_l_neg = -r_squares[l]; - evals.emplace_back(A_l.evaluate(r_l_neg)); - } + const Polynomial& A_l = fold_polynomials[l + 1]; - /* - * Add evaluations a_i, i = 0,...,m-1 to transcript - */ - for (size_t i = 0; i < evals.size(); ++i) { - std::string label = "a_" + std::to_string(i); - transcript->add_element(label, evals[i].to_buffer()); + fold_polynomial_evals.emplace_back(A_l.evaluate(-r_squares[l])); + transcript->add_element("a_" + std::to_string(l), fold_polynomial_evals[l].to_buffer()); } - /* - * Construct the 'Proof' which consists of: - * (1) The m-1 commitments [Fold^{l}], l = 1, ..., m-1 - * (2) The m evaluations a_0 = Fold_{-r}^(0)(-r), and a_l = Fold^(l)(-r^{2^l}), l = 1, ..., m-1 - */ - Proof proof = { commitments, evals }; + // Compute evaluation A₀(r) + auto a_0_pos = fold_polynomials[0].evaluate(r_challenge); - /* - * Compute new claims and add them to the output - */ - auto result_claims = - compute_output_claim_from_proof(claims_f, claims_g, mle_opening_point, rhos, r_squares, proof); + std::vector> fold_poly_opening_pairs; + fold_poly_opening_pairs.reserve(num_variables + 1); - return { result_claims, std::move(witness_polynomials), proof }; + // ( r, A₀(r) ) + fold_poly_opening_pairs.emplace_back(OpeningPair{ r_challenge, a_0_pos }); + // (-r, Aₗ(−r^{2ˡ}) ) + for (size_t l = 0; l < num_variables; ++l) { + fold_poly_opening_pairs.emplace_back(OpeningPair{ -r_squares[l], fold_polynomial_evals[l] }); + } + + return { fold_poly_opening_pairs, std::move(fold_polynomials) }; }; /** * @brief Checks that all MLE evaluations vⱼ contained in the list of m MLE opening claims * is correct, and returns univariate polynomial opening claims to be checked later * - * @param mle_opening_point the MLE evaluation point for all claims - * @param claims MLE claims with (C, v) and C is a univariate commitment - * @param claims_shifted MLE claims with (C, v↺) and C is a univariate commitment - * to the non-shifted polynomial + * @param mle_opening_point the MLE evaluation point u + * @param batched_evaluation batched evaluation from multivariate evals at the point u + * @param batched_f batched commitment to unshifted polynomials + * @param batched_g batched commitment to to-be-shifted polynomials * @param proof commitments to the m-1 folded polynomials, and alleged evaluations. * @param transcript - * @return BatchOpeningClaim + * @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 OutputClaim reduce_verify(std::span mle_opening_point, - std::span> claims, - std::span> claims_shifted, - const Proof& proof, - const auto& transcript) + static std::vector> reduce_verify(std::span mle_opening_point, /* u */ + const Fr batched_evaluation, /* all */ + Commitment& batched_f, /* unshifted */ + Commitment& batched_g, /* to-be-shifted */ + const Proof& proof, + const auto& transcript) { - // Relabel inputs to be more consistent with the math comments. - auto& claims_f = claims; - auto& claims_g = claims_shifted; - const size_t num_variables = mle_opening_point.size(); - const size_t num_claims_f = claims_f.size(); - const size_t num_claims_g = claims_g.size(); - const size_t num_claims = num_claims_f + num_claims_g; - - // batching challenge ρ - const Fr rho = Fr::serialize_from_buffer(transcript->get_challenge("rho").begin()); - // compute vector of powers of rho only once - std::vector rhos = powers_of_rho(rho, num_claims); - // random evaluation point r + // compute vector of powers of random evaluation point r const Fr r = Fr::serialize_from_buffer(transcript->get_challenge("r").begin()); - std::vector r_squares = squares_of_r(r, num_variables); - return compute_output_claim_from_proof(claims_f, claims_g, mle_opening_point, rhos, r_squares, proof); + // Compute evaluation A₀(r) + auto a_0_pos = compute_eval_pos(batched_evaluation, mle_opening_point, r_squares, proof.evaluations); + + // C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] + r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ] + // C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] - r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ] + auto [c0_r_pos, c0_r_neg] = compute_simulated_commitments(batched_f, batched_g, 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 }); + // ( [A₀₋], -r, A₀(-r) ) + fold_polynomial_opening_claims.emplace_back(OpeningClaim{ { -r, proof.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( + OpeningClaim{ { -r_squares[l + 1], proof.evaluations[l + 1] }, proof.commitments[l] }); + } + + return fold_polynomial_opening_claims; + }; + + /** + * @brief Reconstruct Gemini proof from transcript + * + * @param transcript + * @return Proof + * @details Proof consists of: + * - d Fold poly evaluations a_0, ..., a_{d-1} + * - (d-1) Fold polynomial commitments [Fold^(1)], ..., [Fold^(d-1)] + */ + static Proof reconstruct_proof_from_transcript(const auto& transcript, const size_t log_n) + { + Proof proof; + for (size_t i = 0; i < log_n; i++) { + std::string label = "a_" + std::to_string(i); + proof.evaluations.emplace_back(transcript->get_field_element(label)); + }; + for (size_t i = 1; i < log_n; i++) { + std::string label = "FOLD_" + std::to_string(i); + proof.commitments.emplace_back(transcript->get_group_element(label)); + }; + + return proof; + } + + static std::vector powers_of_rho(const Fr rho, const size_t num_powers) + { + std::vector rhos = { Fr(1), rho }; + rhos.reserve(num_powers); + for (size_t j = 2; j < num_powers; j++) { + rhos.emplace_back(rhos[j - 1] * rho); + } + return rhos; }; private: /** - * @brief computes the output claim given the transcript. + * @brief computes the output pair given the transcript. * This method is common for both prover and verifier. * - * @param claims_f set of input claims for non-shifted evaluations - * @param claims_g set of input claims for shifted evaluations + * @param evaluations evaluations of each multivariate * @param mle_vars MLE opening point u * @param rhos powers of the initial batching challenge ρ * @param r_squares squares of r, r², ..., r^{2ᵐ⁻¹} - * @param proof the proof produced by the prover - * @return OutputClaim + * @return evaluation A₀(r) */ - static OutputClaim compute_output_claim_from_proof(std::span> claims_f, - std::span> claims_g, - std::span mle_vars, - std::span rhos, - std::span r_squares, - const Proof& proof) + static Fr compute_eval_pos(const Fr batched_mle_eval, + std::span mle_vars, + std::span r_squares, + std::span fold_polynomial_evals) { const size_t num_variables = mle_vars.size(); - const size_t num_claims_f = claims_f.size(); - const size_t num_claims_g = claims_g.size(); - - const Fr r = r_squares[0]; - - const auto& evals = proof.evals; - // compute the batched MLE evaluation - // v = ∑ⱼ ρʲ vⱼ + ∑ⱼ ρᵏ⁺ʲ v↺ⱼ - Fr mle_eval{ Fr::zero() }; - - // add non-shifted evaluations - std::span rhos_f = rhos.subspan(0, num_claims_f); - for (size_t j = 0; j < num_claims_f; ++j) { - mle_eval += claims_f[j].evaluation * rhos_f[j]; - } - // add shifted evaluations - std::span rhos_g = rhos.subspan(num_claims_f, num_claims_g); - for (size_t j = 0; j < num_claims_g; ++j) { - mle_eval += claims_g[j].evaluation * rhos_g[j]; - } + const auto& evals = fold_polynomial_evals; - // For l = m, ..., 1 - // Initialize eval_pos = Aₘ(r^2ᵐ) = v - Fr eval_pos = mle_eval; + // Initialize eval_pos with batched MLE eval v = ∑ⱼ ρʲ vⱼ + ∑ⱼ ρᵏ⁺ʲ v↺ⱼ + Fr eval_pos = batched_mle_eval; for (size_t l = num_variables; l != 0; --l) { const Fr r = r_squares[l - 1]; // = rₗ₋₁ = r^{2ˡ⁻¹} const Fr eval_neg = evals[l - 1]; // = Aₗ₋₁(−r^{2ˡ⁻¹}) @@ -440,38 +360,8 @@ template class MultilinearReductionScheme { // and using Aₗ₋₁(−r^{2ˡ⁻¹}) sent by the prover in the proof. eval_pos = ((r * eval_pos * 2) - eval_neg * (r * (Fr(1) - u) - u)) / (r * (Fr(1) - u) + u); } - // eval_pos now equals A₀(r) - // add the claim for the first polynomial A₀ - // if there is a shift, then we need to add a separate claim for the - // evaluation at r and -r. - // C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] + r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ] - // C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] - r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ] - auto [c0_r_pos, c0_r_neg] = compute_simulated_commitments(claims_f, claims_g, rhos, r); - - std::vector> result_claims; - result_claims.reserve(num_variables + 1); - - // ( [A₀₊], r, A₀(r) ) - result_claims.emplace_back(OpeningClaim{ c0_r_pos, r, eval_pos }); - // ( [A₀₋], -r, A₀(-r) ) - result_claims.emplace_back(OpeningClaim{ c0_r_neg, -r, evals[0] }); - for (size_t l = 0; l < num_variables - 1; ++l) { - // ([A₀₋], -r, Aₗ(−r^{2ˡ}) ) - result_claims.emplace_back(OpeningClaim{ proof.commitments[l], -r_squares[l + 1], evals[l + 1] }); - } - - return result_claims; - }; - - static std::vector powers_of_rho(const Fr rho, const size_t num_powers) - { - std::vector rhos = { Fr(1), rho }; - rhos.reserve(num_powers); - for (size_t j = 2; j < num_powers; j++) { - rhos.emplace_back(rhos[j - 1] * rho); - } - return rhos; + return eval_pos; // return A₀(r) }; static std::vector squares_of_r(const Fr r, const size_t num_squares) @@ -487,41 +377,20 @@ template class MultilinearReductionScheme { /** * @brief Computes two commitments to A₀ partially evaluated in r and -r. * - * @param claims_f array of claims containing commitments to non-shifted polynomials - * @param claims_g array of claims containing commitments to shifted polynomials - * @param rhos vector of m powers of rho used for linear combination + * @param batched_f batched commitment to non-shifted polynomials + * @param batched_g 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( - std::span> claims_f, - std::span> claims_g, - std::span rhos, - Fr r) + static std::pair compute_simulated_commitments(Commitment& batched_f, + Commitment& batched_g, + Fr r) { - const size_t num_claims_f = claims_f.size(); - const size_t num_claims_g = claims_g.size(); - - Fr r_inv = r.invert(); - - // Commitment to F(X), G(X) - Commitment batched_f = Commitment::zero(); - std::span rhos_f = rhos.subspan(0, num_claims_f); - for (size_t j = 0; j < num_claims_f; ++j) { - batched_f += claims_f[j].commitment * rhos_f[j]; - } - - // Commitment to G(X) - Commitment batched_g = Commitment::zero(); - std::span rhos_g = rhos.subspan(num_claims_f, num_claims_g); - for (size_t j = 0; j < num_claims_g; ++j) { - batched_g += claims_g[j].commitment * rhos_g[j]; - } - // C₀ᵣ₊ = [F] + r⁻¹⋅[G] Commitment C0_r_pos = batched_f; // C₀ᵣ₋ = [F] - r⁻¹⋅[G] Commitment C0_r_neg = batched_f; + Fr r_inv = r.invert(); if (!batched_g.is_point_at_infinity()) { batched_g *= r_inv; C0_r_pos += batched_g; diff --git a/barretenberg/cpp/src/aztec/honk/pcs/gemini/gemini.test.cpp b/barretenberg/cpp/src/aztec/honk/pcs/gemini/gemini.test.cpp index 24fe08833d6d..12969c7580ad 100644 --- a/barretenberg/cpp/src/aztec/honk/pcs/gemini/gemini.test.cpp +++ b/barretenberg/cpp/src/aztec/honk/pcs/gemini/gemini.test.cpp @@ -1,18 +1,102 @@ #include "gemini.hpp" #include "../commitment_key.test.hpp" +#include "polynomials/polynomial.hpp" +#include #include +#include namespace honk::pcs::gemini { -template class GeminiTest : public CommitmentTest {}; +template class GeminiTest : public CommitmentTest { + using Gemini = MultilinearReductionScheme; + using Fr = typename Params::Fr; + using Commitment = typename Params::Commitment; + using Polynomial = typename barretenberg::Polynomial; + + public: + void execute_gemini_and_verify_claims(size_t log_n, + std::vector multilinear_evaluation_point, + std::vector multilinear_evaluations, + std::vector> multilinear_polynomials, + std::vector> multilinear_polynomials_to_be_shifted, + std::vector multilinear_commitments, + std::vector multilinear_commitments_to_be_shifted) + { + using Transcript = transcript::StandardTranscript; + auto transcript = std::make_shared(StandardHonk::create_unrolled_manifest(0, log_n)); + transcript->mock_inputs_prior_to_challenge("rho"); + transcript->apply_fiat_shamir("rho"); + const Fr rho = Fr::serialize_from_buffer(transcript->get_challenge("rho").begin()); + + std::vector rhos = Gemini::powers_of_rho(rho, multilinear_evaluations.size()); + + // Compute batched multivariate evaluation + Fr batched_evaluation = Fr::zero(); + for (size_t i = 0; i < multilinear_evaluations.size(); ++i) { + batched_evaluation += multilinear_evaluations[i] * rhos[i]; + } + + Polynomial batched_unshifted(1 << log_n); + Polynomial batched_to_be_shifted(1 << log_n); + Commitment batched_commitment_unshifted = Commitment::zero(); + Commitment batched_commitment_to_be_shifted = Commitment::zero(); + const size_t num_unshifted = multilinear_polynomials.size(); + const size_t num_shifted = multilinear_polynomials_to_be_shifted.size(); + for (size_t i = 0; i < num_unshifted; ++i) { + batched_unshifted.add_scaled(multilinear_polynomials[i], rhos[i]); + batched_commitment_unshifted += multilinear_commitments[i] * rhos[i]; + } + for (size_t i = 0; i < num_shifted; ++i) { + size_t rho_idx = num_unshifted + i; + batched_to_be_shifted.add_scaled(multilinear_polynomials_to_be_shifted[i], rhos[rho_idx]); + batched_commitment_to_be_shifted += multilinear_commitments_to_be_shifted[i] * rhos[rho_idx]; + } + + // 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 = Gemini::reduce_prove(this->ck(), + multilinear_evaluation_point, + std::move(batched_unshifted), + std::move(batched_to_be_shifted), + transcript); + + // Check that the Fold polynomials have been evaluated correctly in the prover + this->verify_batch_opening_pair(prover_output.opening_pairs, prover_output.witnesses); + + // Construct a Gemini proof object consisting of + // - d Fold poly evaluations a_0, ..., a_{d-1} + // - (d-1) Fold polynomial commitments [Fold^(1)], ..., [Fold^(d-1)] + auto gemini_proof = Gemini::reconstruct_proof_from_transcript(transcript, log_n); + + // 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_claim = Gemini::reduce_verify(multilinear_evaluation_point, + batched_evaluation, + batched_commitment_unshifted, + batched_commitment_to_be_shifted, + gemini_proof, + transcript); + + // Check equality of the opening pairs computed by prover and verifier + for (size_t i = 0; i < (log_n + 1); ++i) { + ASSERT_EQ(prover_output.opening_pairs[i], verifier_claim[i].opening_pair); + } + + // Explicitly verify the claims computed by the verfier + this->verify_batch_opening_claim(verifier_claim, prover_output.witnesses); + } +}; TYPED_TEST_SUITE(GeminiTest, CommitmentSchemeParams); -TYPED_TEST(GeminiTest, single) +TYPED_TEST(GeminiTest, Single) { - using Gemini = MultilinearReductionScheme; - using MLEOpeningClaim = MLEOpeningClaim; + using Fr = typename TypeParam::Fr; + using Commitment = typename TypeParam::Commitment; const size_t n = 16; const size_t log_n = 4; @@ -22,30 +106,26 @@ TYPED_TEST(GeminiTest, single) auto commitment = this->commit(poly); auto eval = poly.evaluate_mle(u); - // create opening claim - auto claims = { MLEOpeningClaim{ commitment, eval } }; - - using Transcript = transcript::StandardTranscript; - auto transcript = std::make_shared(StandardHonk::create_unrolled_manifest(0, log_n)); - - transcript->mock_inputs_prior_to_challenge("rho"); - - auto [prover_claim, witness, proof] = Gemini::reduce_prove(this->ck(), u, claims, {}, { poly }, {}, transcript); - - this->verify_batch_opening_claim(prover_claim, witness); - - auto verifier_claim = Gemini::reduce_verify(u, claims, {}, proof, transcript); - - this->verify_batch_opening_claim(verifier_claim, witness); - - EXPECT_EQ(prover_claim, verifier_claim); + // Collect multilinear polynomials evaluations, and commitments for input to prover/verifier + std::vector multilinear_evaluations = { eval }; + std::vector> multilinear_polynomials = { poly }; + std::vector> multilinear_polynomials_to_be_shifted = {}; + std::vector multilinear_commitments = { commitment }; + std::vector multilinear_commitments_to_be_shifted = {}; + + this->execute_gemini_and_verify_claims(log_n, + u, + multilinear_evaluations, + multilinear_polynomials, + multilinear_polynomials_to_be_shifted, + multilinear_commitments, + multilinear_commitments_to_be_shifted); } -TYPED_TEST(GeminiTest, shift) +TYPED_TEST(GeminiTest, SingleShift) { - using Gemini = MultilinearReductionScheme; using Fr = typename TypeParam::Fr; - using MLEOpeningClaim = MLEOpeningClaim; + using Commitment = typename TypeParam::Commitment; const size_t n = 16; const size_t log_n = 4; @@ -59,30 +139,26 @@ TYPED_TEST(GeminiTest, shift) auto commitment = this->commit(poly); auto eval_shift = poly.evaluate_mle(u, true); - // create opening claim - auto claims_shift = { - MLEOpeningClaim{ commitment, eval_shift }, - }; - - using Transcript = transcript::StandardTranscript; - auto transcript = std::make_shared(StandardHonk::create_unrolled_manifest(0, log_n)); - - transcript->mock_inputs_prior_to_challenge("rho"); - - auto [prover_claim, witness, proof] = - Gemini::reduce_prove(this->ck(), u, {}, claims_shift, {}, { poly }, transcript); - - this->verify_batch_opening_claim(prover_claim, witness); - - auto verifier_claim = Gemini::reduce_verify(u, {}, claims_shift, proof, transcript); - - EXPECT_EQ(prover_claim, verifier_claim); + // Collect multilinear polynomials evaluations, and commitments for input to prover/verifier + std::vector multilinear_evaluations = { eval_shift }; + std::vector> multilinear_polynomials = {}; + std::vector> multilinear_polynomials_to_be_shifted = { poly }; + std::vector multilinear_commitments = {}; + std::vector multilinear_commitments_to_be_shifted = { commitment }; + + this->execute_gemini_and_verify_claims(log_n, + u, + multilinear_evaluations, + multilinear_polynomials, + multilinear_polynomials_to_be_shifted, + multilinear_commitments, + multilinear_commitments_to_be_shifted); } -TYPED_TEST(GeminiTest, double) +TYPED_TEST(GeminiTest, Double) { - using Gemini = MultilinearReductionScheme; - using MLEOpeningClaim = MLEOpeningClaim; + using Fr = typename TypeParam::Fr; + using Commitment = typename TypeParam::Commitment; const size_t n = 16; const size_t log_n = 4; @@ -98,32 +174,27 @@ TYPED_TEST(GeminiTest, double) auto eval1 = poly1.evaluate_mle(u); auto eval2 = poly2.evaluate_mle(u); - const auto claims = { - MLEOpeningClaim{ commitment1, eval1 }, - MLEOpeningClaim{ commitment2, eval2 }, - }; - - using Transcript = transcript::StandardTranscript; - auto transcript = std::make_shared(StandardHonk::create_unrolled_manifest(0, log_n)); - - transcript->mock_inputs_prior_to_challenge("rho"); - - auto [prover_claim, witness, proof] = - Gemini::reduce_prove(this->ck(), u, claims, {}, { poly1, poly2 }, {}, transcript); - - this->verify_batch_opening_claim(prover_claim, witness); - - auto verifier_claim = Gemini::reduce_verify(u, claims, {}, proof, transcript); - - this->verify_batch_opening_claim(verifier_claim, witness); - EXPECT_EQ(prover_claim, verifier_claim); + // Collect multilinear polynomials evaluations, and commitments for input to prover/verifier + std::vector multilinear_evaluations = { eval1, eval2 }; + std::vector> multilinear_polynomials = { poly1, poly2 }; + std::vector> multilinear_polynomials_to_be_shifted = {}; + std::vector multilinear_commitments = { commitment1, commitment2 }; + std::vector multilinear_commitments_to_be_shifted = {}; + + this->execute_gemini_and_verify_claims(log_n, + u, + multilinear_evaluations, + multilinear_polynomials, + multilinear_polynomials_to_be_shifted, + multilinear_commitments, + multilinear_commitments_to_be_shifted); } -TYPED_TEST(GeminiTest, double_shift) +TYPED_TEST(GeminiTest, DoubleWithShift) { - using Gemini = MultilinearReductionScheme; + // using Gemini = MultilinearReductionScheme; using Fr = typename TypeParam::Fr; - using MLEOpeningClaim = MLEOpeningClaim; + using Commitment = typename TypeParam::Commitment; const size_t n = 16; const size_t log_n = 4; @@ -132,7 +203,7 @@ TYPED_TEST(GeminiTest, double_shift) auto poly1 = this->random_polynomial(n); auto poly2 = this->random_polynomial(n); - poly2[0] = Fr::zero(); + poly2[0] = Fr::zero(); // necessary for polynomial to be 'shiftable' auto commitment1 = this->commit(poly1); auto commitment2 = this->commit(poly2); @@ -141,28 +212,20 @@ TYPED_TEST(GeminiTest, double_shift) auto eval2 = poly2.evaluate_mle(u); auto eval2_shift = poly2.evaluate_mle(u, true); - auto claims = { - MLEOpeningClaim{ commitment1, eval1 }, - MLEOpeningClaim{ commitment2, eval2 }, - }; - - auto claims_shift = { - MLEOpeningClaim{ commitment2, eval2_shift }, - }; - - using Transcript = transcript::StandardTranscript; - auto transcript = std::make_shared(StandardHonk::create_unrolled_manifest(0, log_n)); - - transcript->mock_inputs_prior_to_challenge("rho"); - - auto [prover_claim, witness, proof] = - Gemini::reduce_prove(this->ck(), u, claims, claims_shift, { poly1, poly2 }, { poly2 }, transcript); - - this->verify_batch_opening_claim(prover_claim, witness); - - auto verifier_claim = Gemini::reduce_verify(u, claims, claims_shift, proof, transcript); - - ASSERT_EQ(prover_claim, verifier_claim); + // Collect multilinear polynomials evaluations, and commitments for input to prover/verifier + std::vector multilinear_evaluations = { eval1, eval2, eval2_shift }; + std::vector> multilinear_polynomials = { poly1, poly2 }; + std::vector> multilinear_polynomials_to_be_shifted = { poly2 }; + std::vector multilinear_commitments = { commitment1, commitment2 }; + std::vector multilinear_commitments_to_be_shifted = { commitment2 }; + + this->execute_gemini_and_verify_claims(log_n, + u, + multilinear_evaluations, + multilinear_polynomials, + multilinear_polynomials_to_be_shifted, + multilinear_commitments, + multilinear_commitments_to_be_shifted); } } // namespace honk::pcs::gemini \ No newline at end of file diff --git a/barretenberg/cpp/src/aztec/honk/pcs/kzg/kzg.hpp b/barretenberg/cpp/src/aztec/honk/pcs/kzg/kzg.hpp index 401286adfab5..3915c1a93b3f 100644 --- a/barretenberg/cpp/src/aztec/honk/pcs/kzg/kzg.hpp +++ b/barretenberg/cpp/src/aztec/honk/pcs/kzg/kzg.hpp @@ -28,7 +28,8 @@ template class BilinearAccumulator { * @param proof a Commitment π */ BilinearAccumulator(const OpeningClaim& claim, const Commitment& proof) - : lhs(claim.commitment - (Commitment::one() * claim.eval) + (proof * claim.opening_point)) + : lhs(claim.commitment - (Commitment::one() * claim.opening_pair.evaluation) + + (proof * claim.opening_pair.query)) , rhs(-proof) {} @@ -38,7 +39,7 @@ template class BilinearAccumulator { * @param vk VerificationKey * @return e(P₀,[1]₁)e(P₁,[x]₂)≡ [1]ₜ */ - bool verify(VK* vk) const { return vk->pairing_check(lhs, rhs); }; + bool verify(std::shared_ptr vk) const { return vk->pairing_check(lhs, rhs); }; bool operator==(const BilinearAccumulator& other) const = default; @@ -50,44 +51,41 @@ template class UnivariateOpeningScheme { using Fr = typename Params::Fr; using Commitment = typename Params::Commitment; + using CommitmentAffine = typename Params::C; using Polynomial = barretenberg::Polynomial; public: using Accumulator = BilinearAccumulator; - using Proof = Commitment; - - struct Output { - Accumulator accumulator; - Proof proof; - }; /** - * @brief Compute an accumulator for a single polynomial commitment opening claim + * @brief Compute KZG opening proof (commitment) and add it to transcript * * @param ck CommitmentKey - * @param claim OpeningClaim = (C,r,v) - * @param polynomial the witness polynomial for C - * @return Output{Accumulator, Proof} + * @param opening_pair OpeningPair = {r, v = polynomial(r)} + * @param polynomial the witness polynomial being opened */ - static Output reduce_prove(std::shared_ptr ck, const OpeningClaim& claim, const Polynomial& polynomial) + static void reduce_prove(std::shared_ptr ck, + const OpeningPair& opening_pair, + const Polynomial& polynomial, + const auto& transcript) { Polynomial quotient(polynomial); - quotient[0] -= claim.eval; - quotient.factor_roots(claim.opening_point); - Proof proof = ck->commit(quotient); + quotient[0] -= opening_pair.evaluation; + quotient.factor_roots(opening_pair.query); + Commitment proof = ck->commit(quotient); - return Output{ Accumulator(claim, proof), proof }; + transcript->add_element("W", static_cast(proof).to_buffer()); }; /** * @brief Computes the accumulator for a single polynomial commitment opening claim * This reduction is non-interactive and always succeeds. * - * @param claim OpeningClaim (C,r,v) - * @param proof a commitment to Q(X) = ( P(X) - v )/( X - r) - * @return Accumulator + * @param claim OpeningClaim ({r, v}, C) + * @param proof π, a commitment to Q(X) = ( P(X) - v )/( X - r) + * @return Accumulator {C − v⋅[1]₁ + r⋅π, −π} */ - static Accumulator reduce_verify(const OpeningClaim& claim, const Proof& proof) + static Accumulator reduce_verify(const OpeningClaim& claim, const Commitment& proof) { return Accumulator(claim, proof); }; diff --git a/barretenberg/cpp/src/aztec/honk/pcs/kzg/kzg.test.cpp b/barretenberg/cpp/src/aztec/honk/pcs/kzg/kzg.test.cpp index eed6ee3f6007..a9ab989ea026 100644 --- a/barretenberg/cpp/src/aztec/honk/pcs/kzg/kzg.test.cpp +++ b/barretenberg/cpp/src/aztec/honk/pcs/kzg/kzg.test.cpp @@ -4,6 +4,7 @@ #include "../gemini/gemini.hpp" #include "../commitment_key.test.hpp" +#include "honk/pcs/claim.hpp" #include "honk/pcs/commitment_key.hpp" #include "polynomials/polynomial.hpp" @@ -17,17 +18,8 @@ namespace honk::pcs::kzg { template class BilinearAccumulationTest : public CommitmentTest { public: using Fr = typename Params::Fr; - using Commitment = typename Params::Commitment; using Polynomial = barretenberg::Polynomial; - - using Accumulator = BilinearAccumulator; - - void verify_accumulators(const Accumulator& prover_acc, const Accumulator& verifier_acc) - { - EXPECT_EQ(prover_acc, verifier_acc) << "BilinearAccumulation: accumulator mismatch"; - EXPECT_TRUE(prover_acc.verify(this->vk())) << "BilinearAccumulation: pairing check failed"; - } }; TYPED_TEST_SUITE(BilinearAccumulationTest, CommitmentSchemeParams); @@ -35,27 +27,40 @@ TYPED_TEST_SUITE(BilinearAccumulationTest, CommitmentSchemeParams); TYPED_TEST(BilinearAccumulationTest, single) { const size_t n = 16; + const size_t log_n = 4; - using OpeningScheme = UnivariateOpeningScheme; + using KZG = UnivariateOpeningScheme; + using Fr = typename TypeParam::Fr; - auto [claim, witness] = this->random_claim(n); + // Instantiate a transcript from the real Honk manifest, then mock the inputs prior to Gemini. + using Transcript = transcript::StandardTranscript; + auto transcript = std::make_shared(StandardHonk::create_unrolled_manifest(0, log_n)); + transcript->mock_inputs_prior_to_challenge("z"); - auto [acc, proof] = OpeningScheme::reduce_prove(this->ck(), claim, witness); - auto result_acc = OpeningScheme::reduce_verify(claim, proof); + auto witness = this->random_polynomial(n); + auto commitment = this->commit(witness); + auto query = Fr::random_element(); + auto evaluation = witness.evaluate(query); + auto opening_pair = OpeningPair{ query, evaluation }; - this->verify_accumulators(acc, result_acc); + KZG::reduce_prove(this->ck(), opening_pair, witness, transcript); + + // Reconstruct the KZG Proof (commitment [W]) from the transcript + auto kzg_proof = transcript->get_group_element("W"); + + auto opening_claim = OpeningClaim{ opening_pair, commitment }; + + auto kzg_claim = KZG::reduce_verify(opening_claim, kzg_proof); + + bool verified = kzg_claim.verify(this->vk()); + + EXPECT_EQ(verified, true); } /** * @brief Test full PCS protocol: Gemini, Shplonk, KZG and pairing check - * @details This test serves two purposes: - * (1) Demonstrate the full PCS protocol as it is used in the construction and verification + * @details Demonstrates the full PCS protocol as it is used in the construction and verification * of a single Honk proof. (Expository comments included throughout). - * (2) Demonstrate that proof construction/verification does not require the prover to pass - * genuine claims to the PCS. (This is relevant since in practice the prover does not have - * access to, for example, commitments to non-witness polynomials). The prover must provide - * only the multivariate polynomials and their genuine evaluations to the PCS, not commitments - * to those polynomials. * */ TYPED_TEST(BilinearAccumulationTest, GeminiShplonkKzgWithShift) @@ -63,8 +68,10 @@ TYPED_TEST(BilinearAccumulationTest, GeminiShplonkKzgWithShift) using Transcript = transcript::StandardTranscript; using Shplonk = shplonk::SingleBatchOpeningScheme; using Gemini = gemini::MultilinearReductionScheme; - using MLEOpeningClaim = MLEOpeningClaim; - using OpeningScheme = UnivariateOpeningScheme; + using KZG = UnivariateOpeningScheme; + using Fr = typename TypeParam::Fr; + using Commitment = typename TypeParam::Commitment; + using Polynomial = typename barretenberg::Polynomial; const size_t n = 16; const size_t log_n = 4; @@ -72,6 +79,8 @@ TYPED_TEST(BilinearAccumulationTest, GeminiShplonkKzgWithShift) // Instantiate a transcript from the real Honk manifest, then mock the inputs prior to Gemini. auto transcript = std::make_shared(StandardHonk::create_unrolled_manifest(0, log_n)); transcript->mock_inputs_prior_to_challenge("rho"); + transcript->apply_fiat_shamir("rho"); + const Fr rho = Fr::serialize_from_buffer(transcript->get_challenge("rho").begin()); // Generate multilinear polynomials, their commitments (genuine and mocked) and evaluations (genuine) at a random // point. @@ -87,142 +96,76 @@ TYPED_TEST(BilinearAccumulationTest, GeminiShplonkKzgWithShift) auto eval2 = poly2.evaluate_mle(mle_opening_point); auto eval2_shift = poly2.evaluate_mle(mle_opening_point, true); - const auto mock_commitment = Params::C::one(); + // Collect multilinear evaluations for input to prover + std::vector multilinear_evaluations = { eval1, eval2, eval2_shift }; - std::vector claims; - std::vector claims_shift; - std::vector claims_mock; - std::vector claims_shift_mock; - std::vector> multivariate_polynomials; - std::vector> multivariate_polynomials_shifted; + std::vector rhos = Gemini::powers_of_rho(rho, multilinear_evaluations.size()); - // Create genuine opening claims (for use by verifier) and mock opening claims (for prover) - claims.emplace_back(commitment1, eval1); - claims.emplace_back(commitment2, eval2); - claims_shift.emplace_back(commitment2, eval2_shift); + // Compute batched multivariate evaluation + Fr batched_evaluation = Fr::zero(); + for (size_t i = 0; i < rhos.size(); ++i) { + batched_evaluation += multilinear_evaluations[i] * rhos[i]; + } - claims_mock.emplace_back(mock_commitment, eval1); - claims_mock.emplace_back(mock_commitment, eval2); - claims_shift_mock.emplace_back(mock_commitment, eval2_shift); + // Compute batched polynomials + Polynomial batched_unshifted(n); + Polynomial batched_to_be_shifted(n); + batched_unshifted.add_scaled(poly1, rhos[0]); + batched_unshifted.add_scaled(poly2, rhos[1]); + batched_to_be_shifted.add_scaled(poly2, rhos[2]); - multivariate_polynomials.emplace_back(poly1); - multivariate_polynomials.emplace_back(poly2); - multivariate_polynomials_shifted.emplace_back(poly2); + // Compute batched commitments + Commitment batched_commitment_unshifted = Commitment::zero(); + Commitment batched_commitment_to_be_shifted = Commitment::zero(); + batched_commitment_unshifted = commitment1 * rhos[0] + commitment2 * rhos[1]; + batched_commitment_to_be_shifted = commitment2 * rhos[2]; - // Run the full prover PCS protocol with mocked opening claims (mocked commitment, genuine evaluation) + // Run the full prover PCS protocol: // Gemini prover output: - // - claim: junk commitments, d+1 genuine evaluations a_0_pos, a_l, l = 0:d-1 + // - opening pairs: d+1 pairs (r, a_0_pos) and (-r^{2^l}, a_l), l = 0:d-1 // - witness: the d+1 polynomials Fold_{r}^(0), Fold_{-r}^(0), Fold^(l), l = 1:d-1 - // - proof: d-1 commitments [Fold^(l)], l = 1:d-1 and d evaluations a_l, l = 0:d-1 - const auto [gemini_prover_claim, gemini_witness, gemini_proof] = - Gemini::reduce_prove(this->ck(), - mle_opening_point, - claims_mock, - claims_shift_mock, - multivariate_polynomials, - multivariate_polynomials_shifted, - transcript); + auto gemini_prover_output = Gemini::reduce_prove( + this->ck(), mle_opening_point, std::move(batched_unshifted), std::move(batched_to_be_shifted), transcript); // Shplonk prover output: - // - claim: junk commitment, evaluation point = zero + // - opening pair: (z_challenge, 0) // - witness: polynomial Q - Q_z - // - proof: commitment [Q] - const auto [shplonk_prover_claim, shplonk_witness, shplonk_proof] = - Shplonk::reduce_prove(this->ck(), gemini_prover_claim, gemini_witness, transcript); + auto shplonk_prover_output = Shplonk::reduce_prove( + this->ck(), gemini_prover_output.opening_pairs, gemini_prover_output.witnesses, transcript); - // KZG prover output: - // - proof: commitment [W] - auto [kzg_accum, kzg_proof] = OpeningScheme::reduce_prove(this->ck(), shplonk_prover_claim, shplonk_witness); + // KZG prover: + // - Adds commitment [W] to transcript + KZG::reduce_prove(this->ck(), shplonk_prover_output.opening_pair, shplonk_prover_output.witness, transcript); // Run the full verifier PCS protocol with genuine opening claims (genuine commitment, genuine evaluation) - // 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 gemini_verifier_claim = - Gemini::reduce_verify(mle_opening_point, claims, claims_shift, gemini_proof, transcript); - - // Shplonk verifier output: - // - claim: commitment [Q] - [Q_z], evaluation zero (at random challenge z) - const auto shplonk_verifier_claim = Shplonk::reduce_verify(gemini_verifier_claim, shplonk_proof, transcript); - - // KZG verifier output: - // - just aggregates inputs [Q] - [Q_z] and [W] into an 'accumulator' (can perform pairing check on result) - auto kzg_claim = OpeningScheme::reduce_verify(shplonk_verifier_claim, kzg_proof); - - // Final pairing check: e([Q] - [Q_z] + z[W], [1]_2) = e([W], [x]_2) - bool verified = kzg_claim.verify(this->vk()); - - EXPECT_EQ(verified, true); -} - -TYPED_TEST(BilinearAccumulationTest, GeminiShplonkKzgSimple) -{ - using Transcript = transcript::StandardTranscript; - using Shplonk = shplonk::SingleBatchOpeningScheme; - using Gemini = gemini::MultilinearReductionScheme; - using MLEOpeningClaim = MLEOpeningClaim; - using OpeningScheme = UnivariateOpeningScheme; - - const size_t n = 16; - const size_t log_n = 4; - - // Instantiate a transcript from the real Honk manifest, then mock the inputs prior to Gemini. - auto transcript = std::make_shared(StandardHonk::create_unrolled_manifest(0, log_n)); - transcript->mock_inputs_prior_to_challenge("rho"); - - // Generate multilinear polynomials, their commitments (genuine and mocked) and evaluations (genuine) at a random - // point. - const auto mle_opening_point = this->random_evaluation_point(log_n); // sometimes denoted 'u' - auto poly1 = this->random_polynomial(n); - - auto commitment1 = this->commit(poly1); - - auto eval1 = poly1.evaluate_mle(mle_opening_point); - - const auto mock_commitment = Params::C::one(); + // Construct a Gemini proof object consisting of + // - d Fold poly evaluations a_0, ..., a_{d-1} + // - (d-1) Fold polynomial commitments [Fold^(1)], ..., [Fold^(d-1)] + auto gemini_proof = Gemini::reconstruct_proof_from_transcript(transcript, log_n); - std::vector claims; - std::vector claims_mock; - std::vector> multivariate_polynomials; + // Reconstruct the Shplonk Proof (commitment [Q]) from the transcript + auto shplonk_proof = transcript->get_group_element("Q"); - claims.emplace_back(commitment1, eval1); - claims_mock.emplace_back(mock_commitment, eval1); - multivariate_polynomials.emplace_back(poly1); - - // Run the full prover PCS protocol with mocked opening claims (mocked commitment, genuine evaluation) - - // Gemini prover output: - // - claim: junk commitments, d+1 genuine evaluations a_0_pos, a_l, l = 0:d-1 - // - witness: the d+1 polynomials Fold_{r}^(0), Fold_{-r}^(0), Fold^(l), l = 1:d-1 - // - proof: d-1 commitments [Fold^(l)], l = 1:d-1 and d evaluations a_l, l = 0:d-1 - const auto [gemini_prover_claim, gemini_witness, gemini_proof] = - Gemini::reduce_prove(this->ck(), mle_opening_point, claims_mock, {}, multivariate_polynomials, {}, transcript); - - // Shplonk prover output: - // - claim: junk commitment, evaluation point = zero - // - witness: polynomial Q - Q_z - // - proof: commitment [Q] - const auto [shplonk_prover_claim, shplonk_witness, shplonk_proof] = - Shplonk::reduce_prove(this->ck(), gemini_prover_claim, gemini_witness, transcript); - - // KZG prover output: - // - proof: commitment [W] - auto [kzg_accum, kzg_proof] = OpeningScheme::reduce_prove(this->ck(), shplonk_prover_claim, shplonk_witness); - - // Run the full verifier PCS protocol with genuine opening claims (genuine commitment, genuine evaluation) + // Reconstruct the KZG Proof (commitment [W]) from the transcript + auto kzg_proof = transcript->get_group_element("W"); // 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 gemini_verifier_claim = Gemini::reduce_verify(mle_opening_point, claims, {}, gemini_proof, transcript); - - // Shplonk verifier output: - // - claim: commitment [Q] - [Q_z], evaluation zero (at random challenge z) + auto gemini_verifier_claim = Gemini::reduce_verify(mle_opening_point, + batched_evaluation, + batched_commitment_unshifted, + batched_commitment_to_be_shifted, + gemini_proof, + transcript); + + // Shplonk verifier claim: commitment [Q] - [Q_z], opening point (z_challenge, 0) const auto shplonk_verifier_claim = Shplonk::reduce_verify(gemini_verifier_claim, shplonk_proof, transcript); - // KZG verifier output: - // - just aggregates inputs [Q] - [Q_z] and [W] into an 'accumulator' (can perform pairing check on result) - auto kzg_claim = OpeningScheme::reduce_verify(shplonk_verifier_claim, kzg_proof); + // KZG verifier: + // aggregates inputs [Q] - [Q_z] and [W] into an 'accumulator' (can perform pairing check on result) + auto kzg_claim = KZG::reduce_verify(shplonk_verifier_claim, kzg_proof); // Final pairing check: e([Q] - [Q_z] + z[W], [1]_2) = e([W], [x]_2) bool verified = kzg_claim.verify(this->vk()); diff --git a/barretenberg/cpp/src/aztec/honk/pcs/shplonk/shplonk.hpp b/barretenberg/cpp/src/aztec/honk/pcs/shplonk/shplonk.hpp index e3bee20f817b..c40d684a1fec 100644 --- a/barretenberg/cpp/src/aztec/honk/pcs/shplonk/shplonk.hpp +++ b/barretenberg/cpp/src/aztec/honk/pcs/shplonk/shplonk.hpp @@ -25,13 +25,6 @@ namespace honk::pcs::shplonk { */ template using Proof = typename Params::Commitment; -/** - * @brief Single opening claim ([G], r, 0) so that G(r) = 0 - * - * @tparam Params CommitmentScheme parameters - */ -template using OutputClaim = OpeningClaim; - /** * @brief Polynomial G(X) = Q(X) - ∑ₖ ẑₖ(r)⋅( Bₖ(X) − Tₖ(z) ) * @@ -46,9 +39,8 @@ template using OutputWitness = barretenberg::Polynomial struct ProverOutput { - OutputClaim claim; - OutputWitness witness; - Proof proof; + OpeningPair opening_pair; // single opening pair (query, G(query) = 0) + OutputWitness witness; // single polynomial G(X) }; } // namespace honk::pcs::shplonk \ No newline at end of file diff --git a/barretenberg/cpp/src/aztec/honk/pcs/shplonk/shplonk.test.cpp b/barretenberg/cpp/src/aztec/honk/pcs/shplonk/shplonk.test.cpp index 658a68222530..ef25cbae4e89 100644 --- a/barretenberg/cpp/src/aztec/honk/pcs/shplonk/shplonk.test.cpp +++ b/barretenberg/cpp/src/aztec/honk/pcs/shplonk/shplonk.test.cpp @@ -1,4 +1,3 @@ -#include "shplonk_multi.hpp" #include "shplonk_single.hpp" #include "../gemini/gemini.hpp" @@ -11,172 +10,73 @@ #include "honk/pcs/claim.hpp" #include "polynomials/polynomial.hpp" namespace honk::pcs::shplonk { -template class ShplonkTest : public CommitmentTest { - using Base = CommitmentTest; - - using Fr = typename Params::Fr; - - using Commitment = typename Params::Commitment; - using Polynomial = barretenberg::Polynomial; - - public: - std::vector random_opening_set(const size_t m) - { - std::vector opening_set(m); - for (size_t i = 0; i < m; ++i) { - auto x = this->random_element(); - opening_set[i] = x; - } - return opening_set; - } - - void add_random_batch_opening_sub_claims(std::vector>& multi_claims, - std::vector& polys, - const std::vector& queries, - std::span poly_sizes) - { - using Opening = typename MultiOpeningClaim::Opening; - auto& new_claim = multi_claims.emplace_back(MultiOpeningClaim{ queries, {} }); - - for (const size_t poly_size : poly_sizes) { - const auto& p = polys.emplace_back(Base::random_polynomial(poly_size)); - const auto& c = Base::commit(p); - std::vector evals; - for (const auto query : queries) { - evals.push_back(p.evaluate(query)); - } - new_claim.openings.emplace_back(Opening{ c, evals }); - } - }; -}; +template class ShplonkTest : public CommitmentTest {}; TYPED_TEST_SUITE(ShplonkTest, CommitmentSchemeParams); -TYPED_TEST(ShplonkTest, single_poly_two_points) +// Test of Shplonk prover/verifier using real Gemini claim +TYPED_TEST(ShplonkTest, GeminiShplonk) { - using Shplonk = MultiBatchOpeningScheme; - using MultiOpeningClaim = MultiOpeningClaim; + using Shplonk = SingleBatchOpeningScheme; + using Gemini = gemini::MultilinearReductionScheme; using Fr = typename TypeParam::Fr; - using Polynomial = barretenberg::Polynomial; - constexpr size_t n = 16; - const size_t log_n = 4; - - auto queries = this->random_opening_set(2); - std::vector claims; - std::vector polys; - - this->add_random_batch_opening_sub_claims(claims, polys, queries, std::array{ n }); - - using Transcript = transcript::StandardTranscript; - auto transcript = std::make_shared(StandardHonk::create_unrolled_manifest(0, log_n)); - - transcript->mock_inputs_prior_to_challenge("nu"); - - const auto [prover_claim, witness, proof] = Shplonk::reduce_prove(this->ck(), claims, polys, transcript); + using Commitment = typename TypeParam::Commitment; + using Polynomial = typename barretenberg::Polynomial; - this->verify_opening_claim(prover_claim, witness); - const auto verifier_claim = Shplonk::reduce_verify(claims, proof, transcript); - EXPECT_EQ(prover_claim, verifier_claim); - this->verify_opening_claim(prover_claim, witness); -} - -TYPED_TEST(ShplonkTest, two_polys_different_size_at_two_different_points) -{ - using Shplonk = MultiBatchOpeningScheme; - using MultiOpeningClaim = MultiOpeningClaim; - using Fr = typename TypeParam::Fr; - using Polynomial = barretenberg::Polynomial; const size_t n = 16; const size_t log_n = 4; - std::vector claims; - std::vector polys; - - auto queries = this->random_opening_set(2); - - this->add_random_batch_opening_sub_claims(claims, polys, { queries[0] }, std::array{ n }); - this->add_random_batch_opening_sub_claims(claims, polys, { queries[1] }, std::array{ n - 1 }); - using Transcript = transcript::StandardTranscript; auto transcript = std::make_shared(StandardHonk::create_unrolled_manifest(0, log_n)); - - transcript->mock_inputs_prior_to_challenge("nu"); - - const auto [prover_claim, witness, proof] = Shplonk::reduce_prove(this->ck(), claims, polys, transcript); - - this->verify_opening_claim(prover_claim, witness); - const auto verifier_claim = Shplonk::reduce_verify(claims, proof, transcript); - EXPECT_EQ(prover_claim, verifier_claim); - this->verify_opening_claim(prover_claim, witness); -} - -TYPED_TEST(ShplonkTest, three_polys_different_sizes_and_different_queries) -{ - using Shplonk = MultiBatchOpeningScheme; - using MultiOpeningClaim = MultiOpeningClaim; - using Fr = typename TypeParam::Fr; - using Polynomial = barretenberg::Polynomial; - const size_t n = 16; - const size_t log_n = 4; - - std::vector claims; - std::vector polys; - - auto queries = this->random_opening_set(3); - - this->add_random_batch_opening_sub_claims(claims, polys, { queries[0] }, std::array{ n }); - this->add_random_batch_opening_sub_claims(claims, polys, { queries[1], queries[2] }, std::array{ n - 1, n + 2 }); - this->add_random_batch_opening_sub_claims(claims, polys, { queries[0], queries[2] }, std::array{ n }); - - using Transcript = transcript::StandardTranscript; - auto transcript = std::make_shared(StandardHonk::create_unrolled_manifest(0, log_n)); - - transcript->mock_inputs_prior_to_challenge("nu"); - - const auto [prover_claim, witness, proof] = Shplonk::reduce_prove(this->ck(), claims, polys, transcript); - - this->verify_opening_claim(prover_claim, witness); - const auto verifier_claim = Shplonk::reduce_verify(claims, proof, transcript); - EXPECT_EQ(prover_claim, verifier_claim); - this->verify_opening_claim(prover_claim, witness); -} - -// Test of Shplonk prover/verifier using real Gemini claim -TYPED_TEST(ShplonkTest, Gemini) -{ - using Shplonk = SingleBatchOpeningScheme; - using Gemini = gemini::MultilinearReductionScheme; - using MLEOpeningClaim = MLEOpeningClaim; - - const size_t n = 16; - const size_t log_n = 4; + transcript->mock_inputs_prior_to_challenge("rho"); + transcript->apply_fiat_shamir("rho"); + const Fr rho = Fr::serialize_from_buffer(transcript->get_challenge("rho").begin()); const auto u = this->random_evaluation_point(log_n); auto poly = this->random_polynomial(n); const auto commitment = this->commit(poly); const auto eval = poly.evaluate_mle(u); - // create opening claim - const auto claims = { MLEOpeningClaim{ commitment, eval } }; + // Collect multilinear polynomials evaluations, and commitments for input to prover/verifier + std::vector multilinear_evaluations = { eval }; - using Transcript = transcript::StandardTranscript; - auto transcript = std::make_shared(StandardHonk::create_unrolled_manifest(0, log_n)); + std::vector rhos = Gemini::powers_of_rho(rho, multilinear_evaluations.size()); - transcript->mock_inputs_prior_to_challenge("rho"); + // Compute batched multivariate evaluation + Fr batched_evaluation = multilinear_evaluations[0] * rhos[0]; + + Polynomial batched_unshifted(n); + Polynomial batched_to_be_shifted(n); + batched_unshifted.add_scaled(poly, rhos[0]); + + Commitment batched_commitment_unshifted = commitment * rhos[0]; + Commitment batched_commitment_to_be_shifted = Commitment::zero(); + + auto gemini_prover_output = + Gemini::reduce_prove(this->ck(), u, std::move(batched_unshifted), std::move(batched_to_be_shifted), transcript); + + const auto [prover_opening_pair, shplonk_prover_witness] = Shplonk::reduce_prove( + this->ck(), gemini_prover_output.opening_pairs, gemini_prover_output.witnesses, transcript); - const auto [gemini_prover_claim, gemini_witness, gemini_proof] = - Gemini::reduce_prove(this->ck(), u, claims, {}, { poly }, {}, transcript); + this->verify_opening_pair(prover_opening_pair, shplonk_prover_witness); - const auto gemini_verifier_claim = Gemini::reduce_verify(u, claims, {}, gemini_proof, transcript); + // Reconstruct a Gemini proof object consisting of + // - d Fold poly evaluations a_0, ..., a_{d-1} + // - (d-1) Fold polynomial commitments [Fold^(1)], ..., [Fold^(d-1)] + auto gemini_proof = Gemini::reconstruct_proof_from_transcript(transcript, log_n); - EXPECT_EQ(gemini_prover_claim, gemini_verifier_claim); + auto gemini_verifier_claim = Gemini::reduce_verify(u, + batched_evaluation, + batched_commitment_unshifted, + batched_commitment_to_be_shifted, + gemini_proof, + transcript); - const auto [prover_claim, witness, proof] = - Shplonk::reduce_prove(this->ck(), gemini_prover_claim, gemini_witness, transcript); + // Reconstruct the Shplonk Proof (commitment [Q]) from the transcript + auto shplonk_proof = transcript->get_group_element("Q"); - this->verify_opening_claim(prover_claim, witness); + const auto verifier_claim = Shplonk::reduce_verify(gemini_verifier_claim, shplonk_proof, transcript); - const auto verifier_claim = Shplonk::reduce_verify(gemini_prover_claim, proof, transcript); - EXPECT_EQ(prover_claim, verifier_claim); + this->verify_opening_claim(verifier_claim, shplonk_prover_witness); } } // namespace honk::pcs::shplonk \ No newline at end of file diff --git a/barretenberg/cpp/src/aztec/honk/pcs/shplonk/shplonk_multi.hpp b/barretenberg/cpp/src/aztec/honk/pcs/shplonk/shplonk_multi.hpp deleted file mode 100644 index 21e771d82eb6..000000000000 --- a/barretenberg/cpp/src/aztec/honk/pcs/shplonk/shplonk_multi.hpp +++ /dev/null @@ -1,275 +0,0 @@ -#pragma once -#include "shplonk.hpp" -#include "honk/pcs/commitment_key.hpp" - -namespace honk::pcs::shplonk { - -/** - * @brief Open several polynomials, each at a different set of points. - * Reduces to opening a single univariate at a single point. - * - * @tparam Params for the given commitment scheme - */ -template class MultiBatchOpeningScheme { - using CK = typename Params::CK; - - using Fr = typename Params::Fr; - using Commitment = typename Params::Commitment; - using Polynomial = barretenberg::Polynomial; - - public: - /** - * @brief Batches a list of 'MultiOpeningClaim' into a single 'OpeningClaim' suitable for an polynomial opening - * scheme. - * - * @param ck CommitmentKey - * @param multi_claims a set of commitments and evaluation queries claimed - * @param witness_polynomials witnesses corresponsing to the commitments in 'batch_claim'. - * Note that the order of the polynomials must follow the order of the commitments - * in 'batch_claim'. - * @param transcript - * @return Output{OpeningClaim, WitnessPolynomial, Proof} - */ - static ProverOutput reduce_prove(std::shared_ptr ck, - std::span> multi_claims, - std::span witness_polynomials, - const auto& transcript) - { - transcript->apply_fiat_shamir("nu"); - Fr nu = Fr::serialize_from_buffer(transcript->get_challenge("nu").begin()); - - const size_t num_multi_claims = multi_claims.size(); - - // allocate the merged polynomials Bₖ(X) and compute them - // while we could simply allocate vectors of the same size, - // for Gemini we know that the sizes are powers of 2, so we'd - // be allocating nlogn space instead of 2n - std::vector merged_polynomials; - merged_polynomials.reserve(num_multi_claims); - size_t max_poly_size{ 0 }; - - // allocate polynomial Tₖ(X) interpolating Yₖ over Ωₖ, where - // Yₖ = {yᵏ₁, …, yᵏₘₖ}, where yᵏᵢ = ∑ⱼ ρʲ⋅yʲᵢ for all i in 1,…,mₖ - // is the random linear combination of all evaluations openened at the same opening - // set Ωₖ - std::vector interpolated_polynomials; - interpolated_polynomials.reserve(num_multi_claims); - - // initialize the {Cₖ} - // we will then set them to Cₖ = ∑ⱼ ρʲ⋅Cⱼ for all commitments Cⱼ - // belonging to the k-th opening set. - std::vector merged_commitments; - merged_commitments.reserve(num_multi_claims); - - // keep track of number of polynomials we have mergeds - size_t current_poly_idx = 0; - - Fr current_nu = Fr::one(); - // iterate over all claims, grouped by common opening set - for (const auto& [queries_k, openings_k] : multi_claims) { - const size_t num_queries_k = queries_k.size(); - const size_t num_openings_k = openings_k.size(); - - // get the max size of each polynomial being opened at Ωₖ - size_t merged_poly_size{ 0 }; - for (size_t sub_claim_idx = 0; sub_claim_idx < num_openings_k; sub_claim_idx++) { - merged_poly_size = - std::max(merged_poly_size, witness_polynomials[current_poly_idx + sub_claim_idx].size()); - } - - // we loop again, this time actully computing the linear combination of - // - Bₖ(X) = ∑ⱼ ρʲ⋅pⱼ(X) - // - Cₖ = ∑ⱼ ρʲ⋅Cⱼ - // - Yₖ = {yᵏ₁, …, yᵏₘₖ}, where yᵏᵢ = ∑ⱼ ρʲ⋅yʲᵢ - // create an empty polynomial in which we merge all polynomials openened at the same claim - auto& B_k = merged_polynomials.emplace_back(merged_poly_size); - auto& C_k = merged_commitments.emplace_back(Commitment::zero()); - std::vector evals_k(num_queries_k, Fr::zero()); - - for (const auto& [C_j, evals_j] : openings_k) { - // add ρʲ⋅pⱼ to merged polynomial Bₖ(X) - B_k.add_scaled(witness_polynomials[current_poly_idx], current_nu); - // add ρʲ⋅Cⱼ to the merged commitment Cₖ - C_k = C_k + (C_j * current_nu); - for (size_t i = 0; i < num_queries_k; ++i) { - // add ρʲ⋅yʲᵢ to yᵏᵢ - evals_k[i] += current_nu * evals_j[i]; - } - - current_nu *= nu; - current_poly_idx++; - } - // compute the interpolation polynomial Tₖ(X) of Yₖ over Ωₖ. - // for each xᵏᵢ in Ωₖ, we have yᵏᵢ = Bₖ(xᵏᵢ) = Tₖ(xᵏᵢ), - interpolated_polynomials.emplace_back(Polynomial(queries_k, evals_k)); - - // we update the max size across all polys - max_poly_size = std::max(merged_poly_size, max_poly_size); - } - - // initialize Q(X) = 0 - Polynomial Q(max_poly_size); - Polynomial tmp(max_poly_size); - for (size_t k = 0; k < num_multi_claims; ++k) { - // Bₖ(X) into temp_poly - tmp = merged_polynomials[k]; - // subtract Bₖ(X) - Tₖ(X) in-place - tmp -= interpolated_polynomials[k]; - - // compute ( Bₖ(X) - Tₖ(X) ) / zₖ(X) - tmp.factor_roots(multi_claims[k].queries); - Q += tmp; - } - - // commit to Q(X) and add [Q] to the transcript - Commitment Q_commitment = ck->commit(Q); - transcript->add_element("Q", static_cast(Q_commitment).to_buffer()); - - // generate random evaluation challenge zeta_challenge - transcript->apply_fiat_shamir("z"); - const Fr zeta_challenge = Fr::serialize_from_buffer(transcript->get_challenge("z").begin()); - - // reuse the quotient polynomial Q(X) - // G(X) = Q(X) - Polynomial& G = Q; - - // {ẑₖ(r)}ₖ , where ẑₖ(r) = 1/zₖ(r) - std::vector inverse_vanishing_evals; - { - for (const auto& [queries_k, openings_k] : multi_claims) { - Fr eval{ Fr::one() }; - for (const Fr& x : queries_k) { - eval *= (zeta_challenge - x); - } - inverse_vanishing_evals.emplace_back(eval); - } - Fr::batch_invert(inverse_vanishing_evals); - } - - // evaluations of interpolated polynomials Tₖ(r) - std::vector T_r(num_multi_claims); - - for (size_t k = 0; k < num_multi_claims; ++k) { - // evaluate Tₖ(r) - auto& T_k = interpolated_polynomials[k]; - T_r[k] = T_k.evaluate(zeta_challenge); - - // subtract ( Bₖ(X) − Tₖ(r) )/zₖ(r) from G(X) - merged_polynomials[k][0] -= T_r[k]; - G.add_scaled(merged_polynomials[k], -inverse_vanishing_evals[k]); - } - - // compute simulated commitment to [G] as - // [Q] - ∑ₖ (1/zₖ(r))[Bₖ] + ( ∑ₖ (1/zₖ(r)) Tₖ(r))[1] - Commitment G_commitment = Q_commitment; - { - Fr G_commitment_constant = Fr::zero(); - - for (size_t k = 0; k < num_multi_claims; ++k) { - G_commitment += merged_commitments[k] * (-inverse_vanishing_evals[k]); - G_commitment_constant += T_r[k] * inverse_vanishing_evals[k]; - } - - G_commitment += (Commitment::one() * G_commitment_constant); - } - - return { .claim = OpeningClaim{ .commitment = G_commitment, - .opening_point = zeta_challenge, - .eval = Fr::zero() }, - .witness = std::move(Q), - .proof = Q_commitment }; - }; - - /** - * @brief Recomputes the new claim commitment [G] given the proof and - * the challenge r. No verification happens so this function always succeeds. - * - * @param multi_claims a set of commitments and evaluation queries claimed - * @param proof [Q(X)] - * @param transcript - * @return OpeningClaim - */ - static OpeningClaim reduce_verify(std::span> multi_claims, - const Proof& proof, - const auto& transcript) - { - const Fr nu = Fr::serialize_from_buffer(transcript->get_challenge("nu").begin()); - const Fr zeta_challenge = Fr::serialize_from_buffer(transcript->get_challenge("z").begin()); - - // compute simulated commitment to [G] as - // [Q] - ∑ₖ (1/zₖ(r))[Bₖ] + ( ∑ₖ (1/zₖ(r)) Tₖ(r) )[1] - // = [Q] - ∑ₖ (1/zₖ(r))[Bₖ] + ( ∑ₖ ( ∑ {xᵢ ∈ Ωₖ} yᵢ / ( dᵢ ⋅ (r−xᵢ) ) ) [1] - - // C₀ = ∑ₖ (1/zₖ(r)) Tₖ(r)) - Fr commitment_constant{ Fr::zero() }; - - // [Q] - ∑ₖ (1/zₖ(r)[Bₖ] + C₀[1] - Commitment commitment = Commitment::zero(); - - Fr current_nu{ Fr::one() }; - - // iterate over all queries_k = Ωₖ, and openings_k = [(Cⱼ, Yⱼ)]ⱼ - for (const auto& [queries_k, openings_k] : multi_claims) { - const size_t num_queries = queries_k.size(); - - // merged_evals is a vector Yₖ = {yᵏ₁, …, yᵏₘₖ}, where - // yᵏᵢ = ∑ⱼ ρʲ⋅yʲᵢ for all i in 1,…,mₖ - // for all evaluations at the same opening set Ωₖ - std::vector merged_evals_k(num_queries, Fr::zero()); - // merged_commitment is the linear combination of all commitments - // for polynomials openened at the same opening set Ωₖ. - // Cₖ = ∑ⱼ ρʲ⋅Cⱼ = [Bₖ] - Commitment merged_commitment = Commitment::zero(); - // If we define the merged polynomial Bₖ(X) = ∑ⱼ ρʲ⋅pⱼ(X), then - // - Cₖ = Commit(Bₖ(X)) for each k - // - yᵏᵢ = Bₖ(xᵏᵢ) for all k,i - for (const auto& [C_j, evals_j] : openings_k) { - for (size_t i = 0; i < num_queries; ++i) { - merged_evals_k[i] += current_nu * evals_j[i]; - } - merged_commitment += C_j * current_nu; - current_nu *= nu; - } - - // zₖ = ∏ (r − xᵢ), ranging over xᵢ ∈ Ωₖ. - // It is the vanishing polynomial for the k-th opening set Ωₖ - Fr z_k{ Fr::one() }; - - // We have ẑₖ(r) Tₖ(r) = z(r)⋅( ∑ {xᵢ ∈ Ωₖ} yᵢ / ( dᵢ ⋅ (r−xᵢ) ) ) - // where dᵢ = ∏ { j ≠ i} (xᵢ − xⱼ) - // so we compute ∑ {xᵢ ∈ Ωₖ} yᵢ / ( dᵢ ⋅ (r−xᵢ) ) for each claim - // constant_k is the running sum computing the above - Fr constant_k{ Fr::zero() }; - for (size_t i = 0; i < num_queries; ++i) { - Fr x_i{ queries_k[i] }; - Fr y_i{ merged_evals_k[i] }; - z_k *= (zeta_challenge - x_i); - - // dᵢ = ∏ { j ≠ i} (xᵢ − xⱼ) - Fr d_i{ Fr::one() }; - for (const auto x_j : queries_k) { - if (x_i != x_j) { - d_i *= (x_i - x_j); - } else { - d_i *= (zeta_challenge - x_i); - } - } - // NOTE: these inversions are expensive, but this method aims to mimick an - // implementation inside a circuit. - // moreover, the number of claims is usually small, so this shouldn't be a - // big performance hit. - constant_k += (y_i / d_i); - } - // add (1/zₖ(r)) Tₖ(r) to the constant term C₀ - commitment_constant += constant_k; - - // add (- ∑ₖ (1/zₖ(r))[Bₖ]) to [Q] - commitment += merged_commitment * (-z_k.invert()); - } - commitment += proof; - commitment += Commitment::one() * commitment_constant; - - return { .commitment = commitment, .opening_point = zeta_challenge, .eval = Fr::zero() }; - }; -}; -} // namespace honk::pcs::shplonk \ No newline at end of file diff --git a/barretenberg/cpp/src/aztec/honk/pcs/shplonk/shplonk_single.hpp b/barretenberg/cpp/src/aztec/honk/pcs/shplonk/shplonk_single.hpp index 50a6572742e7..2f387d551dc8 100644 --- a/barretenberg/cpp/src/aztec/honk/pcs/shplonk/shplonk_single.hpp +++ b/barretenberg/cpp/src/aztec/honk/pcs/shplonk/shplonk_single.hpp @@ -1,4 +1,5 @@ #pragma once +#include "honk/pcs/claim.hpp" #include "shplonk.hpp" #include "honk/pcs/commitment_key.hpp" @@ -15,6 +16,7 @@ template class SingleBatchOpeningScheme { using Fr = typename Params::Fr; using Commitment = typename Params::Commitment; + using CommitmentAffine = typename Params::C; using Polynomial = barretenberg::Polynomial; public: @@ -23,20 +25,20 @@ template class SingleBatchOpeningScheme { * a univariate polynomial opening scheme. * * @param ck CommitmentKey - * @param claims list of opening claims (Cⱼ, xⱼ, vⱼ) for a witness polynomial fⱼ(X), s.t. fⱼ(xⱼ) = vⱼ. + * @param opening_pairs list of opening pairs (xⱼ, vⱼ) for a witness polynomial fⱼ(X), s.t. fⱼ(xⱼ) = vⱼ. * @param witness_polynomials list of polynomials fⱼ(X). * @param transcript * @return Output{OpeningClaim, WitnessPolynomial, Proof} */ static ProverOutput reduce_prove(std::shared_ptr ck, - std::span> claims, + std::span> opening_pairs, std::span witness_polynomials, const auto& transcript) { transcript->apply_fiat_shamir("nu"); Fr nu = Fr::serialize_from_buffer(transcript->get_challenge("nu").begin()); - const size_t num_claims = claims.size(); + const size_t num_opening_pairs = opening_pairs.size(); // Find n, the maximum size of all polynomials fⱼ(X) size_t max_poly_size{ 0 }; @@ -48,14 +50,14 @@ template class SingleBatchOpeningScheme { Polynomial tmp(max_poly_size); Fr current_nu = Fr::one(); - for (size_t j = 0; j < num_claims; ++j) { + for (size_t j = 0; j < num_opening_pairs; ++j) { // (Cⱼ, xⱼ, vⱼ) - const auto& [commitment_j, opening_j, eval_j] = claims[j]; + const auto& [query, evaluation] = opening_pairs[j]; // tmp = ρʲ ⋅ ( fⱼ(X) − vⱼ) / ( X − xⱼ ) tmp = witness_polynomials[j]; - tmp[0] -= eval_j; - tmp.factor_roots(opening_j); + tmp[0] -= evaluation; + tmp.factor_roots(query); Q.add_scaled(tmp, current_nu); current_nu *= nu; @@ -63,57 +65,45 @@ template class SingleBatchOpeningScheme { // [Q] Commitment Q_commitment = ck->commit(Q); - transcript->add_element("Q", static_cast(Q_commitment).to_buffer()); + transcript->add_element("Q", static_cast(Q_commitment).to_buffer()); - // generate random evaluation challenge zeta_challenge + // generate random evaluation challenge "z" transcript->apply_fiat_shamir("z"); - const Fr zeta_challenge = Fr::serialize_from_buffer(transcript->get_challenge("z").begin()); + const Fr z_challenge = Fr::serialize_from_buffer(transcript->get_challenge("z").begin()); // {ẑⱼ(r)}ⱼ , where ẑⱼ(r) = 1/zⱼ(r) = 1/(r - xⱼ) std::vector inverse_vanishing_evals; - inverse_vanishing_evals.reserve(num_claims); + inverse_vanishing_evals.reserve(num_opening_pairs); { - for (const auto& claim_j : claims) { - inverse_vanishing_evals.emplace_back(zeta_challenge - claim_j.opening_point); + for (const auto& pair : opening_pairs) { + inverse_vanishing_evals.emplace_back(z_challenge - pair.query); } Fr::batch_invert(inverse_vanishing_evals); } - // G(X) = Q(X) - ∑ⱼ ρʲ ⋅ ( fⱼ(X) − vⱼ) / ( r − xⱼ ), + // G(X) = Q(X) - Q_z(X) = Q(X) - ∑ⱼ ρʲ ⋅ ( fⱼ(X) − vⱼ) / ( r − xⱼ ), // s.t. G(r) = 0 Polynomial& G = Q; // G₀ = ∑ⱼ ρʲ ⋅ vⱼ / ( r − xⱼ ) - Fr G_commitment_constant = Fr::zero(); - // [G] = [Q] - ∑ⱼ ρʲ / ( r − xⱼ )⋅[fⱼ] + G₀⋅[1] - // = [Q] - [∑ⱼ ρʲ ⋅ ( fⱼ(X) − vⱼ) / ( r − xⱼ )] - Commitment G_commitment = Q_commitment; current_nu = Fr::one(); - for (size_t j = 0; j < num_claims; ++j) { + for (size_t j = 0; j < num_opening_pairs; ++j) { // (Cⱼ, xⱼ, vⱼ) - const auto& [commitment_j, opening_j, eval_j] = claims[j]; + const auto& [query, evaluation] = opening_pairs[j]; // tmp = ρʲ ⋅ ( fⱼ(X) − vⱼ) / ( r − xⱼ ) tmp = witness_polynomials[j]; - tmp[0] -= eval_j; + tmp[0] -= evaluation; Fr scaling_factor = current_nu * inverse_vanishing_evals[j]; // = ρʲ / ( r − xⱼ ) // G -= ρʲ ⋅ ( fⱼ(X) − vⱼ) / ( r − xⱼ ) G.add_scaled(tmp, -scaling_factor); - // G₀ += ρʲ / ( r − xⱼ ) ⋅ vⱼ - G_commitment_constant += scaling_factor * eval_j; - // [G] -= ρʲ / ( r − xⱼ )⋅[fⱼ] - G_commitment -= commitment_j * scaling_factor; - current_nu *= nu; } - // [G] += G₀⋅[1] = [G] + (∑ⱼ ρʲ ⋅ vⱼ / ( r − xⱼ ))⋅[1] - G_commitment += Commitment::one() * G_commitment_constant; - return { .claim = { .commitment = G_commitment, .opening_point = zeta_challenge, .eval = Fr::zero() }, - .witness = std::move(G), - .proof = Q_commitment }; + // Return opening pair (z, 0) and polynomial G(X) = Q(X) - Q_z(X) + return { .opening_pair = { .query = z_challenge, .evaluation = Fr::zero() }, .witness = std::move(G) }; }; /** @@ -131,7 +121,7 @@ template class SingleBatchOpeningScheme { { const size_t num_claims = claims.size(); const Fr nu = Fr::serialize_from_buffer(transcript->get_challenge("nu").begin()); - const Fr zeta_challenge = Fr::serialize_from_buffer(transcript->get_challenge("z").begin()); + const Fr z_challenge = Fr::serialize_from_buffer(transcript->get_challenge("z").begin()); // compute simulated commitment to [G] as a linear combination of // [Q], { [fⱼ] }, [1]: @@ -149,8 +139,8 @@ template class SingleBatchOpeningScheme { std::vector inverse_vanishing_evals; inverse_vanishing_evals.reserve(num_claims); { - for (const auto& claim_j : claims) { - inverse_vanishing_evals.emplace_back(zeta_challenge - claim_j.opening_point); + for (const auto& claim : claims) { + inverse_vanishing_evals.emplace_back(z_challenge - claim.opening_pair.query); } Fr::batch_invert(inverse_vanishing_evals); } @@ -158,21 +148,22 @@ template class SingleBatchOpeningScheme { Fr current_nu{ Fr::one() }; for (size_t j = 0; j < num_claims; ++j) { // (Cⱼ, xⱼ, vⱼ) - const auto& [commitment_j, opening_j, eval_j] = claims[j]; + const auto& [opening_pair, commitment] = claims[j]; Fr scaling_factor = current_nu * inverse_vanishing_evals[j]; // = ρʲ / ( r − xⱼ ) // G₀ += ρʲ / ( r − xⱼ ) ⋅ vⱼ - G_commitment_constant += scaling_factor * eval_j; + G_commitment_constant += scaling_factor * opening_pair.evaluation; // [G] -= ρʲ / ( r − xⱼ )⋅[fⱼ] - G_commitment -= commitment_j * scaling_factor; + G_commitment -= commitment * scaling_factor; current_nu *= nu; } // [G] += G₀⋅[1] = [G] + (∑ⱼ ρʲ ⋅ vⱼ / ( r − xⱼ ))⋅[1] G_commitment += Commitment::one() * G_commitment_constant; - return { .commitment = G_commitment, .opening_point = zeta_challenge, .eval = Fr::zero() }; + // Return opening pair (z, 0) and commitment [G] + return { { z_challenge, Fr::zero() }, G_commitment }; }; }; } // namespace honk::pcs::shplonk diff --git a/barretenberg/cpp/src/aztec/honk/proof_system/prover.cpp b/barretenberg/cpp/src/aztec/honk/proof_system/prover.cpp index 7cea23b962ce..c47d8c54dc73 100644 --- a/barretenberg/cpp/src/aztec/honk/proof_system/prover.cpp +++ b/barretenberg/cpp/src/aztec/honk/proof_system/prover.cpp @@ -10,13 +10,11 @@ #include #include #include -#include "common/assert.hpp" #include "ecc/curves/bn254/fr.hpp" #include "ecc/curves/bn254/g1.hpp" #include #include #include -#include "honk/pcs/gemini/gemini.hpp" #include "polynomials/polynomial.hpp" #include "proof_system/flavor/flavor.hpp" #include "transcript/transcript_wrappers.hpp" @@ -71,11 +69,6 @@ Prover::Prover(std::vector&& wire_polys, } /** - * For Plonk systems: - * - Compute commitments to wires 1,2,3 - * - Get public inputs (which were stored in w_2_lagrange) and add to transcript - * - * For Honk, we should * - Commit to wires 1,2,3 * - Add PI to transcript (I guess PI will stay in w_2 for now?) * @@ -199,13 +192,7 @@ template Polynomial Prover::compute_grand_product_ } /** - * For Plonk systems: - * - added some initial data to transcript: circuit size and PI size - * - added randomness to lagrange wires - * - performed ifft to get monomial wires - * - * For Honk: - * - Add circuit size and PI size to transcript. That's it? + * - Add circuit size and PI size to transcript * * */ template void Prover::execute_preamble_round() @@ -230,11 +217,6 @@ template void Prover::execute_preamble_round() } /** - * For Plonk systems: - * - compute wire commitments - * - add public inputs to transcript (done in compute_wire_commitments() for some reason) - * - * For Honk: * - compute wire commitments * - add public inputs to transcript (done explicitly in execute_first_round()) * */ @@ -254,10 +236,6 @@ template void Prover::execute_wire_commitments_rou } /** - * For Plonk systems: - * - Do Fiat-Shamir to get "eta" challenge (done regardless of arithmetization but only required for Ultra) - * - does stuff related only to lookups (compute 's' etc and do some RAM/ROM stuff with w_4). - * * For Standard Honk, this is a non-op (just like for Standard/Turbo Plonk). * */ template void Prover::execute_tables_round() @@ -269,12 +247,6 @@ template void Prover::execute_tables_round() } /** - * For Plonk systems: - * - Do Fiat-Shamir to get "beta" challenge - * - Compute grand product polynomials (permutation and lookup) and commitments - * - Compute wire polynomial coset FFTs - * - * For Honk: * - Do Fiat-Shamir to get "beta" challenge (Note: gamma = beta^2) * - Compute grand product polynomial (permutation only) and commitment * */ @@ -296,13 +268,6 @@ template void Prover::execute_grand_product_comput } /** - * For Plonk systems: - * - Do Fiat-Shamir to get "alpha" challenge - * - Compute coset_fft(L_1) - * - Compute quotient polynomial (with blinding) - * - Compute quotient polynomial commitment - * - * For Honk * - Do Fiat-Shamir to get "alpha" challenge * - Run Sumcheck resulting in u = (u_1,...,u_d) challenges and all * evaluations at u being calculated. @@ -311,7 +276,6 @@ template void Prover::execute_relation_check_round { // queue.flush_queue(); // NOTE: Don't remove; we may reinstate the queue - using Transcript = transcript::StandardTranscript; using Sumcheck = sumcheck::Sumcheck void Prover::execute_relation_check_round } /** - * For Plonk: the polynomials are univariate, so this is a no-op. - * For Honk: * - Get rho challenge - * - Compute Fold polynomials and commitments. + * - Compute d+1 Fold polynomials and their evaluations. * * */ template void Prover::execute_univariatization_round() { - using Gemini = pcs::gemini::MultilinearReductionScheme; - using MLEOpeningClaim = pcs::MLEOpeningClaim; - - // Construct inputs for Gemini: - // - Multivariate opening point u = (u_0, ..., u_{d-1}) - // - MLE opening claim = {commitment, eval} for each multivariate and shifted multivariate polynomial - // - Pointers to multivariate and shifted multivariate polynomials - std::vector opening_point; - std::vector opening_claims; - std::vector opening_claims_shifted; - std::vector> multivariate_polynomials; - std::vector> multivariate_polynomials_shifted; - - // Construct MLE opening point + const size_t NUM_POLYNOMIALS = bonk::StandardArithmetization::NUM_POLYNOMIALS; + const size_t NUM_UNSHIFTED_POLYS = bonk::StandardArithmetization::NUM_UNSHIFTED_POLYNOMIALS; + + // Construct MLE opening point u = (u_0, ..., u_{d-1}) + std::vector opening_point; // u for (size_t round_idx = 0; round_idx < key->log_circuit_size; round_idx++) { std::string label = "u_" + std::to_string(round_idx); opening_point.emplace_back(transcript.get_challenge_field_element(label)); } + // Generate batching challenge ρ and powers 1,ρ,…,ρᵐ⁻¹ + transcript.apply_fiat_shamir("rho"); + Fr rho = Fr::serialize_from_buffer(transcript.get_challenge("rho").begin()); + std::vector rhos = Gemini::powers_of_rho(rho, NUM_POLYNOMIALS); + // Get vector of multivariate evaluations produced by Sumcheck auto multivariate_evaluations = transcript.get_field_element_vector("multivariate_evaluations"); - // Collect NON-shifted and shifted polynomials and opening claims based on enum. - auto mock_commitment = Commitment::one(); // prover does not require genuine commitments - for (size_t i = 0; i < bonk::StandardArithmetization::NUM_POLYNOMIALS; ++i) { - if (i < bonk::StandardArithmetization::NUM_UNSHIFTED_POLYNOMIALS) { - multivariate_polynomials.push_back(prover_polynomials[i]); - opening_claims.emplace_back(mock_commitment, multivariate_evaluations[i]); - } else { // shifted polynomials/claims - // Note: we must provide the NON-shifted polynomial but the shifted evaluation. - // TODO(luke): This is a bit hard-coded for standard Honk right now. Can do away with this after changing - // Gemini interface to accept shifted polynomials. - multivariate_polynomials_shifted.push_back(prover_polynomials[POLYNOMIAL::Z_PERM]); - opening_claims_shifted.emplace_back(mock_commitment, multivariate_evaluations[i]); - } + // Batch the unshifted polynomials and the to-be-shifted polynomials using ρ + Polynomial batched_poly_unshifted(key->circuit_size); // batched unshifted polynomials + for (size_t i = 0; i < NUM_UNSHIFTED_POLYS; ++i) { + batched_poly_unshifted.add_scaled(prover_polynomials[i], rhos[i]); } + Polynomial batched_poly_to_be_shifted(key->circuit_size); // batched to-be-shifted polynomials + batched_poly_to_be_shifted.add_scaled(prover_polynomials[POLYNOMIAL::Z_PERM], rhos[NUM_UNSHIFTED_POLYS]); + // Compute d+1 Fold polynomials and their evaluations gemini_output = Gemini::reduce_prove(commitment_key, opening_point, - opening_claims, - opening_claims_shifted, - multivariate_polynomials, - multivariate_polynomials_shifted, + std::move(batched_poly_unshifted), + std::move(batched_poly_to_be_shifted), &transcript); } /** - * For Plonk systems: - * - Do Fiat-Shamir to get "frak-z" challenge - * - Compute linearization or evaluation of quotient polynomial. - * - * For Honk: * - Do Fiat-Shamir to get "r" challenge * - Compute evaluations of folded polynomials. * */ @@ -397,39 +342,24 @@ template void Prover::execute_pcs_evaluation_round } /** - * For Plonk: Batching is combined with generation of opening proof polynomial commitments. - * - * For Honk: * - Do Fiat-Shamir to get "nu" challenge. - * - Compute Shplonk batched quotient commitment [Q]_1. + * - Compute commitment [Q]_1 + * - Do Fiat-Shamir to get "z" challenge. + * - Compute polynomial Q(X) - Q_z(X) * */ template void Prover::execute_shplonk_round() { - using Shplonk = pcs::shplonk::SingleBatchOpeningScheme; - shplonk_output = Shplonk::reduce_prove(commitment_key, gemini_output.claim, gemini_output.witness, &transcript); + shplonk_output = + Shplonk::reduce_prove(commitment_key, gemini_output.opening_pairs, gemini_output.witnesses, &transcript); } /** - * For Plonk systems: - * - Do Fiat-Shamir to get "nu" challenge - * - Compute KZG batch opening polynomial commitments. - * - * For Honk: - * - Get "z" challenge. - * - Compute KZG quotient [W]_1. + * - Compute KZG quotient commitment [W]_1. * * */ template void Prover::execute_kzg_round() { - // Note(luke): Fiat-Shamir to get "z" challenge is done in Shplonk::reduce_prove - // TODO(luke): Get KZG opening point [W]_1 - using KZG = pcs::kzg::UnivariateOpeningScheme; - using KzgOutput = pcs::kzg::UnivariateOpeningScheme::Output; - KzgOutput kzg_output = KZG::reduce_prove(commitment_key, shplonk_output.claim, shplonk_output.witness); - - auto W_commitment = static_cast(kzg_output.proof).to_buffer(); - - transcript.add_element("W", W_commitment); + KZG::reduce_prove(commitment_key, shplonk_output.opening_pair, shplonk_output.witness, &transcript); } template plonk::proof& Prover::export_proof() diff --git a/barretenberg/cpp/src/aztec/honk/proof_system/prover.hpp b/barretenberg/cpp/src/aztec/honk/proof_system/prover.hpp index 660f643c62fb..565b1e610de4 100644 --- a/barretenberg/cpp/src/aztec/honk/proof_system/prover.hpp +++ b/barretenberg/cpp/src/aztec/honk/proof_system/prover.hpp @@ -1,8 +1,8 @@ #pragma once #include "ecc/curves/bn254/fr.hpp" +#include "honk/pcs/shplonk/shplonk.hpp" #include "polynomials/polynomial.hpp" #include "proof_system/flavor/flavor.hpp" -#include "proof_system/polynomial_cache/polynomial_cache.hpp" #include #include #include @@ -14,6 +14,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include namespace honk { @@ -77,6 +83,11 @@ template class Prover { pcs::gemini::ProverOutput gemini_output; pcs::shplonk::ProverOutput shplonk_output; + using Transcript = transcript::StandardTranscript; + using Gemini = pcs::gemini::MultilinearReductionScheme; + using Shplonk = pcs::shplonk::SingleBatchOpeningScheme; + using KZG = pcs::kzg::UnivariateOpeningScheme; + private: plonk::proof proof; }; diff --git a/barretenberg/cpp/src/aztec/honk/proof_system/verifier.cpp b/barretenberg/cpp/src/aztec/honk/proof_system/verifier.cpp index 50e044d6ec33..11e5a30be25a 100644 --- a/barretenberg/cpp/src/aztec/honk/proof_system/verifier.cpp +++ b/barretenberg/cpp/src/aztec/honk/proof_system/verifier.cpp @@ -54,8 +54,7 @@ template Verifier& Verifier Verifier& Verifier bool Verifier::verify_proof(const plonk::proof& proof) { - using FF = typename program_settings::fr; - using Commitment = barretenberg::g1::affine_element; + using Commitment = barretenberg::g1::element; using Transcript = typename program_settings::Transcript; using Gemini = pcs::gemini::MultilinearReductionScheme; using Shplonk = pcs::shplonk::SingleBatchOpeningScheme; using KZG = pcs::kzg::UnivariateOpeningScheme; - using MLEOpeningClaim = pcs::MLEOpeningClaim; - using GeminiProof = pcs::gemini::Proof; - using POLYNOMIAL = bonk::StandardArithmetization::POLYNOMIAL; + const size_t NUM_POLYNOMIALS = bonk::StandardArithmetization::NUM_POLYNOMIALS; const size_t NUM_UNSHIFTED = bonk::StandardArithmetization::NUM_UNSHIFTED_POLYNOMIALS; const size_t NUM_PRECOMPUTED = bonk::StandardArithmetization::NUM_PRECOMPUTED_POLYNOMIALS; @@ -143,55 +139,56 @@ template bool Verifier::verify_pro // Construct inputs for Gemini verifier: // - Multivariate opening point u = (u_0, ..., u_{d-1}) - // - MLE opening claim = {commitment, eval} for each multivariate and shifted multivariate polynomial - std::vector opening_point; - std::vector opening_claims; - std::vector opening_claims_shifted; + // - batched unshifted and to-be-shifted polynomial commitments + std::vector opening_point; // u = (u_0,...,u_{d-1}) + auto batched_commitment_unshifted = Commitment::zero(); + auto batched_commitment_to_be_shifted = Commitment::zero(); // Construct MLE opening point - // Note: for consistency the evaluation point must be constructed as u = (u_0,...,u_{d-1}) for (size_t round_idx = 0; round_idx < key->log_circuit_size; round_idx++) { std::string label = "u_" + std::to_string(round_idx); opening_point.emplace_back(transcript.get_challenge_field_element(label)); } + // Compute powers of batching challenge rho + Fr rho = Fr::serialize_from_buffer(transcript.get_challenge("rho").begin()); + std::vector rhos = Gemini::powers_of_rho(rho, NUM_POLYNOMIALS); + // Get vector of multivariate evaluations produced by Sumcheck auto multivariate_evaluations = transcript.get_field_element_vector("multivariate_evaluations"); - // Construct NON-shifted opening claims + // Compute batched multivariate evaluation + Fr batched_evaluation = Fr::zero(); + for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { + batched_evaluation += multivariate_evaluations[i] * rhos[i]; + } + + // Construct batched commitment for NON-shifted polynomials for (size_t i = 0; i < NUM_UNSHIFTED; ++i) { + Commitment commitment; if (i < NUM_PRECOMPUTED) { // if precomputed, commitment comes from verification key - Commitment commitment = key->commitments[bonk::StandardArithmetization::ENUM_TO_COMM[i]]; - opening_claims.emplace_back(commitment, multivariate_evaluations[i]); + commitment = key->commitments[bonk::StandardArithmetization::ENUM_TO_COMM[i]]; } else { // if witness, commitment comes from prover (via transcript) - Commitment commitment = transcript.get_group_element(bonk::StandardArithmetization::ENUM_TO_COMM[i]); - opening_claims.emplace_back(commitment, multivariate_evaluations[i]); + commitment = transcript.get_group_element(bonk::StandardArithmetization::ENUM_TO_COMM[i]); } + batched_commitment_unshifted += commitment * rhos[i]; } - // Constructed shifted opening claims - Commitment commitment = transcript.get_group_element("Z_PERM"); - Fr evaluation = multivariate_evaluations[POLYNOMIAL::Z_PERM_SHIFT]; - opening_claims_shifted.emplace_back(commitment, evaluation); + // Construct batched commitment for to-be-shifted polynomials + batched_commitment_to_be_shifted = transcript.get_group_element("Z_PERM") * rhos[NUM_UNSHIFTED]; // Reconstruct the Gemini Proof from the transcript - GeminiProof gemini_proof; - - for (size_t i = 1; i < key->log_circuit_size; i++) { - std::string label = "FOLD_" + std::to_string(i); - gemini_proof.commitments.emplace_back(transcript.get_group_element(label)); - }; - - for (size_t i = 0; i < key->log_circuit_size; i++) { - std::string label = "a_" + std::to_string(i); - gemini_proof.evals.emplace_back(transcript.get_field_element(label)); - }; + auto gemini_proof = Gemini::reconstruct_proof_from_transcript(&transcript, key->log_circuit_size); // Produce a Gemini claim consisting of: // - d+1 commitments [Fold_{r}^(0)], [Fold_{-r}^(0)], and [Fold^(l)], l = 1:d-1 // - d+1 evaluations a_0_pos, and a_l, l = 0:d-1 - auto gemini_claim = - Gemini::reduce_verify(opening_point, opening_claims, opening_claims_shifted, gemini_proof, &transcript); + auto gemini_claim = Gemini::reduce_verify(opening_point, + batched_evaluation, + batched_commitment_unshifted, + batched_commitment_to_be_shifted, + gemini_proof, + &transcript); // Reconstruct the Shplonk Proof (commitment [Q]) from the transcript auto shplonk_proof = transcript.get_group_element("Q"); @@ -206,7 +203,7 @@ template bool Verifier::verify_pro auto kzg_claim = KZG::reduce_verify(shplonk_claim, kzg_proof); // Do final pairing check - bool pairing_result = kzg_claim.verify(kate_verification_key.get()); + bool pairing_result = kzg_claim.verify(kate_verification_key); bool result = sumcheck_result && pairing_result;