diff --git a/cpp/src/aztec/crypto/blake3s/blake3s.hpp b/cpp/src/aztec/crypto/blake3s/blake3s.hpp index eb581479a9..bb147d06be 100644 --- a/cpp/src/aztec/crypto/blake3s/blake3s.hpp +++ b/cpp/src/aztec/crypto/blake3s/blake3s.hpp @@ -27,7 +27,7 @@ Also, the length of the output in this specific implementation is fixed at 32 bytes which is the only version relevant to Barretenberg. */ - +#pragma once #include #include #include diff --git a/cpp/src/aztec/honk/composer/composer_helper/composer_helper.cpp b/cpp/src/aztec/honk/composer/composer_helper/composer_helper.cpp index dfbcff0588..dd908f92f9 100644 --- a/cpp/src/aztec/honk/composer/composer_helper/composer_helper.cpp +++ b/cpp/src/aztec/honk/composer/composer_helper/composer_helper.cpp @@ -221,10 +221,7 @@ template StandardVerifier ComposerHelper::create_verifier(const CircuitConstructor& circuit_constructor) { compute_verification_key(circuit_constructor); - StandardVerifier output_state( - circuit_verification_key, - honk::StandardHonk::create_manifest(circuit_constructor.public_inputs.size(), - numeric::get_msb(circuit_verification_key->circuit_size))); + StandardVerifier output_state(circuit_verification_key); // TODO(Cody): This should be more generic auto kate_verification_key = std::make_unique("../srs_db/ignition"); @@ -242,9 +239,7 @@ StandardProver ComposerHelper::create_prover(const CircuitCo compute_proving_key(circuit_constructor); compute_witness(circuit_constructor); - size_t num_sumcheck_rounds(circuit_proving_key->log_circuit_size); - auto manifest = Flavor::create_manifest(circuit_constructor.public_inputs.size(), num_sumcheck_rounds); - StandardProver output_state(std::move(wire_polynomials), circuit_proving_key, manifest); + StandardProver output_state(std::move(wire_polynomials), circuit_proving_key); // TODO(Cody): This should be more generic std::unique_ptr kate_commitment_key = diff --git a/cpp/src/aztec/honk/composer/standard_honk_composer.test.cpp b/cpp/src/aztec/honk/composer/standard_honk_composer.test.cpp index 4fb256ca9a..4215c059ae 100644 --- a/cpp/src/aztec/honk/composer/standard_honk_composer.test.cpp +++ b/cpp/src/aztec/honk/composer/standard_honk_composer.test.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -335,7 +336,6 @@ TEST(StandardHonkComposer, SumcheckRelationCorrectness) // Generate beta and gamma fr beta = fr::random_element(); fr gamma = fr::random_element(); - fr zeta = fr::random_element(); // Compute public input delta const auto public_inputs = composer.circuit_constructor.get_public_inputs(); @@ -343,8 +343,6 @@ TEST(StandardHonkComposer, SumcheckRelationCorrectness) honk::compute_public_input_delta(public_inputs, beta, gamma, prover.key->circuit_size); sumcheck::RelationParameters params{ - .zeta = zeta, - .alpha = fr::one(), .beta = beta, .gamma = gamma, .public_input_delta = public_input_delta, @@ -397,13 +395,13 @@ TEST(StandardHonkComposer, SumcheckRelationCorrectness) // i-th row/vertex of the hypercube. // We use ASSERT_EQ instead of EXPECT_EQ so that the tests stops at the first index at which the result is not // 0, since result = 0 + C(transposed), which we expect will equal 0. - std::get<0>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + result = std::get<0>(relations).evaluate_full_relation_value_contribution(evaluations_at_index_i, params); ASSERT_EQ(result, 0); - std::get<1>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + result = std::get<1>(relations).evaluate_full_relation_value_contribution(evaluations_at_index_i, params); ASSERT_EQ(result, 0); - std::get<2>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + result = std::get<2>(relations).evaluate_full_relation_value_contribution(evaluations_at_index_i, params); ASSERT_EQ(result, 0); } } diff --git a/cpp/src/aztec/honk/oracle/oracle.hpp b/cpp/src/aztec/honk/oracle/oracle.hpp deleted file mode 100644 index 0a78654fa9..0000000000 --- a/cpp/src/aztec/honk/oracle/oracle.hpp +++ /dev/null @@ -1,274 +0,0 @@ -#pragma once - -#include -#include - -namespace honk { - -template struct Oracle { - size_t consumed{ 0 }; - using Transcript = TranscriptType; - - using Fr = typename TranscriptType::Fr; - Oracle(Transcript*){}; - - /** - * @brief commit data to the current challenge buffer - * - * @tparam T template parameter pack! List of types we're inputting - * @param args data we want to add to the transcript - * - * @details Method is deliberately generic. T can be an array of types or a list of parameters. The only - condition - * is that T (or its inner types if T is an array) have valid serialization functions `read/write` - * - * e.g. all of these are valid uses of `append_data` - * - * ``` - * Fr a = something_old; - * Fr b = something_new(); - * append_data(a, b); - * append_data({a, b}); - * G1& c = something_borrowed(); - * std::string d = "something new"; - * append_data({a, b}, c, d); - * ``` - * - */ - template void consume(const T&...) { ++consumed; } - - /** - * @brief use the current value of `current_round_challenge_inputs` to generate a challenge via the Fiat-Shamir - * heuristic - * - * @return Fr the generated challenge - */ - Fr generate_challenge() { return Fr(consumed + 2); } -}; -// /** -// * @brief Oracle class wraps a Transcript and exposes an interface to generate plonk/honk challenges -// * -// * @tparam TranscriptType the transcript class being prametrised -// * -// * @details The purpose of the Oracle is to allow the proof system to generate and retrieve challenges without having -// to -// * keep track of a manifest (like in old Plonk) -// * -// * Conceptually there are two "categories" of challenges in our proof system: -// * -// * 1. Challenges specific to the plonk circuit arithmetisation (alpha, gamma, beta, eta) -// * 2. Challenges local to a modular component used to parametrise a specific proof system (e.g. commitment scheme -// * challenges, sumcheck challenges) -// * -// * Oracle exposes methods to explicitly create/recover challenges of the former type -// * -// * The latter type of challenge uses the Oracle in a more idiomatic manner, where the following assumption is made: -// * -// * 1. If a modular component gneerates/uses challenges, those challenges can remain local to the module and will not -// * leak across the module boundary. -// * -// * e.g. challenges generated as part of the IPA commitment scheme should *not* be required elsewhere in the proof -// system -// * (e.g. the identity tester module) -// * -// * The interface to produce "local" challenges is via: -// * -// * consume(...input data) -// * followed by -// * generate_challenge() -// * generate_challenges() -// * -// * ### HANDLING TRANSCRIPT DATA ### -// * -// * Oracle handles transcript data in a deterministic and generic manner. -// * When challenges are generated, input data is pushed into a uint8_t buffer. -// * -// * This buffer can be retrieved via `export_transcript` -// * -// * Converting this transcript data to/from MEANINGFUL information is done via PROOF classes. -// * e.g. see `proof_systems/standard_plonk/standard_proof.hpp` -// * -// * Proof classes contain explicit member variables describing the data that goes into/from a transcript. -// * Serialization functions (read/write) are used to convert the uint8_t buffer produced by Oracle into a Proof -// object. -// * -// * The serialize functions are the point at which we should check that the transcript is correctly produced -// * e.g. all commitments are valid commitments -// * -// * The end result is that we do not have a manifest like in old plonk. The round structure of the proof system is -// * derived via the order in which data is added into the transcript. This can then be explicitly checked by a -// * proof-system-specific Proof class. -// */ -// template class Oracle { -// public: -// using Transcript = TranscriptType; -// using Fr = typename Transcript::Fr; -// Oracle(Transcript* _transcript) -// : transcript(_transcript) -// {} - -// template Fr generate_initialisation_challenge(const T&... args) -// { -// consume(args...); -// initialisation_challenge = generate_challenge(); -// return get_initialisation_challenge(); -// } - -// template std::array generate_permutation_challenges(const T&... args) -// { -// consume(args...); -// auto res = generate_challenges<2>(); -// beta = res[0]; -// gamma = res[1]; -// return get_permutation_challenges(); -// } - -// template Fr generate_plookup_challenge(const T&... args) -// { -// consume(args...); -// eta = generate_challenge(); -// return get_plookup_challenge(); -// } - -// template Fr generate_identity_separator_challenge(const T&... args) -// { -// consume(args...); -// alpha = generate_challenge(); -// return get_identity_separator_challenge(); -// } - -// /** -// * @brief commit data to the current challenge buffer -// * -// * @tparam T template parameter pack! List of types we're inputting -// * @param args data we want to add to the transcript -// * -// * @details Method is deliberately generic. T can be an array of types or a list of parameters. The only -// condition -// * is that T (or its inner types if T is an array) have valid serialization functions `read/write` -// * -// * e.g. all of these are valid uses of `append_data` -// * -// * ``` -// * Fr a = something_old; -// * Fr b = something_new(); -// * append_data(a, b); -// * append_data({a, b}); -// * G1& c = something_borrowed(); -// * std::string d = "something new"; -// * append_data({a, b}, c, d); -// * ``` -// * -// */ -// template void consume(const T&... args) { (_append_to_buffer(args), ...); } - -// /** -// * @brief use the current value of `current_round_challenge_inputs` to generate a challenge via the Fiat-Shamir -// * heuristic -// * -// * @return Fr the generated challenge -// */ -// Fr generate_challenge() -// { -// ASSERT(current_round_challenge_inputs.size() > 0); -// const Fr result = transcript->generate_challenge(current_round_challenge_inputs); -// current_round_challenge_inputs.clear(); -// return result; -// } - -// /** -// * @brief like generate_challenge but multiple challenges can be generated + returned -// * -// * @tparam num_challenges -// * @return std::array -// */ -// template std::array generate_challenges() -// { -// ASSERT(current_round_challenge_inputs.size() > 0); -// const auto result = transcript->template generate_challenges(current_round_challenge_inputs); -// current_round_challenge_inputs.clear(); -// return result; -// } - -// /** -// * @brief Set the opening points object -// * -// * Opening points are one area where the Oracle abstraction is a bit leaky (TODO: find a fix??) -// * -// * When the IdentityTester::evaluate_identity is called, the number of challenges (and rounds) will be -// * entirely defined by IdentityTester and is not fixed by the higher-level circuit arithmetisation. -// * (e.g. quotient testing needs 1 challenge, identity testing needs logn challenges) -// * -// * However the challenges generated are *not* local to the IdentityTester module, because they represent the -// points -// * that we need to open our polynomials at! -// * -// * The current pattern is as follows: -// * -// * 1. The IdentityTester::evaluate_identity uses the generic `generate_challenge`/`generate_challenges` methods -// to -// * produce challenges -// * 2. IdentityTester::evaluate_identity *returns* the list of opening points -// * 3. Prover/Verifier class will then call `oracle.set_opening_points` on the return data. -// * -// * @param _opening_points -// */ -// void set_opening_points(const std::vector& _opening_points) { opening_points = _opening_points; } - -// std::vector export_transcript() { return transcript->export_transcript(); } - -// Fr get_initialisation_challenge() const { return initialisation_challenge; } -// Fr get_plookup_challenge() const { return eta; } -// std::array get_permutation_challenges() const { return { beta, gamma }; } -// Fr get_identity_separator_challenge() const { return alpha; } -// std::vector get_opening_points() const { return opening_points; } -// Fr get_beta() const { return beta; } -// Fr get_gamma() const { return gamma; } - -// virtual consteval Fr get_identity_permutation_coset_generator() const { return Fr::coset_generator(0); } - -// private: -// template void _append_to_buffer(const T& value) -// { -// using serialize::write; -// // when writing into the buffer that we use to generate the fiat-shamir challenge, -// // we first want to flatten our data structures. -// // e.g. if T is a vector of size 2, calling write(buf, val) will append the vector size "2" into -// // the buffer. We don't want this as it increases the size of the fiat-shamir hash with redundant -// information. -// // TODO ADD! -// // if constexpr (T_is_nested_container) -// // { -// // auto flattened = std::ranges::views::join(value); -// // _append_to_buffer(buf, flattened); -// // } -// write(current_round_challenge_inputs, value); -// } -// template void _append_to_buffer(const T& value) -// { -// using serialize::write; -// for (const auto& param : value) { -// write(current_round_challenge_inputs, param); -// } -// } - -// // TODO make private but allow tests to fiddle with values -// public: -// Transcript* transcript; - -// Fr initialisation_challenge = 0; -// Fr eta = 0; -// Fr beta = 0; -// Fr gamma = 0; -// Fr alpha = 0; -// Fr nu = 0; -// Fr u = 0; -// std::vector opening_points = {}; - -// std::vector public_inputs = {}; - -// std::vector current_round_challenge_inputs = {}; - -// Fr public_input_delta = 1; // TODO FIX -// }; -} // namespace honk \ No newline at end of file diff --git a/cpp/src/aztec/honk/pcs/claim.hpp b/cpp/src/aztec/honk/pcs/claim.hpp index eb162fbe07..a57ea98e31 100644 --- a/cpp/src/aztec/honk/pcs/claim.hpp +++ b/cpp/src/aztec/honk/pcs/claim.hpp @@ -27,14 +27,14 @@ template class OpeningPair { */ template class OpeningClaim { using CK = typename Params::CK; - using Commitment = typename Params::Commitment; + using CommitmentAffine = typename Params::C; using Fr = typename Params::Fr; public: // (query r, evaluation v = p(r)) OpeningPair opening_pair; // commitment to univariate polynomial p(X) - Commitment commitment; + CommitmentAffine commitment; /** * @brief inefficiently check that the claim is correct by recomputing the commitment @@ -52,11 +52,7 @@ template class OpeningClaim { } // Note: real_commitment is a raw type, while commitment may be a linear combination. auto real_commitment = ck->commit(polynomial); - if (real_commitment != commitment) { - // if (commitment != real_commitment) { - return false; - } - return true; + return (real_commitment == commitment); }; bool operator==(const OpeningClaim& other) const = default; @@ -81,19 +77,15 @@ template class OpeningClaim { * * @tparam CommitmentKey */ -template struct MLEOpeningClaim { - using Commitment = typename Params::Commitment; +template class MLEOpeningClaim { + using CommitmentAffine = typename Params::C; using Fr = typename Params::Fr; - MLEOpeningClaim(auto commitment, auto evaluation) - : commitment(commitment) - , evaluation(evaluation) - {} - + public: // commitment to a univariate polynomial // whose coefficients are the multi-linear evaluations // of C = [f] - Commitment commitment; + CommitmentAffine commitment; // v = f(u) = ∑ᵢ aᵢ⋅Lᵢ(u) // v↺ = g(u) = a₁⋅L₀(u) + … + aₙ₋₁⋅Lₙ₋₂(u) Fr evaluation; diff --git a/cpp/src/aztec/honk/pcs/commitment_key.test.hpp b/cpp/src/aztec/honk/pcs/commitment_key.test.hpp index e662ce402a..0d67d06f84 100644 --- a/cpp/src/aztec/honk/pcs/commitment_key.test.hpp +++ b/cpp/src/aztec/honk/pcs/commitment_key.test.hpp @@ -12,7 +12,6 @@ #include #include -#include "../oracle/oracle.hpp" #include "../../transcript/transcript_wrappers.hpp" #include "../../proof_system/flavor/flavor.hpp" @@ -56,7 +55,7 @@ template class CommitmentTest : public ::testing::Test { using VK = typename Params::VK; using Fr = typename Params::Fr; - using Commitment = typename Params::Commitment; + using CommitmentAffine = typename Params::C; using Polynomial = typename Params::Polynomial; using Transcript = transcript::StandardTranscript; @@ -68,7 +67,7 @@ template class CommitmentTest : public ::testing::Test { std::shared_ptr ck() { return commitment_key; } std::shared_ptr vk() { return verification_key; } - Commitment commit(const Polynomial& polynomial) { return commitment_key->commit(polynomial); } + CommitmentAffine commit(const Polynomial& polynomial) { return commitment_key->commit(polynomial); } Polynomial random_polynomial(const size_t n) { @@ -112,7 +111,7 @@ template class CommitmentTest : public ::testing::Test { auto& [x, y] = claim.opening_pair; Fr y_expected = witness.evaluate(x); EXPECT_EQ(y, y_expected) << "OpeningClaim: evaluations mismatch"; - Commitment commitment_expected = commit(witness); + CommitmentAffine commitment_expected = commit(witness); EXPECT_EQ(commitment, commitment_expected) << "OpeningClaim: commitment mismatch"; } @@ -186,9 +185,6 @@ typename std::shared_ptr CommitmentTest::commitment template typename std::shared_ptr CommitmentTest::verification_key = nullptr; -using CommitmentSchemeParams = ::testing::Types; -// IMPROVEMENT: reinstate typed-tests for multiple field types, i.e.: -// using CommitmentSchemeParams = -// ::testing::Types, fake::Params, kzg::Params>; +using CommitmentSchemeParams = ::testing::Types>; } // namespace honk::pcs \ No newline at end of file diff --git a/cpp/src/aztec/honk/pcs/gemini/gemini.hpp b/cpp/src/aztec/honk/pcs/gemini/gemini.hpp index 2b7fe30924..14e308b16f 100644 --- a/cpp/src/aztec/honk/pcs/gemini/gemini.hpp +++ b/cpp/src/aztec/honk/pcs/gemini/gemini.hpp @@ -1,8 +1,7 @@ #pragma once #include "../claim.hpp" -#include "common/log.hpp" -#include "honk/pcs/commitment_key.hpp" +#include "honk/transcript/transcript.hpp" #include "polynomials/polynomial.hpp" #include @@ -47,30 +46,6 @@ */ namespace honk::pcs::gemini { -/** - * @brief A Gemini proof contains the m-1 commitments to the - * folded univariates, and corresponding evaluations - * at -r, -r², …, r^{2ᵐ⁻¹}. - * - * The evaluations allow the verifier to reconstruct the evaluation of A₀(r). - * - * @tparam Params CommitmentScheme parameters - */ -template struct Proof { - /** @brief Commitments to folded polynomials (size = m-1) - * - * [ C₁, …, Cₘ₋₁], where Cₗ = commit(Aₗ(X)) of size 2ᵐ⁻ˡ - */ - std::vector commitments; - - /** - * @brief Evaluations of batched and folded polynomials (size m) - * - * [A₀(-r) , ..., Aₘ₋₁(-r^{2ᵐ⁻¹})] - */ - std::vector evaluations; -}; - /** * @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] @@ -113,7 +88,7 @@ template class MultilinearReductionScheme { std::span mle_opening_point, const Polynomial&& batched_shifted, /* unshifted */ const Polynomial&& batched_to_be_shifted, /* to-be-shifted */ - const auto& transcript) + ProverTranscript& transcript) { const size_t num_variables = mle_opening_point.size(); // m @@ -165,19 +140,17 @@ template class MultilinearReductionScheme { /* * Create commitments C₁,…,Cₘ₋₁ to polynomials FOLD_i, i = 1,...,d-1 and add to transcript */ - std::vector commitments; + std::vector commitments; commitments.reserve(num_variables - 1); for (size_t l = 0; l < num_variables - 1; ++l) { commitments.emplace_back(ck->commit(fold_polynomials[l + 2])); - transcript->add_element("FOLD_" + std::to_string(l + 1), - static_cast(commitments[l]).to_buffer()); + transcript.send_to_verifier("Gemini:FOLD_" + std::to_string(l + 1), commitments[l]); } /* * Generate evaluation challenge r, and compute rₗ = r^{2ˡ} for l = 0, 1, ..., m-1 */ - transcript->apply_fiat_shamir("r"); - const Fr r_challenge = Fr::serialize_from_buffer(transcript->get_challenge("r").begin()); + const Fr r_challenge = transcript.get_challenge("Gemini:r"); std::vector r_squares = squares_of_r(r_challenge, num_variables); /* @@ -223,7 +196,7 @@ template class MultilinearReductionScheme { const Polynomial& A_l = fold_polynomials[l + 1]; 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()); + transcript.send_to_verifier("Gemini:a_" + std::to_string(l), fold_polynomial_evals[l]); } // Compute evaluation A₀(r) @@ -259,17 +232,34 @@ template class MultilinearReductionScheme { const Fr batched_evaluation, /* all */ Commitment& batched_f, /* unshifted */ Commitment& batched_g, /* to-be-shifted */ - const Proof& proof, - const auto& transcript) + VerifierTranscript& transcript) { const size_t num_variables = mle_opening_point.size(); + std::vector commitments; + commitments.reserve(num_variables - 1); + for (size_t i = 0; i < num_variables - 1; ++i) { + auto commitment = + transcript.template receive_from_prover("Gemini:FOLD_" + std::to_string(i + 1)); + commitments.emplace_back(commitment); + } + // compute vector of powers of random evaluation point r - const Fr r = Fr::serialize_from_buffer(transcript->get_challenge("r").begin()); + const Fr r = transcript.get_challenge("Gemini:r"); std::vector r_squares = squares_of_r(r, num_variables); + /* + * Get evaluations a_i, i = 0,...,m-1 from transcript + */ + std::vector evaluations; + evaluations.reserve(num_variables); + for (size_t i = 0; i < num_variables; ++i) { + auto eval = transcript.template receive_from_prover("Gemini:a_" + std::to_string(i + 1)); + evaluations.emplace_back(eval); + } + // Compute evaluation A₀(r) - auto a_0_pos = compute_eval_pos(batched_evaluation, mle_opening_point, r_squares, proof.evaluations); + auto a_0_pos = compute_eval_pos(batched_evaluation, mle_opening_point, r_squares, evaluations); // C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] + r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ] // C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] - r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ] @@ -281,40 +271,16 @@ template class MultilinearReductionScheme { // ( [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 }); + fold_polynomial_opening_claims.emplace_back(OpeningClaim{ { -r, evaluations[0] }, c0_r_neg }); for (size_t l = 0; l < num_variables - 1; ++l) { // ([A₀₋], −r^{2ˡ}, Aₗ(−r^{2ˡ}) ) fold_polynomial_opening_claims.emplace_back( - OpeningClaim{ { -r_squares[l + 1], proof.evaluations[l + 1] }, proof.commitments[l] }); + OpeningClaim{ { -r_squares[l + 1], evaluations[l + 1] }, 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 }; diff --git a/cpp/src/aztec/honk/pcs/gemini/gemini.test.cpp b/cpp/src/aztec/honk/pcs/gemini/gemini.test.cpp index 7d04b7afe5..86aac17add 100644 --- a/cpp/src/aztec/honk/pcs/gemini/gemini.test.cpp +++ b/cpp/src/aztec/honk/pcs/gemini/gemini.test.cpp @@ -23,11 +23,7 @@ template class GeminiTest : public CommitmentTest { std::vector multilinear_commitments, std::vector multilinear_commitments_to_be_shifted) { - using Transcript = transcript::StandardTranscript; - auto transcript = std::make_shared(StandardHonk::create_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()); + const Fr rho = Fr::random_element(); std::vector rhos = Gemini::powers_of_rho(rho, multilinear_evaluations.size()); @@ -52,6 +48,7 @@ template class GeminiTest : public CommitmentTest { 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]; } + auto prover_transcript = ProverTranscript::init_empty(); // Compute: // - (d+1) opening pairs: {r, \hat{a}_0}, {-r^{2^i}, a_i}, i = 0, ..., d-1 @@ -60,15 +57,13 @@ template class GeminiTest : public CommitmentTest { multilinear_evaluation_point, std::move(batched_unshifted), std::move(batched_to_be_shifted), - transcript); + prover_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); + auto verifier_transcript = VerifierTranscript::init_empty(prover_transcript); + [[maybe_unused]] const Fr _ = verifier_transcript.get_challenge("rho"); // Compute: // - Single opening pair: {r, \hat{a}_0} @@ -78,8 +73,7 @@ template class GeminiTest : public CommitmentTest { batched_evaluation, batched_commitment_unshifted, batched_commitment_to_be_shifted, - gemini_proof, - transcript); + verifier_transcript); // Check equality of the opening pairs computed by prover and verifier for (size_t i = 0; i < (log_n + 1); ++i) { diff --git a/cpp/src/aztec/honk/pcs/kzg/kzg.hpp b/cpp/src/aztec/honk/pcs/kzg/kzg.hpp index 3915c1a93b..eabdcb1258 100644 --- a/cpp/src/aztec/honk/pcs/kzg/kzg.hpp +++ b/cpp/src/aztec/honk/pcs/kzg/kzg.hpp @@ -1,6 +1,7 @@ #pragma once #include "../claim.hpp" +#include "honk/transcript/transcript.hpp" #include "polynomials/polynomial.hpp" #include @@ -17,6 +18,7 @@ namespace honk::pcs::kzg { template class BilinearAccumulator { using VK = typename Params::VK; using Fr = typename Params::Fr; + using CommitmentAffine = typename Params::C; using Commitment = typename Params::Commitment; public: @@ -43,7 +45,7 @@ template class BilinearAccumulator { bool operator==(const BilinearAccumulator& other) const = default; - Commitment lhs, rhs; + CommitmentAffine lhs, rhs; }; template class UnivariateOpeningScheme { @@ -67,14 +69,14 @@ template class UnivariateOpeningScheme { static void reduce_prove(std::shared_ptr ck, const OpeningPair& opening_pair, const Polynomial& polynomial, - const auto& transcript) + ProverTranscript& transcript) { Polynomial quotient(polynomial); quotient[0] -= opening_pair.evaluation; quotient.factor_roots(opening_pair.query); - Commitment proof = ck->commit(quotient); + CommitmentAffine quotient_commitment = ck->commit(quotient); - transcript->add_element("W", static_cast(proof).to_buffer()); + transcript.send_to_verifier("KZG:W", quotient_commitment); }; /** @@ -85,9 +87,10 @@ template class UnivariateOpeningScheme { * @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 Commitment& proof) + static Accumulator reduce_verify(const OpeningClaim& claim, VerifierTranscript& verifier_transcript) { - return Accumulator(claim, proof); + auto quotient_commitment = verifier_transcript.template receive_from_prover("KZG:W"); + return Accumulator(claim, quotient_commitment); }; }; } // namespace honk::pcs::kzg \ No newline at end of file diff --git a/cpp/src/aztec/honk/pcs/kzg/kzg.test.cpp b/cpp/src/aztec/honk/pcs/kzg/kzg.test.cpp index f927562a61..a69fd1b38c 100644 --- a/cpp/src/aztec/honk/pcs/kzg/kzg.test.cpp +++ b/cpp/src/aztec/honk/pcs/kzg/kzg.test.cpp @@ -27,34 +27,27 @@ TYPED_TEST_SUITE(BilinearAccumulationTest, CommitmentSchemeParams); TYPED_TEST(BilinearAccumulationTest, single) { const size_t n = 16; - const size_t log_n = 4; using KZG = UnivariateOpeningScheme; using Fr = typename TypeParam::Fr; - // 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_manifest(0, log_n)); - transcript->mock_inputs_prior_to_challenge("z"); - 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 }; + auto opening_claim = OpeningClaim{ .opening_pair = opening_pair, .commitment = commitment }; - 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 prover_transcript = ProverTranscript::init_empty(); - auto opening_claim = OpeningClaim{ opening_pair, commitment }; + KZG::reduce_prove(this->ck(), opening_pair, witness, prover_transcript); - auto kzg_claim = KZG::reduce_verify(opening_claim, kzg_proof); + auto verifier_transcript = VerifierTranscript::init_empty(prover_transcript); + auto kzg_claim = KZG::reduce_verify(opening_claim, verifier_transcript); bool verified = kzg_claim.verify(this->vk()); - EXPECT_EQ(verified, true); + EXPECT_TRUE(verified); } /** @@ -65,7 +58,6 @@ TYPED_TEST(BilinearAccumulationTest, single) */ TYPED_TEST(BilinearAccumulationTest, GeminiShplonkKzgWithShift) { - using Transcript = transcript::StandardTranscript; using Shplonk = shplonk::SingleBatchOpeningScheme; using Gemini = gemini::MultilinearReductionScheme; using KZG = UnivariateOpeningScheme; @@ -76,21 +68,17 @@ TYPED_TEST(BilinearAccumulationTest, GeminiShplonkKzgWithShift) 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_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()); + Fr rho = Fr::random_element(); // 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 poly2 = this->random_polynomial(n); - poly2[0] = Params::Fr::zero(); // this property is required of polynomials whose shift is used + poly2[0] = Fr::zero(); // this property is required of polynomials whose shift is used - auto commitment1 = this->commit(poly1); - auto commitment2 = this->commit(poly2); + Commitment commitment1 = this->commit(poly1); + Commitment commitment2 = this->commit(poly2); auto eval1 = poly1.evaluate_mle(mle_opening_point); auto eval2 = poly2.evaluate_mle(mle_opening_point); @@ -122,55 +110,45 @@ TYPED_TEST(BilinearAccumulationTest, GeminiShplonkKzgWithShift) // Run the full prover PCS protocol: + auto prover_transcript = ProverTranscript::init_empty(); // Gemini prover output: // - 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 - auto gemini_prover_output = Gemini::reduce_prove( - this->ck(), mle_opening_point, std::move(batched_unshifted), std::move(batched_to_be_shifted), transcript); + const auto [gemini_prover_claim, gemini_witness] = Gemini::reduce_prove(this->ck(), + mle_opening_point, + std::move(batched_unshifted), + std::move(batched_to_be_shifted), + prover_transcript); // Shplonk prover output: // - opening pair: (z_challenge, 0) // - witness: polynomial Q - Q_z - auto shplonk_prover_output = Shplonk::reduce_prove( - this->ck(), gemini_prover_output.opening_pairs, gemini_prover_output.witnesses, transcript); + const auto [shplonk_prover_claim, shplonk_witness] = + Shplonk::reduce_prove(this->ck(), gemini_prover_claim, gemini_witness, prover_transcript); // KZG prover: // - Adds commitment [W] to transcript - KZG::reduce_prove(this->ck(), shplonk_prover_output.opening_pair, shplonk_prover_output.witness, transcript); + KZG::reduce_prove(this->ck(), shplonk_prover_claim, shplonk_witness, prover_transcript); // Run the full verifier PCS protocol with genuine opening claims (genuine commitment, genuine evaluation) - // 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); - - // Reconstruct the Shplonk Proof (commitment [Q]) from the transcript - auto shplonk_proof = transcript->get_group_element("Q"); - - // Reconstruct the KZG Proof (commitment [W]) from the transcript - auto kzg_proof = transcript->get_group_element("W"); - + auto verifier_transcript = VerifierTranscript::init_empty(prover_transcript); // Gemini verifier output: // - claim: d+1 commitments to Fold_{r}^(0), Fold_{-r}^(0), Fold^(l), d+1 evaluations a_0_pos, a_l, l = 0:d-1 - auto gemini_verifier_claim = Gemini::reduce_verify(mle_opening_point, - batched_evaluation, - batched_commitment_unshifted, - batched_commitment_to_be_shifted, - gemini_proof, - transcript); + const auto gemini_verifier_claim = Gemini::reduce_verify(mle_opening_point, + batched_evaluation, + batched_commitment_unshifted, + batched_commitment_to_be_shifted, + verifier_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); + // 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, verifier_transcript); // 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()); + auto kzg_claim = KZG::reduce_verify(shplonk_verifier_claim, verifier_transcript); - EXPECT_EQ(verified, true); + EXPECT_TRUE(kzg_claim.verify(this->vk())); } - } // namespace honk::pcs::kzg \ No newline at end of file diff --git a/cpp/src/aztec/honk/pcs/shplonk/shplonk.hpp b/cpp/src/aztec/honk/pcs/shplonk/shplonk.hpp index c40d684a1f..164c6e8683 100644 --- a/cpp/src/aztec/honk/pcs/shplonk/shplonk.hpp +++ b/cpp/src/aztec/honk/pcs/shplonk/shplonk.hpp @@ -19,12 +19,6 @@ */ namespace honk::pcs::shplonk { -/** - * @brief Single commitment to Q(X) = ∑ₖ ( Bₖ(X) − Tₖ(X) ) / zₖ(X) - * - */ -template using Proof = typename Params::Commitment; - /** * @brief Polynomial G(X) = Q(X) - ∑ₖ ẑₖ(r)⋅( Bₖ(X) − Tₖ(z) ) * diff --git a/cpp/src/aztec/honk/pcs/shplonk/shplonk.test.cpp b/cpp/src/aztec/honk/pcs/shplonk/shplonk.test.cpp index 842cdcb070..1b57c02ec2 100644 --- a/cpp/src/aztec/honk/pcs/shplonk/shplonk.test.cpp +++ b/cpp/src/aztec/honk/pcs/shplonk/shplonk.test.cpp @@ -1,14 +1,13 @@ -#include "shplonk_single.hpp" #include "../gemini/gemini.hpp" - -#include #include #include #include -#include "../commitment_key.test.hpp" -#include "honk/pcs/claim.hpp" +#include "honk/pcs/shplonk/shplonk_single.hpp" +#include "honk/transcript/transcript.hpp" +#include "honk/pcs/commitment_key.test.hpp" #include "polynomials/polynomial.hpp" + namespace honk::pcs::shplonk { template class ShplonkTest : public CommitmentTest {}; @@ -17,6 +16,7 @@ TYPED_TEST_SUITE(ShplonkTest, CommitmentSchemeParams); // Test of Shplonk prover/verifier using real Gemini claim TYPED_TEST(ShplonkTest, GeminiShplonk) { + using Fr = typename TypeParam::Fr; using Shplonk = SingleBatchOpeningScheme; using Gemini = gemini::MultilinearReductionScheme; using Fr = typename TypeParam::Fr; @@ -26,11 +26,7 @@ TYPED_TEST(ShplonkTest, GeminiShplonk) const size_t n = 16; const size_t log_n = 4; - using Transcript = transcript::StandardTranscript; - auto transcript = std::make_shared(StandardHonk::create_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()); + const Fr rho = Fr::random_element(); const auto u = this->random_evaluation_point(log_n); auto poly = this->random_polynomial(n); @@ -52,30 +48,21 @@ TYPED_TEST(ShplonkTest, GeminiShplonk) 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); + auto prover_transcript = ProverTranscript::init_empty(); + + auto gemini_prover_output = Gemini::reduce_prove( + this->ck(), u, std::move(batched_unshifted), std::move(batched_to_be_shifted), prover_transcript); const auto [prover_opening_pair, shplonk_prover_witness] = Shplonk::reduce_prove( - this->ck(), gemini_prover_output.opening_pairs, gemini_prover_output.witnesses, transcript); + this->ck(), gemini_prover_output.opening_pairs, gemini_prover_output.witnesses, prover_transcript); this->verify_opening_pair(prover_opening_pair, shplonk_prover_witness); - // 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); - - auto gemini_verifier_claim = Gemini::reduce_verify(u, - 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"); + auto verifier_transcript = VerifierTranscript::init_empty(prover_transcript); + auto gemini_verifier_claim = Gemini::reduce_verify( + u, batched_evaluation, batched_commitment_unshifted, batched_commitment_to_be_shifted, verifier_transcript); - const auto verifier_claim = Shplonk::reduce_verify(gemini_verifier_claim, shplonk_proof, transcript); + const auto verifier_claim = Shplonk::reduce_verify(gemini_verifier_claim, verifier_transcript); this->verify_opening_claim(verifier_claim, shplonk_prover_witness); } diff --git a/cpp/src/aztec/honk/pcs/shplonk/shplonk_single.hpp b/cpp/src/aztec/honk/pcs/shplonk/shplonk_single.hpp index 2f387d551d..779c4a4a07 100644 --- a/cpp/src/aztec/honk/pcs/shplonk/shplonk_single.hpp +++ b/cpp/src/aztec/honk/pcs/shplonk/shplonk_single.hpp @@ -1,7 +1,6 @@ #pragma once -#include "honk/pcs/claim.hpp" +#include "honk/transcript/transcript.hpp" #include "shplonk.hpp" -#include "honk/pcs/commitment_key.hpp" namespace honk::pcs::shplonk { @@ -33,10 +32,9 @@ template class SingleBatchOpeningScheme { static ProverOutput reduce_prove(std::shared_ptr ck, std::span> opening_pairs, std::span witness_polynomials, - const auto& transcript) + ProverTranscript& transcript) { - transcript->apply_fiat_shamir("nu"); - Fr nu = Fr::serialize_from_buffer(transcript->get_challenge("nu").begin()); + Fr nu = transcript.get_challenge("Shplonk:nu"); const size_t num_opening_pairs = opening_pairs.size(); @@ -63,23 +61,20 @@ template class SingleBatchOpeningScheme { current_nu *= nu; } - // [Q] - Commitment Q_commitment = ck->commit(Q); - transcript->add_element("Q", static_cast(Q_commitment).to_buffer()); + // commit to Q(X) and add [Q] to the transcript + auto Q_commitment = ck->commit(Q); + transcript.send_to_verifier("Shplonk:Q", Q_commitment); - // generate random evaluation challenge "z" - transcript->apply_fiat_shamir("z"); - const Fr z_challenge = Fr::serialize_from_buffer(transcript->get_challenge("z").begin()); + // generate random evaluation challenge zeta_challenge + const Fr zeta_challenge = transcript.get_challenge("Shplonk:zeta"); // {ẑⱼ(r)}ⱼ , where ẑⱼ(r) = 1/zⱼ(r) = 1/(r - xⱼ) std::vector inverse_vanishing_evals; inverse_vanishing_evals.reserve(num_opening_pairs); - { - for (const auto& pair : opening_pairs) { - inverse_vanishing_evals.emplace_back(z_challenge - pair.query); - } - Fr::batch_invert(inverse_vanishing_evals); + for (const auto& pair : opening_pairs) { + inverse_vanishing_evals.emplace_back(zeta_challenge - pair.query); } + Fr::batch_invert(inverse_vanishing_evals); // G(X) = Q(X) - Q_z(X) = Q(X) - ∑ⱼ ρʲ ⋅ ( fⱼ(X) − vⱼ) / ( r − xⱼ ), // s.t. G(r) = 0 @@ -103,9 +98,8 @@ template class SingleBatchOpeningScheme { } // 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) }; + return {.opening_pair = { .query = zeta_challenge,.evaluation = Fr::zero(), }, .witness=std::move(G)}; }; - /** * @brief Recomputes the new claim commitment [G] given the proof and * the challenge r. No verification happens so this function always succeeds. @@ -116,12 +110,14 @@ template class SingleBatchOpeningScheme { * @return OpeningClaim */ static OpeningClaim reduce_verify(std::span> claims, - const Proof& proof, - const auto& transcript) + VerifierTranscript& transcript) { const size_t num_claims = claims.size(); - const Fr nu = Fr::serialize_from_buffer(transcript->get_challenge("nu").begin()); - const Fr z_challenge = Fr::serialize_from_buffer(transcript->get_challenge("z").begin()); + + const Fr nu = transcript.get_challenge("Shplonk:nu"); + auto Q_commitment = transcript.template receive_from_prover("Shplonk:Q"); + + const Fr zeta_challenge = transcript.get_challenge("Shplonk:zeta"); // compute simulated commitment to [G] as a linear combination of // [Q], { [fⱼ] }, [1]: @@ -133,17 +129,15 @@ template class SingleBatchOpeningScheme { // [G] = [Q] - ∑ⱼ ρʲ / ( r − xⱼ )⋅[fⱼ] + G₀⋅[1] // = [Q] - [∑ⱼ ρʲ ⋅ ( fⱼ(X) − vⱼ) / ( r − xⱼ )] - Commitment G_commitment = proof; + Commitment G_commitment = Q_commitment; // {ẑⱼ(r)}ⱼ , where ẑⱼ(r) = 1/zⱼ(r) = 1/(r - xⱼ) std::vector inverse_vanishing_evals; inverse_vanishing_evals.reserve(num_claims); - { - for (const auto& claim : claims) { - inverse_vanishing_evals.emplace_back(z_challenge - claim.opening_pair.query); - } - Fr::batch_invert(inverse_vanishing_evals); + for (const auto& claim : claims) { + inverse_vanishing_evals.emplace_back(zeta_challenge - claim.opening_pair.query); } + Fr::batch_invert(inverse_vanishing_evals); Fr current_nu{ Fr::one() }; for (size_t j = 0; j < num_claims; ++j) { @@ -163,7 +157,7 @@ template class SingleBatchOpeningScheme { G_commitment += Commitment::one() * G_commitment_constant; // Return opening pair (z, 0) and commitment [G] - return { { z_challenge, Fr::zero() }, G_commitment }; + return { .opening_pair = { .query = zeta_challenge, .evaluation = Fr::zero() }, .commitment = G_commitment }; }; }; } // namespace honk::pcs::shplonk diff --git a/cpp/src/aztec/honk/pcs/wrapper.hpp b/cpp/src/aztec/honk/pcs/wrapper.hpp deleted file mode 100644 index 3fc43933a5..0000000000 --- a/cpp/src/aztec/honk/pcs/wrapper.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "commitment_key.hpp" -#include "gemini/gemini.hpp" -#include - -namespace honk { - -struct OpeningProof { - std::vector gemini; - barretenberg::g1::affine_element shplonk; - barretenberg::g1::affine_element kzg; -}; - -} // namespace honk \ No newline at end of file diff --git a/cpp/src/aztec/honk/proof_system/prover.cpp b/cpp/src/aztec/honk/proof_system/prover.cpp index c47d8c54dc..d67dc1537c 100644 --- a/cpp/src/aztec/honk/proof_system/prover.cpp +++ b/cpp/src/aztec/honk/proof_system/prover.cpp @@ -15,9 +15,11 @@ #include #include #include +#include "honk/pcs/gemini/gemini.hpp" +#include "honk/sumcheck/relations/relation.hpp" +#include "honk/utils/public_inputs.hpp" #include "polynomials/polynomial.hpp" #include "proof_system/flavor/flavor.hpp" -#include "transcript/transcript_wrappers.hpp" #include #include @@ -38,10 +40,8 @@ using POLYNOMIAL = bonk::StandardArithmetization::POLYNOMIAL; * */ template Prover::Prover(std::vector&& wire_polys, - std::shared_ptr input_key, - const transcript::Manifest& input_manifest) - : transcript(input_manifest, settings::hash_type, settings::num_challenge_bytes) - , wire_polynomials(wire_polys) // TODO(luke): move these properly + std::shared_ptr input_key) + : wire_polynomials(wire_polys) // TODO(luke): move these properly , key(input_key) , commitment_key(std::make_unique( input_key->circuit_size, @@ -66,6 +66,13 @@ Prover::Prover(std::vector&& wire_polys, prover_polynomials[POLYNOMIAL::W_L] = wire_polynomials[0]; prover_polynomials[POLYNOMIAL::W_R] = wire_polynomials[1]; prover_polynomials[POLYNOMIAL::W_O] = wire_polynomials[2]; + + // Add public inputs to transcript from the second wire polynomial + std::span public_wires_source = prover_polynomials[POLYNOMIAL::W_R]; + + for (size_t i = 0; i < key->num_public_inputs; ++i) { + public_inputs.emplace_back(public_wires_source[i]); + } } /** @@ -78,7 +85,7 @@ template void Prover::compute_wire_commitments() for (size_t i = 0; i < settings::program_width; ++i) { auto commitment = commitment_key->commit(wire_polynomials[i]); - transcript.add_element("W_" + std::to_string(i + 1), commitment.to_buffer()); + transcript.send_to_verifier("W_" + std::to_string(i + 1), commitment); } } @@ -178,8 +185,8 @@ template Polynomial Prover::compute_grand_product_ // Construct permutation polynomial 'z_perm' in lagrange form as: // z_perm = [0 numerator_accumulator[0][0] numerator_accumulator[0][1] ... numerator_accumulator[0][n-2] 0] Polynomial z_perm(key->circuit_size); - // We'll need to shift this polynomial to the left by dividing it by X in gemini, so the the 0-th coefficient should - // stay zero + // We'll need to shift this polynomial to the left by dividing it by X in gemini, so the the 0-th coefficient + // should stay zero copy_polynomial(numerator_accumulator[0], &z_perm[1], key->circuit_size - 1, key->circuit_size - 1); // free memory allocated for scratch space @@ -201,19 +208,16 @@ template void Prover::execute_preamble_round() // queue.flush_queue(); // NOTE: Don't remove; we may reinstate the queue - transcript.add_element("circuit_size", - { static_cast(key->circuit_size >> 24), - static_cast(key->circuit_size >> 16), - static_cast(key->circuit_size >> 8), - static_cast(key->circuit_size) }); + const auto circuit_size = static_cast(key->circuit_size); + const auto num_public_inputs = static_cast(key->num_public_inputs); - transcript.add_element("public_input_size", - { static_cast(key->num_public_inputs >> 24), - static_cast(key->num_public_inputs >> 16), - static_cast(key->num_public_inputs >> 8), - static_cast(key->num_public_inputs) }); + transcript.send_to_verifier("circuit_size", circuit_size); + transcript.send_to_verifier("public_input_size", num_public_inputs); - transcript.apply_fiat_shamir("init"); + for (size_t i = 0; i < key->num_public_inputs; ++i) { + auto public_input_i = public_inputs[i]; + transcript.send_to_verifier("public_input_" + std::to_string(i), public_input_i); + } } /** @@ -222,17 +226,9 @@ template void Prover::execute_preamble_round() * */ template void Prover::execute_wire_commitments_round() { + // queue.flush_queue(); // NOTE: Don't remove; we may reinstate the queue compute_wire_commitments(); - - // Add public inputs to transcript from the second wire polynomial - const Polynomial& public_wires_source = wire_polynomials[1]; - - std::vector public_wires; - for (size_t i = 0; i < key->num_public_inputs; ++i) { - public_wires.push_back(public_wires_source[i]); - } - transcript.add_element("public_inputs", ::to_buffer(public_wires)); } /** @@ -241,7 +237,7 @@ template void Prover::execute_wire_commitments_rou template void Prover::execute_tables_round() { // queue.flush_queue(); // NOTE: Don't remove; we may reinstate the queue - transcript.apply_fiat_shamir("eta"); + // transcript.apply_fiat_shamir("eta"); // No operations are needed here for Standard Honk } @@ -254,14 +250,20 @@ template void Prover::execute_grand_product_comput { // queue.flush_queue(); // NOTE: Don't remove; we may reinstate the queue - transcript.apply_fiat_shamir("beta"); + auto [beta, gamma] = transcript.get_challenges("beta", "gamma"); + auto public_input_delta = compute_public_input_delta(public_inputs, beta, gamma, key->circuit_size); + + relation_parameters = sumcheck::RelationParameters{ + .beta = beta, + .gamma = gamma, + .public_input_delta = public_input_delta, + }; - auto beta = transcript.get_challenge_field_element("beta", 0); - auto gamma = transcript.get_challenge_field_element("beta", 1); z_permutation = compute_grand_product_polynomial(beta, gamma); // The actual polynomial is of length n+1, but commitment key is just n, so we need to limit it auto commitment = commitment_key->commit(z_permutation); - transcript.add_element("Z_PERM", commitment.to_buffer()); + + transcript.send_to_verifier("Z_PERM", commitment); prover_polynomials[POLYNOMIAL::Z_PERM] = z_permutation; prover_polynomials[POLYNOMIAL::Z_PERM_SHIFT] = z_permutation.shifted(); @@ -277,16 +279,13 @@ template void Prover::execute_relation_check_round // queue.flush_queue(); // NOTE: Don't remove; we may reinstate the queue using Sumcheck = sumcheck::Sumcheck; - transcript.apply_fiat_shamir("alpha"); - - auto sumcheck = Sumcheck(key->circuit_size, transcript); + auto sumcheck = Sumcheck(key->circuit_size, relation_parameters); - sumcheck.execute_prover(prover_polynomials); + sumcheck_output = sumcheck.execute_prover(prover_polynomials, transcript); } /** @@ -299,20 +298,12 @@ template void Prover::execute_univariatization_rou 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()); + Fr rho = transcript.get_challenge("rho"); 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"); + auto [opening_point, multivariate_evaluations] = sumcheck_output; // Batch the unshifted polynomials and the to-be-shifted polynomials using ρ Polynomial batched_poly_unshifted(key->circuit_size); // batched unshifted polynomials @@ -327,7 +318,7 @@ template void Prover::execute_univariatization_rou opening_point, std::move(batched_poly_unshifted), std::move(batched_poly_to_be_shifted), - &transcript); + transcript); } /** @@ -336,9 +327,9 @@ template void Prover::execute_univariatization_rou * */ template void Prover::execute_pcs_evaluation_round() { - // TODO(luke): This functionality is performed within Gemini::reduce_prove(), called in the previous round. In the - // future we could (1) split the Gemini functionality to match the round structure defined here, or (2) remove this - // function from the prover. The former may be necessary to maintain the work_queue paradigm. + // TODO(luke): This functionality is performed within Gemini::reduce_prove(), called in the previous round. In + // the future we could (1) split the Gemini functionality to match the round structure defined here, or (2) + // remove this function from the prover. The former may be necessary to maintain the work_queue paradigm. } /** @@ -350,7 +341,7 @@ template void Prover::execute_pcs_evaluation_round template void Prover::execute_shplonk_round() { shplonk_output = - Shplonk::reduce_prove(commitment_key, gemini_output.opening_pairs, gemini_output.witnesses, &transcript); + Shplonk::reduce_prove(commitment_key, gemini_output.opening_pairs, gemini_output.witnesses, transcript); } /** @@ -359,12 +350,12 @@ template void Prover::execute_shplonk_round() * */ template void Prover::execute_kzg_round() { - KZG::reduce_prove(commitment_key, shplonk_output.opening_pair, shplonk_output.witness, &transcript); + KZG::reduce_prove(commitment_key, shplonk_output.opening_pair, shplonk_output.witness, transcript); } template plonk::proof& Prover::export_proof() { - proof.proof_data = transcript.export_transcript(); + proof.proof_data = transcript.proof_data; return proof; } diff --git a/cpp/src/aztec/honk/proof_system/prover.hpp b/cpp/src/aztec/honk/proof_system/prover.hpp index 9f83676aac..bf843de3c7 100644 --- a/cpp/src/aztec/honk/proof_system/prover.hpp +++ b/cpp/src/aztec/honk/proof_system/prover.hpp @@ -1,16 +1,22 @@ #pragma once + +#include "honk/sumcheck/relations/relation.hpp" +#include "honk/sumcheck/sumcheck.hpp" +#include "honk/transcript/transcript.hpp" +#include +#include +#include +#include + #include "ecc/curves/bn254/fr.hpp" #include "honk/pcs/shplonk/shplonk.hpp" #include "polynomials/polynomial.hpp" #include "proof_system/flavor/flavor.hpp" -#include #include -#include #include #include -#include -#include -#include + +#include #include #include #include @@ -23,15 +29,13 @@ namespace honk { -using Fr = barretenberg::fr; - template class Prover { + using Fr = barretenberg::fr; + public: // TODO(luke): update this appropriately to work with Honk Manifest - Prover(std::vector&& wire_polys, - std::shared_ptr input_key = nullptr, - const transcript::Manifest& manifest = transcript::Manifest()); + Prover(std::vector&& wire_polys, std::shared_ptr input_key = nullptr); void execute_preamble_round(); void execute_wire_commitments_round(); @@ -52,11 +56,15 @@ template class Prover { plonk::proof& export_proof(); plonk::proof& construct_proof(); - transcript::StandardTranscript transcript; + ProverTranscript transcript; + + std::vector public_inputs; std::vector wire_polynomials; barretenberg::polynomial z_permutation; + sumcheck::RelationParameters relation_parameters; + std::shared_ptr key; std::shared_ptr commitment_key; @@ -80,6 +88,7 @@ template class Prover { // This makes 'settings' accesible from Prover using settings_ = settings; + sumcheck::SumcheckOutput sumcheck_output; pcs::gemini::ProverOutput gemini_output; pcs::shplonk::ProverOutput shplonk_output; diff --git a/cpp/src/aztec/honk/proof_system/verifier.cpp b/cpp/src/aztec/honk/proof_system/verifier.cpp index 11e5a30be2..bd862fa745 100644 --- a/cpp/src/aztec/honk/proof_system/verifier.cpp +++ b/cpp/src/aztec/honk/proof_system/verifier.cpp @@ -1,50 +1,42 @@ -#include -#include -#include -#include -#include + #include "./verifier.hpp" -#include "../../plonk/proof_system/public_inputs/public_inputs.hpp" -#include "ecc/curves/bn254/fr.hpp" +#include +#include +#include #include "honk/pcs/commitment_key.hpp" #include "honk/pcs/gemini/gemini.hpp" #include "honk/pcs/kzg/kzg.hpp" +#include "honk/utils/public_inputs.hpp" +#include "honk/sumcheck/sumcheck.hpp" +#include "honk/sumcheck/relations/relation.hpp" +#include "honk/sumcheck/relations/arithmetic_relation.hpp" +#include "honk/sumcheck/relations/grand_product_computation_relation.hpp" +#include "honk/sumcheck/relations/grand_product_initialization_relation.hpp" #include "numeric/bitop/get_msb.hpp" #include "proof_system/flavor/flavor.hpp" -#include "proof_system/polynomial_cache/polynomial_cache.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include -using namespace barretenberg; -using namespace honk::sumcheck; +#include +#include +#include +#include +#include namespace honk { template -Verifier::Verifier(std::shared_ptr verifier_key, - const transcript::Manifest& input_manifest) - : manifest(input_manifest) - , key(verifier_key) +Verifier::Verifier(std::shared_ptr verifier_key) + : key(verifier_key) {} template Verifier::Verifier(Verifier&& other) - : manifest(other.manifest) - , key(other.key) + : key(other.key) , kate_verification_key(std::move(other.kate_verification_key)) {} template Verifier& Verifier::operator=(Verifier&& other) { key = other.key; - manifest = other.manifest; kate_verification_key = (std::move(other.kate_verification_key)); kate_g1_elements.clear(); kate_fr_elements.clear(); @@ -81,7 +73,7 @@ template bool Verifier::verify_pro { using FF = typename program_settings::fr; using Commitment = barretenberg::g1::element; - using Transcript = typename program_settings::Transcript; + using CommitmentAffine = barretenberg::g1::affine_element; using Gemini = pcs::gemini::MultilinearReductionScheme; using Shplonk = pcs::shplonk::SingleBatchOpeningScheme; using KZG = pcs::kzg::UnivariateOpeningScheme; @@ -89,118 +81,107 @@ template bool Verifier::verify_pro const size_t NUM_UNSHIFTED = bonk::StandardArithmetization::NUM_UNSHIFTED_POLYNOMIALS; const size_t NUM_PRECOMPUTED = bonk::StandardArithmetization::NUM_PRECOMPUTED_POLYNOMIALS; - key->program_width = program_settings::program_width; - - size_t log_n(numeric::get_msb(key->circuit_size)); - - // Add the proof data to the transcript, according to the manifest. Also initialise the transcript's hash type - // and challenge bytes. - auto transcript = transcript::StandardTranscript( - proof.proof_data, manifest, program_settings::hash_type, program_settings::num_challenge_bytes); - - // Add the circuit size and the number of public inputs) to the transcript. - transcript.add_element("circuit_size", - { static_cast(key->circuit_size >> 24), - static_cast(key->circuit_size >> 16), - static_cast(key->circuit_size >> 8), - static_cast(key->circuit_size) }); - - transcript.add_element("public_input_size", - { static_cast(key->num_public_inputs >> 24), - static_cast(key->num_public_inputs >> 16), - static_cast(key->num_public_inputs >> 8), - static_cast(key->num_public_inputs) }); - - // Compute challenges from the proof data, based on the manifest, using the Fiat-Shamir heuristic - transcript.apply_fiat_shamir("init"); - transcript.apply_fiat_shamir("eta"); - transcript.apply_fiat_shamir("beta"); - transcript.apply_fiat_shamir("alpha"); - for (size_t idx = 0; idx < log_n; idx++) { - transcript.apply_fiat_shamir("u_" + std::to_string(idx)); + constexpr auto width = program_settings::program_width; + + VerifierTranscript transcript{ proof.proof_data }; + + // TODO(Adrian): Change the initialization of the transcript to take the VK hash? Also need to add the + // commitments... + auto circuit_size = transcript.template receive_from_prover("circuit_size"); + [[maybe_unused]] auto public_input_size = transcript.template receive_from_prover("public_input_size"); + + if (circuit_size != key->circuit_size) { + return false; + } + if (public_input_size != key->num_public_inputs) { + return false; + } + + std::vector public_inputs; + for (size_t i = 0; i < public_input_size; ++i) { + auto public_input_i = transcript.template receive_from_prover("public_inputs_" + std::to_string(i)); + public_inputs.emplace_back(public_input_i); + } + + std::array wire_commitments; + for (size_t i = 0; i < width; ++i) { + wire_commitments[i] = transcript.template receive_from_prover("W_" + std::to_string(i + 1)); } - transcript.apply_fiat_shamir("rho"); - transcript.apply_fiat_shamir("r"); - transcript.apply_fiat_shamir("nu"); - transcript.apply_fiat_shamir("z"); - transcript.apply_fiat_shamir("separator"); + + auto [beta, gamma] = transcript.get_challenges("beta", "gamma"); + + auto public_input_delta = compute_public_input_delta(public_inputs, beta, gamma, circuit_size); + + auto z_permutation_commitment = transcript.template receive_from_prover("Z_PERM"); // // TODO(Cody): Compute some basic public polys like id(X), pow(X), and any required Lagrange polys + sumcheck::RelationParameters relation_parameters{ + .beta = beta, + .gamma = gamma, + .public_input_delta = public_input_delta, + }; + // Execute Sumcheck Verifier - auto sumcheck = Sumcheck(transcript); - bool sumcheck_result = sumcheck.execute_verifier(); + auto sumcheck = sumcheck::Sumcheck(circuit_size, relation_parameters); + auto sumcheck_result = sumcheck.execute_verifier(transcript); + + if (!sumcheck_result.has_value()) { + return false; + } + // Get opening point and vector of multivariate evaluations produced by Sumcheck + // - Multivariate opening point u = (u_0, ..., u_{d-1}) + auto [opening_point, multivariate_evaluations] = *sumcheck_result; // Execute Gemini/Shplonk verification: // Construct inputs for Gemini verifier: // - Multivariate opening point u = (u_0, ..., u_{d-1}) // - 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 - 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"); + FF rho = transcript.get_challenge("rho"); + std::vector rhos = Gemini::powers_of_rho(rho, NUM_POLYNOMIALS); // Compute batched multivariate evaluation - Fr batched_evaluation = Fr::zero(); + FF batched_evaluation = FF::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 = key->commitments[bonk::StandardArithmetization::ENUM_TO_COMM[i]]; - } else { // if witness, commitment comes from prover (via transcript) - commitment = transcript.get_group_element(bonk::StandardArithmetization::ENUM_TO_COMM[i]); - } + for (size_t i = 0; i < NUM_PRECOMPUTED; ++i) { + // if precomputed, commitment comes from verification key + Commitment commitment = key->commitments[bonk::StandardArithmetization::ENUM_TO_COMM[i]]; batched_commitment_unshifted += commitment * rhos[i]; } + // add wire commitments and evals + for (size_t i = 0; i < width; ++i) { + // if witness, commitment comes from prover + Commitment commitment = wire_commitments[i]; + batched_commitment_unshifted += commitment * rhos[NUM_PRECOMPUTED + i]; + } // 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 - auto gemini_proof = Gemini::reconstruct_proof_from_transcript(&transcript, key->log_circuit_size); + batched_commitment_unshifted += z_permutation_commitment * rhos[NUM_PRECOMPUTED + width]; + batched_commitment_to_be_shifted = z_permutation_commitment * rhos[NUM_UNSHIFTED]; // 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, - 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"); + auto gemini_claim = Gemini::reduce_verify( + opening_point, batched_evaluation, batched_commitment_unshifted, batched_commitment_to_be_shifted, transcript); // Produce a Shplonk claim: commitment [Q] - [Q_z], evaluation zero (at random challenge z) - auto shplonk_claim = Shplonk::reduce_verify(gemini_claim, shplonk_proof, &transcript); - - // Reconstruct the KZG Proof (commitment [W]_1) from the transcript - auto kzg_proof = transcript.get_group_element("W"); + auto shplonk_claim = Shplonk::reduce_verify(gemini_claim, transcript); // Aggregate inputs [Q] - [Q_z] and [W] into an 'accumulator' (can perform pairing check on result) - auto kzg_claim = KZG::reduce_verify(shplonk_claim, kzg_proof); + auto kzg_claim = KZG::reduce_verify(shplonk_claim, transcript); // Do final pairing check bool pairing_result = kzg_claim.verify(kate_verification_key); diff --git a/cpp/src/aztec/honk/proof_system/verifier.hpp b/cpp/src/aztec/honk/proof_system/verifier.hpp index 3cf8e3a092..dc58f06c3c 100644 --- a/cpp/src/aztec/honk/proof_system/verifier.hpp +++ b/cpp/src/aztec/honk/proof_system/verifier.hpp @@ -1,23 +1,17 @@ #pragma once -#include "../../plonk/proof_system/types/proof.hpp" -#include "./program_settings.hpp" -#include "../../proof_system/verification_key/verification_key.hpp" -#include -#include -#include "../sumcheck/sumcheck.hpp" -#include "../sumcheck/relations/arithmetic_relation.hpp" #include "honk/pcs/commitment_key.hpp" -#include "proof_system/flavor/flavor.hpp" #include #include #include +#include "honk/proof_system/program_settings.hpp" +#include "plonk/proof_system/types/proof.hpp" +#include namespace honk { template class Verifier { public: - Verifier(std::shared_ptr verifier_key = nullptr, - const transcript::Manifest& manifest = honk::StandardHonk::create_manifest(0)); + Verifier(std::shared_ptr verifier_key = nullptr); Verifier(Verifier&& other); Verifier(const Verifier& other) = delete; Verifier& operator=(const Verifier& other) = delete; @@ -29,7 +23,6 @@ template class Verifier { // so it's easy to extract particular elements without looking at the manifest and counting // numbers of bytes, for instance. bool verify_proof(const plonk::proof& proof); - transcript::Manifest manifest; std::shared_ptr key; std::map kate_g1_elements; diff --git a/cpp/src/aztec/honk/proof_system/verifier.test.cpp b/cpp/src/aztec/honk/proof_system/verifier.test.cpp index 6aba7cb24f..7495fe29a1 100644 --- a/cpp/src/aztec/honk/proof_system/verifier.test.cpp +++ b/cpp/src/aztec/honk/proof_system/verifier.test.cpp @@ -21,12 +21,6 @@ namespace test_honk_verifier { template class VerifierTests : public testing::Test { public: - // TODO(luke): replace this with an appropriate mock honk manifest - static transcript::Manifest create_manifest(const size_t num_public_inputs, const size_t num_sumcheck_rounds) - { - return honk::StandardHonk::create_manifest(num_public_inputs, num_sumcheck_rounds); - } - static StandardVerifier generate_verifier(std::shared_ptr circuit_proving_key) { std::array poly_coefficients; @@ -67,7 +61,7 @@ template class VerifierTests : public testing::Test { circuit_verification_key->commitments.insert({ "SIGMA_2", commitments[6] }); circuit_verification_key->commitments.insert({ "SIGMA_3", commitments[7] }); - StandardVerifier verifier(circuit_verification_key, create_manifest(0, circuit_proving_key->log_circuit_size)); + StandardVerifier verifier(circuit_verification_key); // TODO(luke): set verifier PCS ala the following: // std::unique_ptr> kate_commitment_scheme = @@ -196,8 +190,7 @@ template class VerifierTests : public testing::Test { // TODO(Cody): This should be more generic std::vector witness_polynomials; - auto prover = StandardProver( - std::move(witness_polynomials), proving_key, create_manifest(0, proving_key->log_circuit_size)); + auto prover = StandardProver(std::move(witness_polynomials), proving_key); std::unique_ptr kate_commitment_key = std::make_unique(proving_key->circuit_size, "../srs_db/ignition"); diff --git a/cpp/src/aztec/honk/sumcheck/polynomials/multivariates.test.cpp b/cpp/src/aztec/honk/sumcheck/polynomials/multivariates.test.cpp index d4da84d4b3..fad44271a4 100644 --- a/cpp/src/aztec/honk/sumcheck/polynomials/multivariates.test.cpp +++ b/cpp/src/aztec/honk/sumcheck/polynomials/multivariates.test.cpp @@ -11,7 +11,6 @@ namespace test_sumcheck_polynomials { template class MultivariatesTests : public testing::Test {}; using FieldTypes = testing::Types; -using Transcript = transcript::StandardTranscript; TYPED_TEST_SUITE(MultivariatesTests, FieldTypes); #define MULTIVARIATES_TESTS_TYPE_ALIASES using FF = TypeParam; @@ -58,24 +57,25 @@ TYPED_TEST(MultivariatesTests, FoldTwoRoundsSpecial) std::array f0 = { v00, v10, v01, v11 }; auto full_polynomials = std::array, 1>({ f0 }); - auto transcript = Transcript(transcript::Manifest()); - auto sumcheck = Sumcheck(multivariate_n, transcript); + auto folded_polynomials = std::array{ std::vector(f0.begin(), f0.end()) }; + + auto sumcheck = Sumcheck(multivariate_n, {}); FF round_challenge_0 = { 0x6c7301b49d85a46c, 0x44311531e39c64f6, 0xb13d66d8d6c1a24c, 0x04410c360230a295 }; round_challenge_0.self_to_montgomery_form(); FF expected_lo = v00 * (FF(1) - round_challenge_0) + v10 * round_challenge_0; FF expected_hi = v01 * (FF(1) - round_challenge_0) + v11 * round_challenge_0; - sumcheck.fold(full_polynomials, multivariate_n, round_challenge_0); + sumcheck.fold(folded_polynomials, full_polynomials, multivariate_n, round_challenge_0); - EXPECT_EQ(sumcheck.folded_polynomials[0][0], round_challenge_0); - EXPECT_EQ(sumcheck.folded_polynomials[0][1], FF(0)); + EXPECT_EQ(folded_polynomials[0][0], round_challenge_0); + EXPECT_EQ(folded_polynomials[0][1], FF(0)); FF round_challenge_1 = 2; FF expected_val = expected_lo * (FF(1) - round_challenge_1) + expected_hi * round_challenge_1; - sumcheck.fold(sumcheck.folded_polynomials, multivariate_n >> 1, round_challenge_1); - EXPECT_EQ(sumcheck.folded_polynomials[0][0], expected_val); + sumcheck.fold(folded_polynomials, folded_polynomials, multivariate_n >> 1, round_challenge_1); + EXPECT_EQ(folded_polynomials[0][0], expected_val); } TYPED_TEST(MultivariatesTests, FoldTwoRoundsGeneric) @@ -93,22 +93,23 @@ TYPED_TEST(MultivariatesTests, FoldTwoRoundsGeneric) std::array f0 = { v00, v10, v01, v11 }; auto full_polynomials = std::array, 1>({ f0 }); - auto transcript = Transcript(transcript::Manifest()); - auto sumcheck = Sumcheck(multivariate_n, transcript); + auto folded_polynomials = std::array{ std::vector(f0.begin(), f0.end()) }; + + auto sumcheck = Sumcheck(multivariate_n, {}); FF round_challenge_0 = FF::random_element(); FF expected_lo = v00 * (FF(1) - round_challenge_0) + v10 * round_challenge_0; FF expected_hi = v01 * (FF(1) - round_challenge_0) + v11 * round_challenge_0; - sumcheck.fold(full_polynomials, multivariate_n, round_challenge_0); + sumcheck.fold(folded_polynomials, full_polynomials, multivariate_n, round_challenge_0); - EXPECT_EQ(sumcheck.folded_polynomials[0][0], expected_lo); - EXPECT_EQ(sumcheck.folded_polynomials[0][1], expected_hi); + EXPECT_EQ(folded_polynomials[0][0], expected_lo); + EXPECT_EQ(folded_polynomials[0][1], expected_hi); FF round_challenge_1 = FF::random_element(); FF expected_val = expected_lo * (FF(1) - round_challenge_1) + expected_hi * round_challenge_1; - sumcheck.fold(sumcheck.folded_polynomials, multivariate_n >> 1, round_challenge_1); - EXPECT_EQ(sumcheck.folded_polynomials[0][0], expected_val); + sumcheck.fold(folded_polynomials, folded_polynomials, multivariate_n >> 1, round_challenge_1); + EXPECT_EQ(folded_polynomials[0][0], expected_val); } /* @@ -152,8 +153,8 @@ TYPED_TEST(MultivariatesTests, FoldThreeRoundsSpecial) std::array f0 = { v000, v100, v010, v110, v001, v101, v011, v111 }; auto full_polynomials = std::array, 1>({ f0 }); - auto transcript = Transcript(transcript::Manifest()); - auto sumcheck = Sumcheck(multivariate_n, transcript); + auto folded_polynomials = std::array{ std::vector(f0.begin(), f0.end()) }; + auto sumcheck = Sumcheck(multivariate_n, {}); FF round_challenge_0 = 1; FF expected_q1 = v000 * (FF(1) - round_challenge_0) + v100 * round_challenge_0; // 2 @@ -161,25 +162,25 @@ TYPED_TEST(MultivariatesTests, FoldThreeRoundsSpecial) FF expected_q3 = v001 * (FF(1) - round_challenge_0) + v101 * round_challenge_0; // 6 FF expected_q4 = v011 * (FF(1) - round_challenge_0) + v111 * round_challenge_0; // 8 - sumcheck.fold(full_polynomials, multivariate_n, round_challenge_0); + sumcheck.fold(folded_polynomials, full_polynomials, multivariate_n, round_challenge_0); - EXPECT_EQ(sumcheck.folded_polynomials[0][0], expected_q1); - EXPECT_EQ(sumcheck.folded_polynomials[0][1], expected_q2); - EXPECT_EQ(sumcheck.folded_polynomials[0][2], expected_q3); - EXPECT_EQ(sumcheck.folded_polynomials[0][3], expected_q4); + EXPECT_EQ(folded_polynomials[0][0], expected_q1); + EXPECT_EQ(folded_polynomials[0][1], expected_q2); + EXPECT_EQ(folded_polynomials[0][2], expected_q3); + EXPECT_EQ(folded_polynomials[0][3], expected_q4); FF round_challenge_1 = 2; FF expected_lo = expected_q1 * (FF(1) - round_challenge_1) + expected_q2 * round_challenge_1; // 6 FF expected_hi = expected_q3 * (FF(1) - round_challenge_1) + expected_q4 * round_challenge_1; // 10 - sumcheck.fold(sumcheck.folded_polynomials, multivariate_n >> 1, round_challenge_1); - EXPECT_EQ(sumcheck.folded_polynomials[0][0], expected_lo); - EXPECT_EQ(sumcheck.folded_polynomials[0][1], expected_hi); + sumcheck.fold(folded_polynomials, folded_polynomials, multivariate_n >> 1, round_challenge_1); + EXPECT_EQ(folded_polynomials[0][0], expected_lo); + EXPECT_EQ(folded_polynomials[0][1], expected_hi); FF round_challenge_2 = 3; FF expected_val = expected_lo * (FF(1) - round_challenge_2) + expected_hi * round_challenge_2; // 18 - sumcheck.fold(sumcheck.folded_polynomials, multivariate_n >> 2, round_challenge_2); - EXPECT_EQ(sumcheck.folded_polynomials[0][0], expected_val); + sumcheck.fold(folded_polynomials, folded_polynomials, multivariate_n >> 2, round_challenge_2); + EXPECT_EQ(folded_polynomials[0][0], expected_val); } TYPED_TEST(MultivariatesTests, FoldThreeRoundsGeneric) @@ -201,8 +202,8 @@ TYPED_TEST(MultivariatesTests, FoldThreeRoundsGeneric) std::array f0 = { v000, v100, v010, v110, v001, v101, v011, v111 }; auto full_polynomials = std::array, 1>({ f0 }); - auto transcript = Transcript(transcript::Manifest()); - auto sumcheck = Sumcheck(multivariate_n, transcript); + auto folded_polynomials = std::array{ std::vector(f0.begin(), f0.end()) }; + auto sumcheck = Sumcheck(multivariate_n, {}); FF round_challenge_0 = FF::random_element(); FF expected_q1 = v000 * (FF(1) - round_challenge_0) + v100 * round_challenge_0; @@ -210,25 +211,25 @@ TYPED_TEST(MultivariatesTests, FoldThreeRoundsGeneric) FF expected_q3 = v001 * (FF(1) - round_challenge_0) + v101 * round_challenge_0; FF expected_q4 = v011 * (FF(1) - round_challenge_0) + v111 * round_challenge_0; - sumcheck.fold(full_polynomials, multivariate_n, round_challenge_0); + sumcheck.fold(folded_polynomials, full_polynomials, multivariate_n, round_challenge_0); - EXPECT_EQ(sumcheck.folded_polynomials[0][0], expected_q1); - EXPECT_EQ(sumcheck.folded_polynomials[0][1], expected_q2); - EXPECT_EQ(sumcheck.folded_polynomials[0][2], expected_q3); - EXPECT_EQ(sumcheck.folded_polynomials[0][3], expected_q4); + EXPECT_EQ(folded_polynomials[0][0], expected_q1); + EXPECT_EQ(folded_polynomials[0][1], expected_q2); + EXPECT_EQ(folded_polynomials[0][2], expected_q3); + EXPECT_EQ(folded_polynomials[0][3], expected_q4); FF round_challenge_1 = FF::random_element(); FF expected_lo = expected_q1 * (FF(1) - round_challenge_1) + expected_q2 * round_challenge_1; FF expected_hi = expected_q3 * (FF(1) - round_challenge_1) + expected_q4 * round_challenge_1; - sumcheck.fold(sumcheck.folded_polynomials, multivariate_n >> 1, round_challenge_1); - EXPECT_EQ(sumcheck.folded_polynomials[0][0], expected_lo); - EXPECT_EQ(sumcheck.folded_polynomials[0][1], expected_hi); + sumcheck.fold(folded_polynomials, folded_polynomials, multivariate_n >> 1, round_challenge_1); + EXPECT_EQ(folded_polynomials[0][0], expected_lo); + EXPECT_EQ(folded_polynomials[0][1], expected_hi); FF round_challenge_2 = FF::random_element(); FF expected_val = expected_lo * (FF(1) - round_challenge_2) + expected_hi * round_challenge_2; - sumcheck.fold(sumcheck.folded_polynomials, multivariate_n >> 2, round_challenge_2); - EXPECT_EQ(sumcheck.folded_polynomials[0][0], expected_val); + sumcheck.fold(folded_polynomials, folded_polynomials, multivariate_n >> 2, round_challenge_2); + EXPECT_EQ(folded_polynomials[0][0], expected_val); } TYPED_TEST(MultivariatesTests, FoldThreeRoundsGenericMultiplePolys) @@ -260,8 +261,12 @@ TYPED_TEST(MultivariatesTests, FoldThreeRoundsGenericMultiplePolys) std::array f2 = { v000[2], v100[2], v010[2], v110[2], v001[2], v101[2], v011[2], v111[2] }; auto full_polynomials = std::array, 3>{ f0, f1, f2 }; - auto transcript = Transcript(transcript::Manifest()); - auto sumcheck = Sumcheck(multivariate_n, transcript); + auto folded_polynomials = std::array{ + std::vector(f0.begin(), f0.end()), + std::vector(f1.begin(), f1.end()), + std::vector(f2.begin(), f2.end()), + }; + auto sumcheck = Sumcheck(multivariate_n, {}); std::array expected_q1; std::array expected_q2; @@ -275,12 +280,12 @@ TYPED_TEST(MultivariatesTests, FoldThreeRoundsGenericMultiplePolys) expected_q4[i] = v011[i] * (FF(1) - round_challenge_0) + v111[i] * round_challenge_0; } - sumcheck.fold(full_polynomials, multivariate_n, round_challenge_0); + sumcheck.fold(folded_polynomials, full_polynomials, multivariate_n, round_challenge_0); for (size_t i = 0; i < 3; i++) { - EXPECT_EQ(sumcheck.folded_polynomials[i][0], expected_q1[i]); - EXPECT_EQ(sumcheck.folded_polynomials[i][1], expected_q2[i]); - EXPECT_EQ(sumcheck.folded_polynomials[i][2], expected_q3[i]); - EXPECT_EQ(sumcheck.folded_polynomials[i][3], expected_q4[i]); + EXPECT_EQ(folded_polynomials[i][0], expected_q1[i]); + EXPECT_EQ(folded_polynomials[i][1], expected_q2[i]); + EXPECT_EQ(folded_polynomials[i][2], expected_q3[i]); + EXPECT_EQ(folded_polynomials[i][3], expected_q4[i]); } FF round_challenge_1 = FF::random_element(); @@ -290,19 +295,19 @@ TYPED_TEST(MultivariatesTests, FoldThreeRoundsGenericMultiplePolys) expected_lo[i] = expected_q1[i] * (FF(1) - round_challenge_1) + expected_q2[i] * round_challenge_1; expected_hi[i] = expected_q3[i] * (FF(1) - round_challenge_1) + expected_q4[i] * round_challenge_1; } - sumcheck.fold(sumcheck.folded_polynomials, multivariate_n >> 1, round_challenge_1); + sumcheck.fold(folded_polynomials, folded_polynomials, multivariate_n >> 1, round_challenge_1); for (size_t i = 0; i < 3; i++) { - EXPECT_EQ(sumcheck.folded_polynomials[i][0], expected_lo[i]); - EXPECT_EQ(sumcheck.folded_polynomials[i][1], expected_hi[i]); + EXPECT_EQ(folded_polynomials[i][0], expected_lo[i]); + EXPECT_EQ(folded_polynomials[i][1], expected_hi[i]); } FF round_challenge_2 = FF::random_element(); std::array expected_val; for (size_t i = 0; i < 3; i++) { expected_val[i] = expected_lo[i] * (FF(1) - round_challenge_2) + expected_hi[i] * round_challenge_2; } - sumcheck.fold(sumcheck.folded_polynomials, multivariate_n >> 2, round_challenge_2); + sumcheck.fold(folded_polynomials, folded_polynomials, multivariate_n >> 2, round_challenge_2); for (size_t i = 0; i < 3; i++) { - EXPECT_EQ(sumcheck.folded_polynomials[i][0], expected_val[i]); + EXPECT_EQ(folded_polynomials[i][0], expected_val[i]); } } diff --git a/cpp/src/aztec/honk/sumcheck/polynomials/univariate.hpp b/cpp/src/aztec/honk/sumcheck/polynomials/univariate.hpp index 961b20803b..e5c22201c9 100644 --- a/cpp/src/aztec/honk/sumcheck/polynomials/univariate.hpp +++ b/cpp/src/aztec/honk/sumcheck/polynomials/univariate.hpp @@ -211,6 +211,18 @@ template class Univariate { } }; +template inline void read(B& it, Univariate& univariate) +{ + using serialize::read; + read(it, univariate.evaluations); +} + +template inline void write(B& it, Univariate const& univariate) +{ + using serialize::write; + write(it, univariate.evaluations); +} + template class UnivariateView { public: std::span evaluations; diff --git a/cpp/src/aztec/honk/sumcheck/relations/arithmetic_relation.hpp b/cpp/src/aztec/honk/sumcheck/relations/arithmetic_relation.hpp index f79794979a..575166cd53 100644 --- a/cpp/src/aztec/honk/sumcheck/relations/arithmetic_relation.hpp +++ b/cpp/src/aztec/honk/sumcheck/relations/arithmetic_relation.hpp @@ -49,9 +49,8 @@ template class ArithmeticRelation : public Relation { evals += tmp; }; - void add_full_relation_value_contribution(FF& full_honk_relation_value, - const auto& purported_evaluations, - const RelationParameters&) const + static FF evaluate_full_relation_value_contribution(const auto& purported_evaluations, + const RelationParameters&) { auto w_l = purported_evaluations[MULTIVARIATE::W_L]; auto w_r = purported_evaluations[MULTIVARIATE::W_R]; @@ -62,10 +61,11 @@ template class ArithmeticRelation : public Relation { auto q_o = purported_evaluations[MULTIVARIATE::Q_O]; auto q_c = purported_evaluations[MULTIVARIATE::Q_C]; - full_honk_relation_value += w_l * (q_m * w_r + q_l); - full_honk_relation_value += q_r * w_r; - full_honk_relation_value += q_o * w_o; - full_honk_relation_value += q_c; + FF eval = w_l * (q_m * w_r + q_l); + eval += q_r * w_r; + eval += q_o * w_o; + eval += q_c; + return eval; }; }; } // namespace honk::sumcheck diff --git a/cpp/src/aztec/honk/sumcheck/relations/grand_product_computation_relation.hpp b/cpp/src/aztec/honk/sumcheck/relations/grand_product_computation_relation.hpp index bded8f7570..edb0759fbe 100644 --- a/cpp/src/aztec/honk/sumcheck/relations/grand_product_computation_relation.hpp +++ b/cpp/src/aztec/honk/sumcheck/relations/grand_product_computation_relation.hpp @@ -60,9 +60,8 @@ template class GrandProductComputationRelation : public Relation& relation_parameters) const + static FF evaluate_full_relation_value_contribution(const auto& purported_evaluations, + const RelationParameters& relation_parameters) { const auto& beta = relation_parameters.beta; const auto& gamma = relation_parameters.gamma; @@ -83,8 +82,7 @@ template class GrandProductComputationRelation : public Relation class GrandProductInitializationRelation : public Relatio evals += (lagrange_last * z_perm_shift) * scaling_factor; }; - void add_full_relation_value_contribution(FF& full_honk_relation_value, - auto& purported_evaluations, - const RelationParameters&) const + static FF evaluate_full_relation_value_contribution(const auto& purported_evaluations, + const RelationParameters&) { auto z_perm_shift = purported_evaluations[MULTIVARIATE::Z_PERM_SHIFT]; auto lagrange_last = purported_evaluations[MULTIVARIATE::LAGRANGE_LAST]; - full_honk_relation_value += lagrange_last * z_perm_shift; - }; + return lagrange_last * z_perm_shift; + } }; } // namespace honk::sumcheck diff --git a/cpp/src/aztec/honk/sumcheck/relations/relation.hpp b/cpp/src/aztec/honk/sumcheck/relations/relation.hpp index 811d49e124..2269592571 100644 --- a/cpp/src/aztec/honk/sumcheck/relations/relation.hpp +++ b/cpp/src/aztec/honk/sumcheck/relations/relation.hpp @@ -4,10 +4,7 @@ namespace honk::sumcheck { template class Relation {}; // TODO(Cody): Use or eventually remove. -// TODO(Adrian): Remove zeta, alpha as they are not used by the relations. template struct RelationParameters { - FF zeta; - FF alpha; FF beta; FF gamma; FF public_input_delta; diff --git a/cpp/src/aztec/honk/sumcheck/relations/relation.test.cpp b/cpp/src/aztec/honk/sumcheck/relations/relation.test.cpp index 68b9f48105..223cfdee15 100644 --- a/cpp/src/aztec/honk/sumcheck/relations/relation.test.cpp +++ b/cpp/src/aztec/honk/sumcheck/relations/relation.test.cpp @@ -94,9 +94,7 @@ template class SumcheckRelation : public testing::Test { */ RelationParameters compute_mock_relation_parameters() { - return { .zeta = FF::random_element(), - .alpha = FF::random_element(), - .beta = FF::random_element(), + return { .beta = FF::random_element(), .gamma = FF::random_element(), .public_input_delta = FF::random_element() }; } @@ -152,8 +150,8 @@ template class SumcheckRelation : public testing::Test { // Get an array of the same size as `extended_edges` with only the i-th element of each extended edge. std::array evals_i = transposed_univariate_array_at(extended_edges, i); // Evaluate the relation - relation.add_full_relation_value_contribution( - expected_evals_index.value_at(i), evals_i, relation_parameters); + expected_evals_index.value_at(i) = + relation.evaluate_full_relation_value_contribution(evals_i, relation_parameters); } EXPECT_EQ(expected_evals, expected_evals_index); diff --git a/cpp/src/aztec/honk/sumcheck/sumcheck.hpp b/cpp/src/aztec/honk/sumcheck/sumcheck.hpp index b3772e94fd..dff06962b2 100644 --- a/cpp/src/aztec/honk/sumcheck/sumcheck.hpp +++ b/cpp/src/aztec/honk/sumcheck/sumcheck.hpp @@ -1,29 +1,40 @@ #pragma once -#include "common/serialize.hpp" -#include -#include -#include "common/throw_or_abort.hpp" + #include "sumcheck_round.hpp" -#include "polynomials/univariate.hpp" -#include + +#include "honk/sumcheck/polynomials/univariate.hpp" +#include "honk/sumcheck/polynomials/pow.hpp" +#include "honk/sumcheck/relations/relation.hpp" +#include "honk/transcript/transcript.hpp" +#include "proof_system/flavor/flavor.hpp" + +#include +#include #include #include #include #include #include -#include namespace honk::sumcheck { -template class... Relations> class Sumcheck { + +template struct SumcheckOutput { + std::vector evaluation_point; + std::array evaluations; + + bool operator==(const SumcheckOutput& other) const = default; +}; + +template class... Relations> class Sumcheck { public: // TODO(luke): this value is needed here but also lives in sumcheck_round static constexpr size_t MAX_RELATION_LENGTH = std::max({ Relations::RELATION_LENGTH... }); + static constexpr size_t NUM_POLYNOMIALS = bonk::StandardArithmetization::NUM_POLYNOMIALS; - std::array purported_evaluations; - Transcript& transcript; + using RoundUnivariate = Univariate; const size_t multivariate_n; const size_t multivariate_d; - SumcheckRound round; + RelationParameters relation_parameters; /** * @@ -54,64 +65,12 @@ template class... Relations> cl * NOTE: With ~40 columns, prob only want to allocate 256 EdgeGroup's at once to keep stack under 1MB? * TODO(Cody): might want to just do C-style multidimensional array? for guaranteed adjacency? */ - std::array, bonk::StandardArithmetization::NUM_POLYNOMIALS> folded_polynomials; // prover instantiates sumcheck with circuit size and transcript - Sumcheck(size_t multivariate_n, Transcript& transcript) - : transcript(transcript) - , multivariate_n(multivariate_n) + Sumcheck(size_t multivariate_n, const RelationParameters& relation_parameters) + : multivariate_n(multivariate_n) , multivariate_d(numeric::get_msb(multivariate_n)) - , round(multivariate_n, std::tuple(Relations()...)) - { - for (auto& polynomial : folded_polynomials) { - polynomial.resize(multivariate_n >> 1); - } - }; - - // verifier instantiates with transcript alone - explicit Sumcheck(Transcript& transcript) - : transcript(transcript) - , multivariate_n([](std::vector buffer) { - return static_cast(buffer[3]) + (static_cast(buffer[2]) << 8) + - (static_cast(buffer[1]) << 16) + (static_cast(buffer[0]) << 24); - }(transcript.get_element("circuit_size"))) - , multivariate_d(numeric::get_msb(multivariate_n)) - , round(std::tuple(Relations()...)) - { - for (auto& polynomial : folded_polynomials) { - polynomial.resize(multivariate_n >> 1); - } - }; - - /** - * @brief Get all the challenges and computed parameters used in sumcheck in a convenient format - * - * @return RelationParameters - */ - RelationParameters retrieve_proof_parameters() - { - const FF alpha = FF::serialize_from_buffer(transcript.get_challenge("alpha").begin()); - const FF zeta = FF::serialize_from_buffer(transcript.get_challenge("alpha", 1).begin()); - const FF beta = FF::serialize_from_buffer(transcript.get_challenge("beta").begin()); - const FF gamma = FF::serialize_from_buffer(transcript.get_challenge("beta", 1).begin()); - const auto public_input_size_vector = transcript.get_element("public_input_size"); - const size_t public_input_size = (static_cast(public_input_size_vector[0]) << 24) | - (static_cast(public_input_size_vector[1]) << 16) | - (static_cast(public_input_size_vector[2]) << 8) | - - static_cast(public_input_size_vector[3]); - const auto circut_size_vector = transcript.get_element("circuit_size"); - const size_t n = (static_cast(circut_size_vector[0]) << 24) | - (static_cast(circut_size_vector[1]) << 16) | - (static_cast(circut_size_vector[2]) << 8) | static_cast(circut_size_vector[3]); - std::vector public_inputs = many_from_buffer(transcript.get_element("public_inputs")); - ASSERT(public_inputs.size() == public_input_size); - FF public_input_delta = honk::compute_public_input_delta(public_inputs, beta, gamma, n); - const RelationParameters relation_parameters = RelationParameters{ - .zeta = zeta, .alpha = alpha, .beta = beta, .gamma = gamma, .public_input_delta = public_input_delta - }; - return relation_parameters; - } + , relation_parameters(relation_parameters){}; /** * @brief Compute univariate restriction place in transcript, generate challenge, fold,... repeat until final round, @@ -119,20 +78,33 @@ template class... Relations> cl * * @details */ - void execute_prover(auto full_polynomials) // pass by value, not by reference + SumcheckOutput execute_prover(auto full_polynomials, // pass by value, not by reference + ProverTranscript& transcript) { + auto [alpha, zeta] = transcript.get_challenges("Sumcheck:alpha", "Sumcheck:zeta"); + + SumcheckRound round(multivariate_n, std::tuple(Relations()...)); + + std::array, NUM_POLYNOMIALS> folded_polynomials; + for (auto& polynomial : folded_polynomials) { + polynomial.resize(multivariate_n >> 1); + } + + std::vector evaluation_points; + evaluation_points.reserve(multivariate_d); + + PowUnivariate pow_univariate(zeta); + // First round - // This populates folded_polynomials. + RoundUnivariate round_univariate = + round.compute_univariate(full_polynomials, relation_parameters, pow_univariate, alpha); - const auto relation_parameters = retrieve_proof_parameters(); - PowUnivariate pow_univariate(relation_parameters.zeta); + transcript.send_to_verifier("Sumcheck:T_0", round_univariate); + FF round_challenge = transcript.get_challenge("Sumcheck:u_0"); + evaluation_points.emplace_back(round_challenge); - auto round_univariate = round.compute_univariate(full_polynomials, relation_parameters, pow_univariate); - transcript.add_element("univariate_0", round_univariate.to_buffer()); - std::string challenge_label = "u_0"; - transcript.apply_fiat_shamir(challenge_label); - FF round_challenge = FF::serialize_from_buffer(transcript.get_challenge(challenge_label).begin()); - fold(full_polynomials, multivariate_n, round_challenge); + // This populates folded_polynomials. + fold(folded_polynomials, full_polynomials, multivariate_n, round_challenge); pow_univariate.partially_evaluate(round_challenge); round.round_size = round.round_size >> 1; // TODO(Cody): Maybe fold should do this and release memory? @@ -140,22 +112,28 @@ template class... Relations> cl // We operate on folded_polynomials in place. for (size_t round_idx = 1; round_idx < multivariate_d; round_idx++) { // Write the round univariate to the transcript - round_univariate = round.compute_univariate(folded_polynomials, relation_parameters, pow_univariate); - transcript.add_element("univariate_" + std::to_string(round_idx), round_univariate.to_buffer()); - challenge_label = "u_" + std::to_string(round_idx); - transcript.apply_fiat_shamir(challenge_label); - FF round_challenge = FF::serialize_from_buffer(transcript.get_challenge(challenge_label).begin()); - fold(folded_polynomials, round.round_size, round_challenge); + round_univariate = round.compute_univariate(folded_polynomials, relation_parameters, pow_univariate, alpha); + std::string round_univariate_label = "Sumcheck:T_" + std::to_string(round_idx); + transcript.send_to_verifier(round_univariate_label, round_univariate); + + // Get the challenge + std::string round_challenge_label = "Sumcheck:u_" + std::to_string(round_idx); + round_challenge = transcript.get_challenge(round_challenge_label); + evaluation_points.emplace_back(round_challenge); + + fold(folded_polynomials, folded_polynomials, round.round_size, round_challenge); pow_univariate.partially_evaluate(round_challenge); round.round_size = round.round_size >> 1; } // Final round: Extract multivariate evaluations from folded_polynomials and add to transcript - std::array multivariate_evaluations; - for (size_t i = 0; i < bonk::StandardArithmetization::NUM_POLYNOMIALS; ++i) { + std::array multivariate_evaluations; + for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { multivariate_evaluations[i] = folded_polynomials[i][0]; } - transcript.add_element("multivariate_evaluations", to_buffer(multivariate_evaluations)); + transcript.send_to_verifier("Sumcheck:evaluations", multivariate_evaluations); + + return SumcheckOutput{ .evaluation_point = evaluation_points, .evaluations = multivariate_evaluations }; }; /** @@ -163,43 +141,61 @@ template class... Relations> cl * round, then use purported evaluations to generate purported full Honk relation value and check against final * target sum. */ - bool execute_verifier() + std::optional> execute_verifier(VerifierTranscript& transcript) { - bool verified(true); - const auto relation_parameters = retrieve_proof_parameters(); - PowUnivariate pow_univariate(relation_parameters.zeta); - // All but final round. - // target_total_sum is initialized to zero then mutated in place. + auto [alpha, zeta] = transcript.get_challenges("Sumcheck:alpha", "Sumcheck:zeta"); - if (multivariate_d == 0) { - throw_or_abort("Number of variables in multivariate is 0."); - } + PowUnivariate pow_univariate{ zeta }; - for (size_t round_idx = 0; round_idx < multivariate_d; round_idx++) { + // Used to evaluate T_l in each round. + auto barycentric = BarycentricData(); + + std::vector evaluation_points; + evaluation_points.reserve(multivariate_d); + + // initialize sigma_0 = 0 + FF target_sum{ 0 }; + for (size_t round_l = 0; round_l < multivariate_d; ++round_l) { // Obtain the round univariate from the transcript - auto round_univariate = Univariate::serialize_from_buffer( - &transcript.get_element("univariate_" + std::to_string(round_idx))[0]); - bool checked = round.check_sum(round_univariate, pow_univariate); - verified = verified && checked; - FF round_challenge = - FF::serialize_from_buffer(transcript.get_challenge("u_" + std::to_string(round_idx)).begin()); - - round.compute_next_target_sum(round_univariate, round_challenge, pow_univariate); - pow_univariate.partially_evaluate(round_challenge); + std::string round_univariate_label = "Sumcheck:T_" + std::to_string(round_l); + auto T_l = transcript.template receive_from_prover(round_univariate_label); - if (!verified) { - return false; + // S^{l}(0) = ( (1−0) + 0⋅ζ^{ 2^{l} } ) ⋅ T^{l}(0) = T^{l}(0) + // S^{l}(1) = ( (1−1) + 1⋅ζ^{ 2^{l} } ) ⋅ T^{l}(1) = ζ^{ 2^{l} } ⋅ T^{l}(1) + FF claimed_sum = T_l.value_at(0) + (pow_univariate.zeta_pow * T_l.value_at(1)); + + if (claimed_sum != target_sum) { + return std::nullopt; } + + std::string round_challenge_label = "Sumcheck:u_" + std::to_string(round_l); + auto u_l = transcript.get_challenge(round_challenge_label); + evaluation_points.emplace_back(u_l); + + // Compute next target_sum + // sigma_{l+1} = S^l(u_l) + // = T^l(u_l) * [ (1−u_l) + u_l⋅ζ^{ 2^l } ] + target_sum = barycentric.evaluate(T_l, u_l) * pow_univariate.univariate_eval(u_l); + + // Partially evaluate the pow_zeta polynomial + pow_univariate.partially_evaluate(u_l); } // Final round - auto purported_evaluations = transcript.get_field_element_vector("multivariate_evaluations"); - FF full_honk_relation_purported_value = round.compute_full_honk_relation_purported_value( - purported_evaluations, relation_parameters, pow_univariate); - verified = verified && (full_honk_relation_purported_value == round.target_total_sum); - return verified; - }; + auto purported_evaluations = + transcript.template receive_from_prover>("Sumcheck:evaluations"); + + FF full_eval = compute_full_evaluation( + purported_evaluations, pow_univariate.partial_evaluation_constant, relation_parameters, alpha); + + // Check against sigma_d + if (full_eval != target_sum) { + return std::nullopt; + } + + return SumcheckOutput{ .evaluation_point = evaluation_points, .evaluations = purported_evaluations }; + } // TODO(Cody): Rename. fold is not descriptive, and it's already in use in the Gemini context. // Probably just call it partial_evaluation? @@ -220,7 +216,7 @@ template class... Relations> cl * * @param challenge */ - void fold(auto& polynomials, size_t round_size, FF round_challenge) + void fold(auto& folded_polynomials, const auto& polynomials, size_t round_size, FF round_challenge) { // after the first round, operate in place on folded_polynomials for (size_t j = 0; j < polynomials.size(); ++j) { @@ -231,5 +227,27 @@ template class... Relations> cl } } }; + + static FF compute_full_evaluation(const std::array& multivariate_evaluations, + const FF pow_zeta_eval, + const RelationParameters& relation_parameters, + const FF alpha) + { + // Compute the evaluations of each relation + // Create an array of the same size as the `Relations` pack, + std::array relation_evals = { Relations::evaluate_full_relation_value_contribution( + multivariate_evaluations, relation_parameters)... }; + + // Do the alpha-linear combination + FF alpha_pow{ alpha }; + FF full_eval{ relation_evals[0] }; + for (size_t i = 1; i < relation_evals.size(); ++i) { + full_eval += relation_evals[i] * alpha_pow; + alpha_pow *= alpha; + } + // Multiply by the evaluation of pow + full_eval *= pow_zeta_eval; + return full_eval; + } }; } // namespace honk::sumcheck diff --git a/cpp/src/aztec/honk/sumcheck/sumcheck.test.cpp b/cpp/src/aztec/honk/sumcheck/sumcheck.test.cpp index d15e9d9f38..7557a14677 100644 --- a/cpp/src/aztec/honk/sumcheck/sumcheck.test.cpp +++ b/cpp/src/aztec/honk/sumcheck/sumcheck.test.cpp @@ -1,4 +1,5 @@ #include "sumcheck.hpp" +#include "honk/sumcheck/relations/relation.hpp" #include "proof_system/flavor/flavor.hpp" #include "transcript/transcript_wrappers.hpp" #include "relations/arithmetic_relation.hpp" @@ -74,68 +75,11 @@ std::array, NUM_POLYNOMIALS> construct_full_polynomials(std::array return full_polynomials; } -Transcript produce_mocked_transcript(size_t multivariate_d, size_t num_public_inputs) -{ - // Create a mock manifest containing only elements needed for testing Sumcheck - constexpr size_t fr_size = 32; - const size_t multivariate_n(1 << multivariate_d); - const size_t public_input_size = fr_size * num_public_inputs; - std::vector manifest_rounds; - manifest_rounds.emplace_back(transcript::Manifest::RoundManifest( - { { .name = "circuit_size", .num_bytes = 4, .derived_by_verifier = true }, - { .name = "public_input_size", .num_bytes = 4, .derived_by_verifier = true } }, - /* challenge_name = */ "init", - /* num_challenges_in = */ 1)); - - manifest_rounds.emplace_back(transcript::Manifest::RoundManifest({ /* this is a noop */ }, - /* challenge_name = */ "alpha", - /* num_challenges_in = */ 2)); - manifest_rounds.emplace_back(transcript::Manifest::RoundManifest( - { { .name = "public_inputs", .num_bytes = public_input_size, .derived_by_verifier = false } }, - /* challenge_name = */ "beta", - /* num_challenges_in = */ 2) // also produce "gamma" - ); - - for (size_t i = 0; i < multivariate_d; i++) { - auto label = std::to_string(i); - manifest_rounds.emplace_back( - transcript::Manifest::RoundManifest({ { .name = "univariate_" + label, - .num_bytes = fr_size * honk::StandardHonk::MAX_RELATION_LENGTH, - .derived_by_verifier = false } }, - /* challenge_name = */ "u_" + label, - /* num_challenges_in = */ 1)); - } - - // Create a transcript from the mock manifest - auto transcript = Transcript(transcript::Manifest(manifest_rounds)); - - transcript.add_element("circuit_size", - { static_cast(multivariate_n >> 24), - static_cast(multivariate_n >> 16), - static_cast(multivariate_n >> 8), - static_cast(multivariate_n) }); - - transcript.add_element("public_input_size", - { static_cast(num_public_inputs >> 24), - static_cast(num_public_inputs >> 16), - static_cast(num_public_inputs >> 8), - static_cast(num_public_inputs) }); - - transcript.apply_fiat_shamir("init"); - transcript.apply_fiat_shamir("alpha"); - std::vector public_inputs_buf(public_input_size, 1); // arbitrary buffer of 1's - transcript.add_element("public_inputs", public_inputs_buf); - transcript.apply_fiat_shamir("beta"); - - return transcript; -} - TEST(Sumcheck, PolynomialNormalization) { // Todo(Cody): We should not use real constants like this in the tests, at least not in so many of them. const size_t multivariate_d(3); const size_t multivariate_n(1 << multivariate_d); - const size_t num_public_inputs(1); std::array w_l; std::array w_r; @@ -196,19 +140,17 @@ TEST(Sumcheck, PolynomialNormalization) lagrange_first, lagrange_last); - auto transcript = produce_mocked_transcript(multivariate_d, num_public_inputs); - - auto sumcheck = Sumcheck(multivariate_n, transcript); + auto sumcheck = + Sumcheck( + multivariate_n, {}); + auto prover_transcript = ProverTranscript{}; + prover_transcript.send_to_verifier("init", FF{ 2 }); - sumcheck.execute_prover(full_polynomials); + auto [evaluation_points, evals] = sumcheck.execute_prover(full_polynomials, prover_transcript); - FF u_0 = transcript.get_challenge_field_element("u_0"); - FF u_1 = transcript.get_challenge_field_element("u_1"); - FF u_2 = transcript.get_challenge_field_element("u_2"); + FF u_0 = evaluation_points[0]; + FF u_1 = evaluation_points[1]; + FF u_2 = evaluation_points[2]; /* sumcheck.execute_prover() terminates with sumcheck.multivariates.folded_polynoimals as an array such that * sumcheck.multivariates.folded_polynoimals[i][0] is the evaluatioin of the i'th multivariate at the vector of @@ -238,7 +180,7 @@ TEST(Sumcheck, PolynomialNormalization) l_2 * full_polynomials[i][2] + l_3 * full_polynomials[i][3] + l_4 * full_polynomials[i][4] + l_5 * full_polynomials[i][5] + l_6 * full_polynomials[i][6] + l_7 * full_polynomials[i][7]; - EXPECT_EQ(hand_computed_value, sumcheck.folded_polynomials[i][0]); + EXPECT_EQ(hand_computed_value, evals[i]); } } @@ -247,7 +189,6 @@ TEST(Sumcheck, Prover) auto run_test = [](bool is_random_input) { const size_t multivariate_d(2); const size_t multivariate_n(1 << multivariate_d); - const size_t num_public_inputs(1); std::array, NUM_POLYNOMIALS> input_polynomials; if (is_random_input) { for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { @@ -297,16 +238,15 @@ TEST(Sumcheck, Prover) lagrange_first, lagrange_last); - auto transcript = produce_mocked_transcript(multivariate_d, num_public_inputs); - auto sumcheck = Sumcheck(multivariate_n, transcript); + auto prover_transcript = ProverTranscript{}; + prover_transcript.send_to_verifier("init", FF{ 2 }); + auto sumcheck = + Sumcheck( + multivariate_n, {}); - sumcheck.execute_prover(full_polynomials); - FF u_0 = transcript.get_challenge_field_element("u_0"); - FF u_1 = transcript.get_challenge_field_element("u_1"); + auto [evaluation_points, evals] = sumcheck.execute_prover(full_polynomials, prover_transcript); + FF u_0 = evaluation_points[0]; + FF u_1 = evaluation_points[1]; std::vector expected_values; for (auto& polynomial : full_polynomials) { // using knowledge of inputs here to derive the evaluation @@ -316,10 +256,8 @@ TEST(Sumcheck, Prover) expected_hi *= u_1; expected_values.emplace_back(expected_lo + expected_hi); } - // pull the sumcheck-produced multivariate evals out of the transcript - auto sumcheck_evaluations = transcript.get_field_element_vector("multivariate_evaluations"); for (size_t poly_idx = 0; poly_idx < NUM_POLYNOMIALS; poly_idx++) { - EXPECT_EQ(sumcheck_evaluations[poly_idx], expected_values[poly_idx]); + EXPECT_EQ(evals[poly_idx], expected_values[poly_idx]); } }; run_test(/* is_random_input=*/false); @@ -332,7 +270,6 @@ TEST(Sumcheck, ProverAndVerifier) { const size_t multivariate_d(1); const size_t multivariate_n(1 << multivariate_d); - const size_t num_public_inputs(1); std::array w_l = { 0, 1 }; std::array w_r = { 0, 1 }; @@ -372,24 +309,23 @@ TEST(Sumcheck, ProverAndVerifier) lagrange_first, lagrange_last); - auto transcript = produce_mocked_transcript(multivariate_d, num_public_inputs); + auto sumcheck = + Sumcheck( + multivariate_n, {}); - auto sumcheck_prover = Sumcheck(multivariate_n, transcript); + auto prover_transcript = ProverTranscript::init_empty(); - sumcheck_prover.execute_prover(full_polynomials); + auto prover_output = sumcheck.execute_prover(full_polynomials, prover_transcript); - auto sumcheck_verifier = Sumcheck(transcript); + auto verifier_transcript = VerifierTranscript::init_empty(prover_transcript); - bool verified = sumcheck_verifier.execute_verifier(); - ASSERT_TRUE(verified); + auto sumcheck_verifier = + Sumcheck( + multivariate_n, {}); + + auto verifier_output = sumcheck_verifier.execute_verifier(verifier_transcript); + ASSERT_TRUE(verifier_output.has_value()); + ASSERT_EQ(prover_output, *verifier_output); } // TODO: make the inputs to this test more interesting, e.g. num_public_inputs > 0 and non-trivial permutations @@ -398,7 +334,6 @@ TEST(Sumcheck, ProverAndVerifierLonger) auto run_test = [](bool expect_verified) { const size_t multivariate_d(2); const size_t multivariate_n(1 << multivariate_d); - const size_t num_public_inputs(0); // clang-format off std::array w_l; @@ -444,24 +379,23 @@ TEST(Sumcheck, ProverAndVerifierLonger) lagrange_first, lagrange_last); - auto transcript = produce_mocked_transcript(multivariate_d, num_public_inputs); + auto sumcheck = + Sumcheck( + multivariate_n, {}); + + auto prover_transcript = ProverTranscript::init_empty(); + + auto prover_output = sumcheck.execute_prover(full_polynomials, prover_transcript); - auto sumcheck_prover = Sumcheck(multivariate_n, transcript); + auto verifier_transcript = VerifierTranscript::init_empty(prover_transcript); - sumcheck_prover.execute_prover(full_polynomials); + auto sumcheck_verifier = + Sumcheck( + multivariate_n, {}); - auto sumcheck_verifier = Sumcheck(transcript); + auto verifier_output = sumcheck_verifier.execute_verifier(verifier_transcript); - bool verified = sumcheck_verifier.execute_verifier(); - EXPECT_EQ(verified, expect_verified); + EXPECT_EQ(verifier_output.has_value(), expect_verified); }; run_test(/* expect_verified=*/true); diff --git a/cpp/src/aztec/honk/sumcheck/sumcheck_round.hpp b/cpp/src/aztec/honk/sumcheck/sumcheck_round.hpp index a63178c4e0..17bad5a290 100644 --- a/cpp/src/aztec/honk/sumcheck/sumcheck_round.hpp +++ b/cpp/src/aztec/honk/sumcheck/sumcheck_round.hpp @@ -60,12 +60,9 @@ template class... Relation static constexpr size_t NUM_RELATIONS = sizeof...(Relations); static constexpr size_t MAX_RELATION_LENGTH = std::max({ Relations::RELATION_LENGTH... }); - FF target_total_sum = 0; - // TODO(Cody): this barycentric stuff should be more built-in? std::tuple::RELATION_LENGTH, MAX_RELATION_LENGTH>...> barycentric_utils; std::tuple::RELATION_LENGTH>...> univariate_accumulators; - std::array evaluations; std::array, num_multivariates> extended_edges; std::array, NUM_RELATIONS> extended_univariates; @@ -79,18 +76,6 @@ template class... Relation , barycentric_utils(BarycentricData::RELATION_LENGTH, MAX_RELATION_LENGTH>()...) , univariate_accumulators(Univariate::RELATION_LENGTH>()...) {} - - // Verifier constructor - explicit SumcheckRound(auto relations) - : relations(relations) - // TODO(Cody): this is a hack; accumulators not needed by verifier - , univariate_accumulators(Univariate::RELATION_LENGTH>()...) - { - // FF's default constructor may not initialize to zero (e.g., barretenberg::fr), hence we can't rely on - // aggregate initialization of the evaluations array. - std::fill(evaluations.begin(), evaluations.end(), FF(0)); - }; - /** * @brief After computing the round univariate, it is necessary to zero-out the accumulators used to compute it. */ @@ -181,30 +166,6 @@ template class... Relation } } - // TODO(Cody): make private - // TODO(Cody): make uniform with accumulate_relation_univariates - /** - * @brief Calculate the contribution of each relation to the expected value of the full Honk relation. - * - * @details For each relation, use the purported values (supplied by the prover) of the multivariates to calculate - * a contribution to the purported value of the full Honk relation. These are stored in `evaluations`. Adding these - * together, with appropriate scaling factors, produces the expected value of the full Honk relation. This value is - * checked against the final value of the target total sum (called sigma_0 in the thesis). - */ - template - // TODO(Cody): Input should be an array? Then challenge container has to know array length. - void accumulate_relation_evaluations(std::vector& purported_evaluations, - const RelationParameters& relation_parameters) - { - std::get(relations).add_full_relation_value_contribution( - evaluations[relation_idx], purported_evaluations, relation_parameters); - - // Repeat for the next relation. - if constexpr (relation_idx + 1 < NUM_RELATIONS) { - accumulate_relation_evaluations(purported_evaluations, relation_parameters); - } - } - /** * @brief After executing each widget on each edge, producing a tuple of univariates of differing lenghths, * extend all univariates to the max of the lenghths required by the largest relation. @@ -229,7 +190,8 @@ template class... Relation */ Univariate compute_univariate(auto& polynomials, const RelationParameters& relation_parameters, - const PowUnivariate& pow_univariate) + const PowUnivariate& pow_univariate, + const FF alpha) { // For each edge_idx = 2i, we need to multiply the whole contribution by zeta^{2^{2i}} // This means that each univariate for each relation needs an extra multiplication. @@ -245,78 +207,11 @@ template class... Relation pow_challenge *= pow_univariate.zeta_pow_sqr; } - auto result = batch_over_relations>(relation_parameters.alpha); + auto result = batch_over_relations>(alpha); reset_accumulators<>(); return result; } - - /** - * @brief Calculate the contribution of each relation to the expected value of the full Honk relation. - * - * @details For each relation, use the purported values (supplied by the prover) of the multivariates to calculate - * a contribution to the purported value of the full Honk relation. These are stored in `evaluations`. Adding these - * together, with appropriate scaling factors, produces the expected value of the full Honk relation. This value is - * checked against the final value of the target total sum, defined as sigma_d. - */ - // TODO(Cody): Input should be an array? Then challenge container has to know array length. - FF compute_full_honk_relation_purported_value(std::vector& purported_evaluations, - const RelationParameters& relation_parameters, - const PowUnivariate& pow_univariate) - { - accumulate_relation_evaluations<>(purported_evaluations, relation_parameters); - - // IMPROVEMENT(Cody): Reuse functions from univariate_accumulators batching? - FF running_challenge = 1; - FF output = 0; - for (auto& evals : evaluations) { - output += evals * running_challenge; - running_challenge *= relation_parameters.alpha; - } - output *= pow_univariate.partial_evaluation_constant; - - return output; - } - - /** - * @brief check if S^{l}(0) + S^{l}(1) = S^{l-1}(u_{l-1}) = sigma_{l} (or 0 if l=0) - * - * @param univariate T^{l}(X), the round univariate that is equal to S^{l}(X)/( (1−X) + X⋅ζ^{ 2^l } ) - */ - bool check_sum(Univariate& univariate, const PowUnivariate& pow_univariate) - { - // S^{l}(0) = ( (1−0) + 0⋅ζ^{ 2^l } ) ⋅ T^{l}(0) = T^{l}(0) - // S^{l}(1) = ( (1−1) + 1⋅ζ^{ 2^l } ) ⋅ T^{l}(1) = ζ^{ 2^l } ⋅ T^{l}(1) - FF total_sum = univariate.value_at(0) + (pow_univariate.zeta_pow * univariate.value_at(1)); - // target_total_sum = sigma_{l} = - bool sumcheck_round_failed = (target_total_sum != total_sum); - round_failed = round_failed || sumcheck_round_failed; - return !sumcheck_round_failed; - }; - - /** - * @brief After checking that the univariate is good for this round, compute the next target sum. - * - * @param univariate T^l(X), given by its evaluations over {0,1,2,...}, - * equal to S^{l}(X)/( (1−X) + X⋅ζ^{ 2^l } ) - * @param round_challenge u_l - * @return FF sigma_{l+1} = S^l(u_l) - */ - FF compute_next_target_sum(Univariate& univariate, - FF& round_challenge, - const PowUnivariate& pow_univariate) - { - // IMPROVEMENT(Cody): Use barycentric static method, maybe implement evaluation as member - // function on Univariate. - auto barycentric = BarycentricData(); - // Evaluate T^{l}(u_{l}) - target_total_sum = barycentric.evaluate(univariate, round_challenge); - // Evaluate (1−u_l) + u_l ⋅ ζ^{2^l} ) - FF pow_monomial_eval = pow_univariate.univariate_eval(round_challenge); - // sigma_{l+1} = S^l(u_l) = (1−u_l) + u_l⋅ζ^{2^l} ) ⋅ T^{l}(u_l) - target_total_sum *= pow_monomial_eval; - return target_total_sum; - } }; } // namespace honk::sumcheck diff --git a/cpp/src/aztec/honk/sumcheck/sumcheck_round.test.cpp b/cpp/src/aztec/honk/sumcheck/sumcheck_round.test.cpp index aa796564f6..140aa8bef9 100644 --- a/cpp/src/aztec/honk/sumcheck/sumcheck_round.test.cpp +++ b/cpp/src/aztec/honk/sumcheck/sumcheck_round.test.cpp @@ -1,4 +1,5 @@ #include +#include "honk/sumcheck/sumcheck.hpp" #include "sumcheck_round.hpp" #include "relations/arithmetic_relation.hpp" #include "relations/grand_product_computation_relation.hpp" @@ -78,7 +79,8 @@ std::array, NUM_POLYNOMIALS> construct_full_polynomials(std::array // The below two methods are used in the test ComputeUnivariateProver static Univariate compute_round_univariate( std::array, NUM_POLYNOMIALS>& input_polynomials, - const RelationParameters& relation_parameters) + const RelationParameters& relation_parameters, + const FF alpha) { size_t round_size = 1; auto relations = std::tuple( @@ -126,15 +128,16 @@ static Univariate compute_round_univariate( id_3, lagrange_first, lagrange_last); - PowUnivariate pow_zeta(relation_parameters.zeta); + PowUnivariate pow_zeta(1); Univariate round_univariate = - round.compute_univariate(full_polynomials, relation_parameters, pow_zeta); + round.compute_univariate(full_polynomials, relation_parameters, pow_zeta, alpha); return round_univariate; } static Univariate compute_expected_round_univariate( std::array, NUM_POLYNOMIALS>& input_univariates, - const RelationParameters& relation_parameters) + const RelationParameters& relation_parameters, + const FF alpha) { BarycentricData barycentric_2_to_max = BarycentricData(); @@ -177,17 +180,17 @@ static Univariate compute_expected_round_univariate( (w_o_univariate + sigma_3_univariate * relation_parameters.beta + relation_parameters.gamma)); auto expected_grand_product_initialization_relation = (z_perm_shift_univariate * lagrange_last_univariate); Univariate expected_round_univariate = - expected_arithmetic_relation + expected_grand_product_computation_relation * relation_parameters.alpha + - expected_grand_product_initialization_relation * relation_parameters.alpha * relation_parameters.alpha; + expected_arithmetic_relation + expected_grand_product_computation_relation * alpha + + expected_grand_product_initialization_relation * alpha.sqr(); return expected_round_univariate; } // The below two methods are used in the test ComputeUnivariateVerifier static FF compute_full_purported_value(std::array& input_values, - const RelationParameters& relation_parameters) + const RelationParameters& relation_parameters, + const FF alpha) { - std::vector purported_evaluations; - purported_evaluations.resize(NUM_POLYNOMIALS); + std::array purported_evaluations; purported_evaluations[POLYNOMIAL::W_L] = input_values[0]; purported_evaluations[POLYNOMIAL::W_R] = input_values[1]; purported_evaluations[POLYNOMIAL::W_O] = input_values[2]; @@ -206,21 +209,16 @@ static FF compute_full_purported_value(std::array& input_va purported_evaluations[POLYNOMIAL::ID_3] = input_values[15]; purported_evaluations[POLYNOMIAL::LAGRANGE_FIRST] = input_values[16]; purported_evaluations[POLYNOMIAL::LAGRANGE_LAST] = input_values[17]; - auto relations = std::tuple( - ArithmeticRelation(), GrandProductComputationRelation(), GrandProductInitializationRelation()); - auto round = SumcheckRound(relations); - PowUnivariate pow_univariate(relation_parameters.zeta); - FF full_purported_value = - round.compute_full_honk_relation_purported_value(purported_evaluations, relation_parameters, pow_univariate); + using Sumcheck = + Sumcheck; + PowUnivariate pow_univariate(1); + FF full_purported_value = Sumcheck::compute_full_evaluation(purported_evaluations, 1, relation_parameters, alpha); return full_purported_value; } static FF compute_full_purported_value_expected(std::array& input_values, - const RelationParameters& relation_parameters) + const RelationParameters& relation_parameters, + const FF alpha) { FF w_l = input_values[0]; FF w_r = input_values[1]; @@ -251,9 +249,9 @@ static FF compute_full_purported_value_expected(std::array& (w_r + sigma_2 * relation_parameters.beta + relation_parameters.gamma) * (w_o + sigma_3 * relation_parameters.beta + relation_parameters.gamma); auto expected_grand_product_initialization_relation = z_perm_shift * lagrange_last; - auto expected_full_purported_value = - expected_arithmetic_relation + expected_grand_product_computation_relation * relation_parameters.alpha + - expected_grand_product_initialization_relation * relation_parameters.alpha * relation_parameters.alpha; + auto expected_full_purported_value = expected_arithmetic_relation + + expected_grand_product_computation_relation * alpha + + expected_grand_product_initialization_relation * alpha.sqr(); return expected_full_purported_value; } @@ -265,19 +263,18 @@ TEST(SumcheckRound, ComputeUnivariateProver) for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { input_polynomials[i] = { FF::random_element(), FF::random_element() }; } - const RelationParameters relation_parameters = - RelationParameters{ .zeta = FF::random_element(), - .alpha = FF::random_element(), - .beta = FF::random_element(), - .gamma = FF::random_element(), - .public_input_delta = FF::random_element() }; - auto round_univariate = compute_round_univariate(input_polynomials, relation_parameters); + const FF alpha = FF::random_element(); + const RelationParameters relation_parameters = RelationParameters{ + .beta = FF::random_element(), .gamma = FF::random_element(), .public_input_delta = FF::random_element() + }; + auto round_univariate = compute_round_univariate(input_polynomials, relation_parameters, alpha); // Compute round_univariate manually std::array, NUM_POLYNOMIALS> input_univariates; for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { input_univariates[i] = Univariate(input_polynomials[i]); } - auto expected_round_univariate = compute_expected_round_univariate(input_univariates, relation_parameters); + auto expected_round_univariate = + compute_expected_round_univariate(input_univariates, relation_parameters, alpha); EXPECT_EQ(round_univariate, expected_round_univariate); } else { std::array, NUM_POLYNOMIALS> input_polynomials; @@ -285,15 +282,16 @@ TEST(SumcheckRound, ComputeUnivariateProver) input_polynomials[i] = { 1, 2 }; } const RelationParameters relation_parameters = - RelationParameters{ .zeta = 1, .alpha = 1, .beta = 1, .gamma = 1, .public_input_delta = 1 }; - auto round_univariate = compute_round_univariate(input_polynomials, relation_parameters); + RelationParameters{ .beta = 1, .gamma = 1, .public_input_delta = 1 }; + auto round_univariate = compute_round_univariate(input_polynomials, relation_parameters, 1); // Compute round_univariate manually std::array, NUM_POLYNOMIALS> input_univariates; for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { input_univariates[i] = Univariate(input_polynomials[i]); } // expected_round_univariate = { 6, 26, 66, 132, 230, 366 } - auto expected_round_univariate = compute_expected_round_univariate(input_univariates, relation_parameters); + auto expected_round_univariate = + compute_expected_round_univariate(input_univariates, relation_parameters, 1); EXPECT_EQ(round_univariate, expected_round_univariate); }; }; @@ -309,16 +307,14 @@ TEST(SumcheckRound, ComputeUnivariateVerifier) for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { input_values[i] = FF::random_element(); } - const RelationParameters relation_parameters = - RelationParameters{ .zeta = FF::random_element(), - .alpha = FF::random_element(), - .beta = FF::random_element(), - .gamma = FF::random_element(), - .public_input_delta = FF::random_element() }; - auto full_purported_value = compute_full_purported_value(input_values, relation_parameters); + const FF alpha = FF::random_element(); + const RelationParameters relation_parameters = RelationParameters{ + .beta = FF::random_element(), .gamma = FF::random_element(), .public_input_delta = FF::random_element() + }; + auto full_purported_value = compute_full_purported_value(input_values, relation_parameters, alpha); // Compute round_univariate manually auto expected_full_purported_value = - compute_full_purported_value_expected(input_values, relation_parameters); + compute_full_purported_value_expected(input_values, relation_parameters, alpha); EXPECT_EQ(full_purported_value, expected_full_purported_value); } else { std::array input_values; @@ -326,11 +322,11 @@ TEST(SumcheckRound, ComputeUnivariateVerifier) input_values[i] = FF(2); } const RelationParameters relation_parameters = - RelationParameters{ .zeta = 2, .alpha = 1, .beta = 1, .gamma = 1, .public_input_delta = 1 }; - auto full_purported_value = compute_full_purported_value(input_values, relation_parameters); + RelationParameters{ .beta = 1, .gamma = 1, .public_input_delta = 1 }; + auto full_purported_value = compute_full_purported_value(input_values, relation_parameters, 1); // Compute round_univariate manually auto expected_full_purported_value = - compute_full_purported_value_expected(input_values, relation_parameters); + compute_full_purported_value_expected(input_values, relation_parameters, 1); EXPECT_EQ(full_purported_value, expected_full_purported_value); }; }; diff --git a/cpp/src/aztec/honk/transcript/transcript.hpp b/cpp/src/aztec/honk/transcript/transcript.hpp new file mode 100644 index 0000000000..a0970abe2f --- /dev/null +++ b/cpp/src/aztec/honk/transcript/transcript.hpp @@ -0,0 +1,208 @@ +#pragma once + +#include "common/serialize.hpp" +#include "crypto/pedersen/pedersen.hpp" +#include "crypto/blake3s/blake3s.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace honk { + +/** + * @brief Common transcript functionality for both parties. Stores the data for the current round, as well as the + * manifest. + * + * @tparam FF Field from which we sample challenges. + */ +template class BaseTranscript { + // TODO(Adrian): Make these tweakable + static constexpr size_t HASH_OUTPUT_SIZE = 32; + static constexpr size_t MIN_BYTES_PER_CHALLENGE = 128 / 8; // 128 bit challenges + + size_t round_number = 0; + std::array previous_challenge_buffer{}; + std::vector current_round_data; + + /** + * @brief Compute c_next = H( Compress(c_prev || round_buffer) ) + * + * @return std::array + */ + [[nodiscard]] std::array get_next_challenge_buffer() const + { + // Prevent challenge generation if nothing was sent by the prover. + ASSERT(!current_round_data.empty()); + + // concatenate the hash of the previous round (if not the first round) with the current round data. + // TODO(Adrian): Do we want to use a domain separator as the initial challenge buffer? + // We could be cheeky and use the hash of the manifest as domain separator, which would prevent us from having + // to domain separate all the data. (See https://safe-hash.dev) + std::vector full_buffer; + if (round_number > 0) { + full_buffer.insert(full_buffer.end(), previous_challenge_buffer.begin(), previous_challenge_buffer.end()); + } + full_buffer.insert(full_buffer.end(), current_round_data.begin(), current_round_data.end()); + + // Optionally pre-hash the full buffer to minimize the amount of data passed to the cryptographic hash function. + // Only a collision-resistant hash-function like Pedersen is required for this step. + std::vector compressed_buffer = to_buffer(crypto::pedersen::compress_native(full_buffer)); + + // Use a strong hash function to derive the new challenge_buffer. + auto base_hash = blake3::blake3s(compressed_buffer); + + std::array new_challenge_buffer; + std::copy_n(base_hash.begin(), HASH_OUTPUT_SIZE, new_challenge_buffer.begin()); + + return new_challenge_buffer; + }; + + protected: + /** + * @brief Adds challenge elements to the current_round_buffer and updates the manifest. + * + * @param label of the element sent + * @param element_bytes serialized + */ + void consume_prover_element_bytes(const std::string& label, std::span element_bytes) + { + (void)label; + + current_round_data.insert(current_round_data.end(), element_bytes.begin(), element_bytes.end()); + } + + public: + /** + * @brief After all the prover messages have been sent, finalize the round by hashing all the data, create the field + * elements and reset the state in preparation for the next round. + * + * @param labels human-readable names for the challenges for the manifest + * @return std::array challenges for this round. + */ + template + std::array get_challenges(const Strings&... labels) + { + constexpr size_t num_challenges = sizeof...(Strings); + constexpr size_t bytes_per_challenge = HASH_OUTPUT_SIZE / num_challenges; + + // Ensure we have enough entropy from the hash function to construct each challenge. + static_assert(bytes_per_challenge >= MIN_BYTES_PER_CHALLENGE, "requested too many challenges in this round"); + + // TODO(Adrian): Add the challenge names to the map. + ((void)labels, ...); + + // Compute the new challenge buffer from which we derive the challenges. + auto next_challenge_buffer = get_next_challenge_buffer(); + + // Create challenges from bytes. + std::array challenges{}; + for (size_t i = 0; i < num_challenges; ++i) { + // Initialize the buffer for the i-th challenge with 0s. + std::array field_element_buffer{}; + // Copy the i-th chunk of size `bytes_per_challenge` to the start of `field_element_buffer` + // The last bytes will be 0, + std::copy_n(next_challenge_buffer.begin() + i * bytes_per_challenge, + bytes_per_challenge, + field_element_buffer.begin()); + + // Create a FF element from a slice of bytes of next_challenge_buffer. + challenges[i] = from_buffer(field_element_buffer); + } + + // Prepare for next round. + ++round_number; + current_round_data.clear(); + previous_challenge_buffer = next_challenge_buffer; + + return challenges; + } + + FF get_challenge(const std::string& label) { return get_challenges(label)[0]; } +}; + +template class ProverTranscript : public BaseTranscript { + + public: + /// Contains the raw data sent by the prover. + std::vector proof_data; + + /** + * @brief Adds a prover message to the transcript. + * + * @details Serializes the provided object into `proof_data`, and updates the current round state. + * + * @param label Description/name of the object being added. + * @param element Serializable object that will be added to the transcript + * + * @todo Use a concept to only allow certain types to be passed. Requirements are that the object should be + * serializable. + * + */ + template void send_to_verifier(const std::string& label, const T& element) + { + using serialize::write; + // DANGER: When serializing an affine_element, we write the x and y coordinates + // but this is annowing to deal with right now. + auto element_bytes = to_buffer(element); + proof_data.insert(proof_data.end(), element_bytes.begin(), element_bytes.end()); + + BaseTranscript::consume_prover_element_bytes(label, element_bytes); + } + + static ProverTranscript init_empty() + { + ProverTranscript transcript; + constexpr uint32_t init{ 42 }; + transcript.send_to_verifier("Init", init); + return transcript; + }; +}; + +template class VerifierTranscript : public BaseTranscript { + + /// Contains the raw data sent by the prover. + const std::vector proof_data_; + typename std::vector::const_iterator read_iterator_; + + public: + explicit VerifierTranscript(const std::vector& proof_data) + : proof_data_(proof_data.begin(), proof_data.end()) + , read_iterator_(proof_data_.cbegin()) + {} + + static VerifierTranscript init_empty(const ProverTranscript& transcript) + { + VerifierTranscript verifier_transcript{ transcript.proof_data }; + [[maybe_unused]] auto _ = verifier_transcript.template receive_from_prover("Init"); + return verifier_transcript; + }; + + /** + * @brief Reads the next element of type `T` from the transcript, with a predefined label. + * + * @details + * + * @param label Human readable name for the challenge. + * @return deserialized element of type T + */ + template T receive_from_prover(const std::string& label) + { + constexpr size_t element_size = sizeof(T); + ASSERT(read_iterator_ + element_size <= proof_data_.end()); + + std::span element_bytes{ read_iterator_, element_size }; + read_iterator_ += element_size; + + BaseTranscript::consume_prover_element_bytes(label, element_bytes); + + T element = from_buffer(element_bytes); + + return element; + } +}; +} // namespace honk \ No newline at end of file diff --git a/cpp/src/aztec/proof_system/flavor/flavor.hpp b/cpp/src/aztec/proof_system/flavor/flavor.hpp index 486ba528da..f015e08698 100644 --- a/cpp/src/aztec/proof_system/flavor/flavor.hpp +++ b/cpp/src/aztec/proof_system/flavor/flavor.hpp @@ -76,114 +76,5 @@ struct StandardHonk { // TODO(Cody): should extract this from the parameter pack. Maybe that should be done here? // num_sumcheck_rounds = 1 if using quotient polynomials, otherwise = number of sumcheck rounds - static transcript::Manifest create_manifest(const size_t num_public_inputs, const size_t num_sumcheck_rounds = 1) - { - constexpr size_t g1_size = 64; - constexpr size_t fr_size = 32; - const size_t public_input_size = fr_size * num_public_inputs; - // clang-format off - /* A RoundManifest describes data that will be put in or extracted from a transcript. - Here we have (1 + 7 + num_sumcheck_rounds)-many RoundManifests. */ - std::vector manifest_rounds; - - // Round 0 - manifest_rounds.emplace_back(transcript::Manifest::RoundManifest( - { - { .name = "circuit_size", .num_bytes = 4, .derived_by_verifier = true }, - { .name = "public_input_size", .num_bytes = 4, .derived_by_verifier = true } - }, - /* challenge_name = */ "init", - /* num_challenges_in = */ 1)); - - // Round 1 - manifest_rounds.emplace_back(transcript::Manifest::RoundManifest( - { /* this is a noop */ }, - /* challenge_name = */ "eta", - /* num_challenges_in = */ 0)); - - // Round 2 - manifest_rounds.emplace_back(transcript::Manifest::RoundManifest( - { - { .name = "public_inputs", .num_bytes = public_input_size, .derived_by_verifier = false }, - { .name = "W_1", .num_bytes = g1_size, .derived_by_verifier = false }, - { .name = "W_2", .num_bytes = g1_size, .derived_by_verifier = false }, - { .name = "W_3", .num_bytes = g1_size, .derived_by_verifier = false }, - }, - /* challenge_name = */ "beta", - /* num_challenges_in = */ 2) // also produce "gamma" - ); - - // Round 3 - manifest_rounds.emplace_back(transcript::Manifest::RoundManifest( - { { .name = "Z_PERM", .num_bytes = g1_size, .derived_by_verifier = false } }, - /* challenge_name = */ "alpha", - /* num_challenges_in = */ 2) - ); - - // Rounds 4, ... 4 + num_sumcheck_rounds-1 - for (size_t i = 0; i < num_sumcheck_rounds; i++) { - auto label = std::to_string(i); - manifest_rounds.emplace_back( - transcript::Manifest::RoundManifest( - { - { .name = "univariate_" + label, .num_bytes = fr_size * honk::StandardHonk::MAX_RELATION_LENGTH, .derived_by_verifier = false } - }, - /* challenge_name = */ "u_" + label, - /* num_challenges_in = */ 1)); - } - - // Round 5 + num_sumcheck_rounds - manifest_rounds.emplace_back(transcript::Manifest::RoundManifest( - { - { .name = "multivariate_evaluations", .num_bytes = fr_size * bonk::StandardArithmetization::NUM_POLYNOMIALS, .derived_by_verifier = false, .challenge_map_index = 0 }, - }, - /* challenge_name = */ "rho", - /* num_challenges_in = */ 1)); /* TODO(Cody): magic number! Where should this be specified? */ - - // Rounds 6 + num_sumcheck_rounds, ... , 6 + 2 * num_sumcheck_rounds - 1 - std::vector fold_commitment_entries; - for (size_t i = 1; i < num_sumcheck_rounds; i++) { - fold_commitment_entries.emplace_back(transcript::Manifest::ManifestEntry( - { .name = "FOLD_" + std::to_string(i), .num_bytes = g1_size, .derived_by_verifier = false })); - }; - manifest_rounds.emplace_back(transcript::Manifest::RoundManifest( - fold_commitment_entries, - /* challenge_name = */ "r", - /* num_challenges_in */ 1)); - - // Rounds 6 + 2 * num_sumcheck_rounds, ..., 6 + 3 * num_sumcheck_rounds - std::vector gemini_evaluation_entries; - for (size_t i = 0; i < num_sumcheck_rounds; i++) { - gemini_evaluation_entries.emplace_back(transcript::Manifest::ManifestEntry( - { .name = "a_" + std::to_string(i), .num_bytes = fr_size, .derived_by_verifier = false })); - }; - manifest_rounds.emplace_back(transcript::Manifest::RoundManifest( - gemini_evaluation_entries, - /* challenge_name = */ "nu", - /* num_challenges_in */ 1)); - - // Round 7 + 3 * num_sumcheck_rounds - manifest_rounds.emplace_back( - transcript::Manifest::RoundManifest( - { - { .name = "Q", .num_bytes = g1_size, .derived_by_verifier = false } - }, - /* challenge_name = */ "z", - /* num_challenges_in */ 1)); - - // Round 8 + 3 * num_sumcheck_rounds - manifest_rounds.emplace_back( - transcript::Manifest::RoundManifest( - { - { .name = "W", .num_bytes = g1_size, .derived_by_verifier = false } - }, - /* challenge_name = */ "separator", - /* num_challenges_in */ 1)); - - // clang-format on - - auto output = transcript::Manifest(manifest_rounds); - return output; - } }; } // namespace honk \ No newline at end of file