From 1817b57092372b7197e1107a47e8b6f273072f6b Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Wed, 15 Mar 2023 18:43:51 +0000 Subject: [PATCH 01/10] adding adrians new transcript classes --- .../honk/transcript/transcript.hpp | 207 ++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 cpp/src/barretenberg/honk/transcript/transcript.hpp diff --git a/cpp/src/barretenberg/honk/transcript/transcript.hpp b/cpp/src/barretenberg/honk/transcript/transcript.hpp new file mode 100644 index 0000000000..46883e3458 --- /dev/null +++ b/cpp/src/barretenberg/honk/transcript/transcript.hpp @@ -0,0 +1,207 @@ +#pragma once + +#include "barretenberg/common/serialize.hpp" +#include "barretenberg/crypto/pedersen/pedersen.hpp" +#include "barretenberg/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 From 9831256d910486831decc51897b5e4c8225b39f2 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Thu, 16 Mar 2023 18:49:49 +0000 Subject: [PATCH 02/10] building with some failing tests --- .../barretenberg/crypto/blake3s/blake3s.hpp | 2 +- .../standard_honk_composer_helper.cpp | 7 +- cpp/src/barretenberg/honk/pcs/claim.hpp | 22 +-- .../honk/pcs/commitment_key.test.hpp | 6 +- .../barretenberg/honk/pcs/gemini/gemini.hpp | 40 +++-- .../honk/pcs/gemini/gemini.test.cpp | 19 +-- cpp/src/barretenberg/honk/pcs/kzg/kzg.hpp | 15 +- .../barretenberg/honk/pcs/kzg/kzg.test.cpp | 62 +++---- .../honk/pcs/shplonk/shplonk.test.cpp | 32 ++-- .../honk/pcs/shplonk/shplonk_single.hpp | 45 +++-- .../barretenberg/honk/proof_system/prover.cpp | 99 +++++------ .../barretenberg/honk/proof_system/prover.hpp | 16 +- .../honk/proof_system/verifier.cpp | 159 +++++++++--------- .../honk/proof_system/verifier.hpp | 4 +- .../honk/proof_system/verifier.test.cpp | 5 +- .../polynomials/multivariates.test.cpp | 13 +- .../honk/sumcheck/polynomials/univariate.hpp | 12 ++ .../barretenberg/honk/sumcheck/sumcheck.hpp | 96 +++++++---- .../honk/sumcheck/sumcheck.test.cpp | 129 +++++--------- .../honk/sumcheck/sumcheck_output.hpp | 19 +++ .../honk/sumcheck/sumcheck_round.hpp | 9 +- .../honk/sumcheck/sumcheck_round.test.cpp | 2 +- 22 files changed, 384 insertions(+), 429 deletions(-) create mode 100644 cpp/src/barretenberg/honk/sumcheck/sumcheck_output.hpp diff --git a/cpp/src/barretenberg/crypto/blake3s/blake3s.hpp b/cpp/src/barretenberg/crypto/blake3s/blake3s.hpp index eb581479a9..bb147d06be 100644 --- a/cpp/src/barretenberg/crypto/blake3s/blake3s.hpp +++ b/cpp/src/barretenberg/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/barretenberg/honk/composer/composer_helper/standard_honk_composer_helper.cpp b/cpp/src/barretenberg/honk/composer/composer_helper/standard_honk_composer_helper.cpp index 466ca098c5..2c89d6a7d8 100644 --- a/cpp/src/barretenberg/honk/composer/composer_helper/standard_honk_composer_helper.cpp +++ b/cpp/src/barretenberg/honk/composer/composer_helper/standard_honk_composer_helper.cpp @@ -148,10 +148,7 @@ StandardVerifier StandardHonkComposerHelper::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"); @@ -172,7 +169,7 @@ StandardProver StandardHonkComposerHelper::create_prover( 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/barretenberg/honk/pcs/claim.hpp b/cpp/src/barretenberg/honk/pcs/claim.hpp index 44b47a1d24..365e663142 100644 --- a/cpp/src/barretenberg/honk/pcs/claim.hpp +++ b/cpp/src/barretenberg/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/barretenberg/honk/pcs/commitment_key.test.hpp b/cpp/src/barretenberg/honk/pcs/commitment_key.test.hpp index 0e59c6f951..47623ee453 100644 --- a/cpp/src/barretenberg/honk/pcs/commitment_key.test.hpp +++ b/cpp/src/barretenberg/honk/pcs/commitment_key.test.hpp @@ -66,7 +66,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; @@ -78,7 +78,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) { @@ -122,7 +122,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"; } diff --git a/cpp/src/barretenberg/honk/pcs/gemini/gemini.hpp b/cpp/src/barretenberg/honk/pcs/gemini/gemini.hpp index 0fd9537bb8..1486fddad7 100644 --- a/cpp/src/barretenberg/honk/pcs/gemini/gemini.hpp +++ b/cpp/src/barretenberg/honk/pcs/gemini/gemini.hpp @@ -4,6 +4,7 @@ #include "barretenberg/common/log.hpp" #include "barretenberg/honk/pcs/commitment_key.hpp" #include "barretenberg/polynomials/polynomial.hpp" +#include "barretenberg/honk/transcript/transcript.hpp" #include "barretenberg/common/assert.hpp" #include @@ -113,7 +114,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 +166,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 +222,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 +258,32 @@ 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,11 +295,11 @@ 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; diff --git a/cpp/src/barretenberg/honk/pcs/gemini/gemini.test.cpp b/cpp/src/barretenberg/honk/pcs/gemini/gemini.test.cpp index 619108737a..d0f5d8cda4 100644 --- a/cpp/src/barretenberg/honk/pcs/gemini/gemini.test.cpp +++ b/cpp/src/barretenberg/honk/pcs/gemini/gemini.test.cpp @@ -1,6 +1,7 @@ #include "gemini.hpp" #include "../commitment_key.test.hpp" +#include "barretenberg/honk/transcript/transcript.hpp" #include "barretenberg/polynomials/polynomial.hpp" #include #include @@ -23,11 +24,9 @@ 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()); + auto prover_transcript = ProverTranscript::init_empty(); + + const Fr rho = Fr::random_element(); std::vector rhos = Gemini::powers_of_rho(rho, multilinear_evaluations.size()); @@ -60,15 +59,12 @@ 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); // Compute: // - Single opening pair: {r, \hat{a}_0} @@ -78,8 +74,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/barretenberg/honk/pcs/kzg/kzg.hpp b/cpp/src/barretenberg/honk/pcs/kzg/kzg.hpp index a538045f84..17385428b3 100644 --- a/cpp/src/barretenberg/honk/pcs/kzg/kzg.hpp +++ b/cpp/src/barretenberg/honk/pcs/kzg/kzg.hpp @@ -2,6 +2,7 @@ #include "../claim.hpp" #include "barretenberg/polynomials/polynomial.hpp" +#include "barretenberg/honk/transcript/transcript.hpp" #include #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 diff --git a/cpp/src/barretenberg/honk/pcs/kzg/kzg.test.cpp b/cpp/src/barretenberg/honk/pcs/kzg/kzg.test.cpp index b5c79fbfed..73f97255a5 100644 --- a/cpp/src/barretenberg/honk/pcs/kzg/kzg.test.cpp +++ b/cpp/src/barretenberg/honk/pcs/kzg/kzg.test.cpp @@ -27,30 +27,23 @@ 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, 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()); @@ -65,32 +58,28 @@ TYPED_TEST(BilinearAccumulationTest, single) */ TYPED_TEST(BilinearAccumulationTest, GeminiShplonkKzgWithShift) { - using Transcript = transcript::StandardTranscript; using Shplonk = shplonk::SingleBatchOpeningScheme; using Gemini = gemini::MultilinearReductionScheme; using KZG = UnivariateOpeningScheme; using Fr = typename TypeParam::Fr; + // using Transcript = ProverTranscript; using Commitment = typename TypeParam::Commitment; using Polynomial = typename barretenberg::Polynomial; 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); @@ -120,36 +109,32 @@ TYPED_TEST(BilinearAccumulationTest, GeminiShplonkKzgWithShift) batched_commitment_unshifted = commitment1 * rhos[0] + commitment2 * rhos[1]; batched_commitment_to_be_shifted = commitment2 * rhos[2]; + auto prover_transcript = ProverTranscript::init_empty(); + // Run the full prover PCS protocol: // 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); + auto gemini_prover_output = 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); + this->ck(), gemini_prover_output.opening_pairs, gemini_prover_output.witnesses, 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_output.opening_pair, shplonk_prover_output.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 @@ -157,15 +142,14 @@ TYPED_TEST(BilinearAccumulationTest, GeminiShplonkKzgWithShift) batched_evaluation, batched_commitment_unshifted, batched_commitment_to_be_shifted, - gemini_proof, - transcript); + 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); + 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); + auto kzg_claim = KZG::reduce_verify(shplonk_verifier_claim, verifier_transcript); // Final pairing check: e([Q] - [Q_z] + z[W], [1]_2) = e([W], [x]_2) bool verified = kzg_claim.verify(this->vk()); diff --git a/cpp/src/barretenberg/honk/pcs/shplonk/shplonk.test.cpp b/cpp/src/barretenberg/honk/pcs/shplonk/shplonk.test.cpp index 6fa06b5808..56b9871900 100644 --- a/cpp/src/barretenberg/honk/pcs/shplonk/shplonk.test.cpp +++ b/cpp/src/barretenberg/honk/pcs/shplonk/shplonk.test.cpp @@ -26,11 +26,9 @@ 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()); + auto prover_transcript = ProverTranscript::init_empty(); + + const Fr rho = Fr::random_element(); const auto u = this->random_evaluation_point(log_n); auto poly = this->random_polynomial(n); @@ -52,30 +50,20 @@ 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 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); + auto verifier_transcript = VerifierTranscript::init_empty(prover_transcript); - // Reconstruct the Shplonk Proof (commitment [Q]) from the transcript - auto shplonk_proof = transcript->get_group_element("Q"); + 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/barretenberg/honk/pcs/shplonk/shplonk_single.hpp b/cpp/src/barretenberg/honk/pcs/shplonk/shplonk_single.hpp index 5826813806..aa6e4f1065 100644 --- a/cpp/src/barretenberg/honk/pcs/shplonk/shplonk_single.hpp +++ b/cpp/src/barretenberg/honk/pcs/shplonk/shplonk_single.hpp @@ -2,6 +2,7 @@ #include "barretenberg/honk/pcs/claim.hpp" #include "shplonk.hpp" #include "barretenberg/honk/pcs/commitment_key.hpp" +#include "barretenberg/honk/transcript/transcript.hpp" namespace honk::pcs::shplonk { @@ -33,10 +34,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 +63,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 z_challenge = transcript.get_challenge("Shplonk:z"); // {ẑⱼ(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(z_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 @@ -116,12 +113,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 z_challenge = transcript.get_challenge("Shplonk:z"); // compute simulated commitment to [G] as a linear combination of // [Q], { [fⱼ] }, [1]: @@ -133,17 +132,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(z_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) { diff --git a/cpp/src/barretenberg/honk/proof_system/prover.cpp b/cpp/src/barretenberg/honk/proof_system/prover.cpp index 15cef9aa23..835028ba72 100644 --- a/cpp/src/barretenberg/honk/proof_system/prover.cpp +++ b/cpp/src/barretenberg/honk/proof_system/prover.cpp @@ -1,7 +1,7 @@ #include "prover.hpp" #include #include -#include "barretenberg/honk/sumcheck/sumcheck.hpp" // will need +#include "barretenberg/honk/sumcheck/sumcheck.hpp" #include #include "barretenberg/honk/sumcheck/polynomials/univariate.hpp" // will go away #include "barretenberg/honk/utils/power_polynomial.hpp" @@ -38,10 +38,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) + std::shared_ptr input_key) + : wire_polynomials(wire_polys) , key(input_key) , commitment_key(std::make_unique( input_key->circuit_size, @@ -66,6 +64,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 +83,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); } } @@ -191,47 +196,32 @@ template Polynomial Prover::compute_grand_product_ } /** - * - Add circuit size and PI size to transcript + * - Add circuit size, public input size, and public inputs to transcript * * */ template void Prover::execute_preamble_round() { - // Add some initial data to transcript (circuit size and PI size) - // 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); + } } /** * - compute wire commitments - * - add public inputs to transcript (done explicitly in execute_first_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)); } /** @@ -239,9 +229,6 @@ 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"); - // No operations are needed here for Standard Honk } @@ -253,14 +240,22 @@ template void Prover::execute_grand_product_comput { // queue.flush_queue(); // NOTE: Don't remove; we may reinstate the queue - transcript.apply_fiat_shamir("beta"); + // Compute and store parameters required by relations in Sumcheck + 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(); @@ -281,11 +276,12 @@ template void Prover::execute_relation_check_round sumcheck::GrandProductComputationRelation, sumcheck::GrandProductInitializationRelation>; - transcript.apply_fiat_shamir("alpha"); + // transcript.apply_fiat_shamir("alpha"); auto sumcheck = Sumcheck(key->circuit_size, transcript); - sumcheck.execute_prover(prover_polynomials); + // TODO(luke): should sumcheck_output be replaced by simply multivariate_query? + sumcheck_output = sumcheck.execute_prover(prover_polynomials, relation_parameters); } /** @@ -298,21 +294,10 @@ 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"); - // Batch the unshifted polynomials and the to-be-shifted polynomials using ρ Polynomial batched_poly_unshifted(key->circuit_size); // batched unshifted polynomials for (size_t i = 0; i < NUM_UNSHIFTED_POLYS; ++i) { @@ -323,10 +308,10 @@ template void Prover::execute_univariatization_rou // Compute d+1 Fold polynomials and their evaluations gemini_output = Gemini::reduce_prove(commitment_key, - opening_point, + sumcheck_output.multivariate_query, std::move(batched_poly_unshifted), std::move(batched_poly_to_be_shifted), - &transcript); + transcript); } /** @@ -349,7 +334,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); } /** @@ -358,12 +343,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/barretenberg/honk/proof_system/prover.hpp b/cpp/src/barretenberg/honk/proof_system/prover.hpp index ffe58f41c0..f75971c12b 100644 --- a/cpp/src/barretenberg/honk/proof_system/prover.hpp +++ b/cpp/src/barretenberg/honk/proof_system/prover.hpp @@ -11,6 +11,9 @@ #include "barretenberg/honk/pcs/gemini/gemini.hpp" #include "barretenberg/honk/pcs/shplonk/shplonk_single.hpp" #include "barretenberg/honk/pcs/kzg/kzg.hpp" +#include "barretenberg/honk/transcript/transcript.hpp" +#include "barretenberg/honk/sumcheck/sumcheck.hpp" +#include "barretenberg/honk/sumcheck/sumcheck_output.hpp" #include #include #include @@ -28,9 +31,7 @@ using Fr = barretenberg::fr; template class Prover { public: - 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(); @@ -51,7 +52,11 @@ template class Prover { plonk::proof& export_proof(); plonk::proof& construct_proof(); - transcript::StandardTranscript transcript; + ProverTranscript transcript; + + std::vector public_inputs; + + sumcheck::RelationParameters relation_parameters; std::vector wire_polynomials; barretenberg::polynomial z_permutation; @@ -79,10 +84,11 @@ 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; - using Transcript = transcript::StandardTranscript; + using Transcript = ProverTranscript; using Gemini = pcs::gemini::MultilinearReductionScheme; using Shplonk = pcs::shplonk::SingleBatchOpeningScheme; using KZG = pcs::kzg::UnivariateOpeningScheme; diff --git a/cpp/src/barretenberg/honk/proof_system/verifier.cpp b/cpp/src/barretenberg/honk/proof_system/verifier.cpp index 7d54319ecf..7baee96270 100644 --- a/cpp/src/barretenberg/honk/proof_system/verifier.cpp +++ b/cpp/src/barretenberg/honk/proof_system/verifier.cpp @@ -18,6 +18,7 @@ #include "barretenberg/polynomials/polynomial_arithmetic.hpp" #include "barretenberg/honk/composer/composer_helper/permutation_helper.hpp" #include +#include #include #include "barretenberg/honk/utils/power_polynomial.hpp" #include "barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp" @@ -28,23 +29,19 @@ using namespace honk::sumcheck; 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 +78,8 @@ 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 Transcript = VerifierTranscript; using Gemini = pcs::gemini::MultilinearReductionScheme; using Shplonk = pcs::shplonk::SingleBatchOpeningScheme; using KZG = pcs::kzg::UnivariateOpeningScheme; @@ -89,41 +87,49 @@ 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 program_width = program_settings::program_width; + + // const size_t log_n(numeric::get_msb(key->circuit_size)); + + VerifierTranscript transcript{ proof.proof_data }; + + // TODO(Adrian): Change the initialization of the transcript to take the VK hash? Also need to add the + // commitments... + const auto circuit_size = transcript.template receive_from_prover("circuit_size"); + const 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); } - 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"); + + // Get commitments to the wires + std::array wire_commitments; + for (size_t i = 0; i < program_width; ++i) { + wire_commitments[i] = transcript.template receive_from_prover("W_" + std::to_string(i + 1)); + } + + // Get permutation challenges + auto [beta, gamma] = transcript.get_challenges("beta", "gamma"); + + const FF public_input_delta = compute_public_input_delta(public_inputs, beta, gamma, circuit_size); + + sumcheck::RelationParameters relation_parameters{ + .beta = beta, + .gamma = gamma, + .public_input_delta = public_input_delta, + }; + + // Get commitment to Z_PERM + 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 @@ -132,31 +138,31 @@ template bool Verifier::verify_pro Transcript, ArithmeticRelation, GrandProductComputationRelation, - GrandProductInitializationRelation>(transcript); - bool sumcheck_result = sumcheck.execute_verifier(); + GrandProductInitializationRelation>(circuit_size, transcript); + std::optional sumcheck_output = sumcheck.execute_verifier(relation_parameters); + + // Abort if sumcheck failed and returned nothing + if (!sumcheck_output.has_value()) { + return false; + } + + auto [multivariate_query, multivariate_evaluations] = *sumcheck_output; + + // auto multivariate_query = sumcheck_output.multivariate_query; + // auto multivariate_evaluations = sumcheck_output.evaluations; // 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()); + 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"); - // Compute batched multivariate evaluation Fr batched_evaluation = Fr::zero(); for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { @@ -164,50 +170,37 @@ template bool Verifier::verify_pro } // 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) { + auto commitment = key->commitments[bonk::StandardArithmetization::ENUM_TO_COMM[i]]; batched_commitment_unshifted += commitment * rhos[i]; } + // add wire commitments + for (size_t i = 0; i < program_width; ++i) { + batched_commitment_unshifted += wire_commitments[i] * rhos[NUM_PRECOMPUTED + i]; + } + // add z_permutation commitment + batched_commitment_unshifted += z_permutation_commitment * rhos[NUM_PRECOMPUTED + program_width]; // 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_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, + auto gemini_claim = Gemini::reduce_verify(multivariate_query, 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"); + 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); - - // Do final pairing check - bool pairing_result = kzg_claim.verify(kate_verification_key); - - bool result = sumcheck_result && pairing_result; + auto kzg_claim = KZG::reduce_verify(shplonk_claim, transcript); - return result; + // Return result of final pairing check + return kzg_claim.verify(kate_verification_key); } template class Verifier; diff --git a/cpp/src/barretenberg/honk/proof_system/verifier.hpp b/cpp/src/barretenberg/honk/proof_system/verifier.hpp index 6c0f808199..8a1614dbd6 100644 --- a/cpp/src/barretenberg/honk/proof_system/verifier.hpp +++ b/cpp/src/barretenberg/honk/proof_system/verifier.hpp @@ -16,15 +16,13 @@ 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; Verifier& operator=(Verifier&& other); bool verify_proof(const plonk::proof& proof); - transcript::Manifest manifest; std::shared_ptr key; std::map kate_g1_elements; diff --git a/cpp/src/barretenberg/honk/proof_system/verifier.test.cpp b/cpp/src/barretenberg/honk/proof_system/verifier.test.cpp index 2eaf8559bc..1569723e90 100644 --- a/cpp/src/barretenberg/honk/proof_system/verifier.test.cpp +++ b/cpp/src/barretenberg/honk/proof_system/verifier.test.cpp @@ -66,7 +66,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); // std::unique_ptr> kate_commitment_scheme = // std::make_unique>(); @@ -192,8 +192,7 @@ template class VerifierTests : public testing::Test { // TODO(#223)(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/barretenberg/honk/sumcheck/polynomials/multivariates.test.cpp b/cpp/src/barretenberg/honk/sumcheck/polynomials/multivariates.test.cpp index aca89f9319..c5d7756b8a 100644 --- a/cpp/src/barretenberg/honk/sumcheck/polynomials/multivariates.test.cpp +++ b/cpp/src/barretenberg/honk/sumcheck/polynomials/multivariates.test.cpp @@ -3,6 +3,7 @@ #include "barretenberg/honk/sumcheck/sumcheck.hpp" #include +#include "barretenberg/honk/transcript/transcript.hpp" #include "barretenberg/numeric/random/engine.hpp" using namespace honk::sumcheck; @@ -11,7 +12,7 @@ namespace test_sumcheck_polynomials { template class MultivariatesTests : public testing::Test {}; using FieldTypes = testing::Types; -using Transcript = transcript::StandardTranscript; +using Transcript = honk::ProverTranscript; TYPED_TEST_SUITE(MultivariatesTests, FieldTypes); #define MULTIVARIATES_TESTS_TYPE_ALIASES using FF = TypeParam; @@ -58,7 +59,7 @@ TYPED_TEST(MultivariatesTests, FoldTwoRoundsSpecial) std::array f0 = { v00, v10, v01, v11 }; auto full_polynomials = std::array, 1>({ f0 }); - auto transcript = Transcript(transcript::Manifest()); + auto transcript = honk::ProverTranscript::init_empty(); auto sumcheck = Sumcheck(multivariate_n, transcript); FF round_challenge_0 = { 0x6c7301b49d85a46c, 0x44311531e39c64f6, 0xb13d66d8d6c1a24c, 0x04410c360230a295 }; @@ -93,7 +94,7 @@ TYPED_TEST(MultivariatesTests, FoldTwoRoundsGeneric) std::array f0 = { v00, v10, v01, v11 }; auto full_polynomials = std::array, 1>({ f0 }); - auto transcript = Transcript(transcript::Manifest()); + auto transcript = honk::ProverTranscript::init_empty(); auto sumcheck = Sumcheck(multivariate_n, transcript); FF round_challenge_0 = FF::random_element(); @@ -152,7 +153,7 @@ 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 transcript = honk::ProverTranscript::init_empty(); auto sumcheck = Sumcheck(multivariate_n, transcript); FF round_challenge_0 = 1; @@ -201,7 +202,7 @@ 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 transcript = honk::ProverTranscript::init_empty(); auto sumcheck = Sumcheck(multivariate_n, transcript); FF round_challenge_0 = FF::random_element(); @@ -260,7 +261,7 @@ 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 transcript = honk::ProverTranscript::init_empty(); auto sumcheck = Sumcheck(multivariate_n, transcript); std::array expected_q1; diff --git a/cpp/src/barretenberg/honk/sumcheck/polynomials/univariate.hpp b/cpp/src/barretenberg/honk/sumcheck/polynomials/univariate.hpp index 0ae49c5f10..89804ebc8f 100644 --- a/cpp/src/barretenberg/honk/sumcheck/polynomials/univariate.hpp +++ b/cpp/src/barretenberg/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/barretenberg/honk/sumcheck/sumcheck.hpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp index f918747be0..5ecca794fe 100644 --- a/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp @@ -1,6 +1,8 @@ #pragma once #include "barretenberg/common/serialize.hpp" #include +#include "barretenberg/honk/sumcheck/relations/relation.hpp" +#include "barretenberg/honk/transcript/transcript.hpp" #include "barretenberg/honk/utils/public_inputs.hpp" #include "barretenberg/common/throw_or_abort.hpp" #include "sumcheck_round.hpp" @@ -12,17 +14,23 @@ #include #include #include "barretenberg/honk/proof_system/prover.hpp" +#include "barretenberg/honk/sumcheck/sumcheck_output.hpp" namespace honk::sumcheck { + template class... Relations> class Sumcheck { public: 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; + std::array purported_evaluations; Transcript& transcript; const size_t multivariate_n; const size_t multivariate_d; - SumcheckRound round; + SumcheckRound round; + + // TODO(luke): The prover and verifier need a different type of trascript which is why Adrian made it an input to + // those respective functions and removed it as a template parameter. This could be done fairly minimally. /** * @@ -53,10 +61,13 @@ template class... Relations> cl * NOTE: With ~40 columns, prob only want to allocate 256 EdgeGroup's at once to keep stack under 1MB? * TODO(#224)(Cody): might want to just do C-style multidimensional array? for guaranteed adjacency? */ - std::array, bonk::StandardArithmetization::NUM_POLYNOMIALS> folded_polynomials; + std::array, NUM_POLYNOMIALS> folded_polynomials; + // the + std::vector multivariate_query; // multivariate query point u = (u_0, ..., u_{d-1}) + std::array multivariate_evaluations; // multivariate evaluations at 'u' // prover instantiates sumcheck with circuit size and transcript - Sumcheck(size_t multivariate_n, Transcript& transcript) + Sumcheck(size_t multivariate_n, ProverTranscript& transcript) : transcript(transcript) , multivariate_n(multivariate_n) , multivariate_d(numeric::get_msb(multivariate_n)) @@ -65,15 +76,13 @@ template class... Relations> cl for (auto& polynomial : folded_polynomials) { polynomial.resize(multivariate_n >> 1); } + multivariate_query.reserve(multivariate_d); }; // verifier instantiates with transcript alone - explicit Sumcheck(Transcript& transcript) + explicit Sumcheck(size_t multivariate_n, VerifierTranscript& 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_n(multivariate_n) , multivariate_d(numeric::get_msb(multivariate_n)) , round(std::tuple(Relations()...)) { @@ -118,19 +127,22 @@ template class... Relations> cl * * @details */ - void execute_prover(auto full_polynomials) // pass by value, not by reference + SumcheckOutput execute_prover( + auto full_polynomials, const RelationParameters& relation_parameters) // pass by value, not by reference { // First round // This populates folded_polynomials. - const auto relation_parameters = retrieve_proof_parameters(); - PowUnivariate pow_univariate(relation_parameters.zeta); + auto [alpha, zeta] = transcript.get_challenges("Sumcheck:alpha", "Sumcheck:zeta"); - 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()); + // const auto relation_parameters = retrieve_proof_parameters(); + PowUnivariate pow_univariate(zeta); + + // TODO(luke): this logic can just be incorporated into the loop below w/ round_idx = 0 + auto round_univariate = round.compute_univariate(full_polynomials, relation_parameters, pow_univariate, alpha); + transcript.send_to_verifier("Sumcheck:univariate_0", round_univariate); + FF round_challenge = transcript.get_challenge("Sumcheck:u_0"); + multivariate_query.emplace_back(round_challenge); fold(full_polynomials, multivariate_n, round_challenge); pow_univariate.partially_evaluate(round_challenge); round.round_size = round.round_size >> 1; // TODO(#224)(Cody): Maybe fold should do this and release memory? @@ -139,22 +151,22 @@ 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()); + round_univariate = round.compute_univariate(folded_polynomials, relation_parameters, pow_univariate, alpha); + transcript.send_to_verifier("Sumcheck:univariate_" + std::to_string(round_idx), round_univariate); + FF round_challenge = transcript.get_challenge("Sumcheck:u_" + std::to_string(round_idx)); + multivariate_query.emplace_back(round_challenge); fold(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) { + 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("multivariate_evaluations", multivariate_evaluations); + + return { multivariate_query, multivariate_evaluations }; }; /** @@ -162,12 +174,14 @@ 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(const RelationParameters& relation_parameters) { bool verified(true); - const auto relation_parameters = retrieve_proof_parameters(); - PowUnivariate pow_univariate(relation_parameters.zeta); + auto [alpha, zeta] = transcript.get_challenges("Sumcheck:alpha", "Sumcheck:zeta"); + + // const auto relation_parameters = retrieve_proof_parameters(); + PowUnivariate pow_univariate(zeta); // All but final round. // target_total_sum is initialized to zero then mutated in place. @@ -175,29 +189,40 @@ template class... Relations> cl throw_or_abort("Number of variables in multivariate is 0."); } + std::vector multivariate_query; + multivariate_query.reserve(multivariate_d); + for (size_t round_idx = 0; round_idx < multivariate_d; round_idx++) { // Obtain the round univariate from the transcript - auto round_univariate = Univariate::serialize_from_buffer( - &transcript.get_element("univariate_" + std::to_string(round_idx))[0]); + std::string round_univariate_label = "Sumcheck:univariate_" + std::to_string(round_idx); + auto round_univariate = + transcript.template receive_from_prover>(round_univariate_label); + 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()); + FF round_challenge = transcript.get_challenge("Sumcheck:u_" + std::to_string(round_idx)); + multivariate_query.emplace_back(round_challenge); round.compute_next_target_sum(round_univariate, round_challenge, pow_univariate); pow_univariate.partially_evaluate(round_challenge); if (!verified) { - return false; + return std::nullopt; } } // Final round - auto purported_evaluations = transcript.get_field_element_vector("multivariate_evaluations"); + auto purported_evaluations = + transcript.template receive_from_prover>("Sumcheck: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; + if (!verified) { + return std::nullopt; + } + + return SumcheckOutput{ multivariate_query, purported_evaluations }; }; // TODO(#224)(Cody): Rename. fold is not descriptive, and it's already in use in the Gemini context. @@ -223,7 +248,6 @@ template class... Relations> cl { // after the first round, operate in place on folded_polynomials for (size_t j = 0; j < polynomials.size(); ++j) { - // for (size_t j = 0; j < bonk::StandardArithmetization::NUM_POLYNOMIALS; ++j) { for (size_t i = 0; i < round_size; i += 2) { folded_polynomials[j][i >> 1] = polynomials[j][i] + round_challenge * (polynomials[j][i + 1] - polynomials[j][i]); diff --git a/cpp/src/barretenberg/honk/sumcheck/sumcheck.test.cpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck.test.cpp index e8b6830233..233470f19e 100644 --- a/cpp/src/barretenberg/honk/sumcheck/sumcheck.test.cpp +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck.test.cpp @@ -1,4 +1,5 @@ #include "sumcheck.hpp" +#include "barretenberg/honk/transcript/transcript.hpp" #include "barretenberg/proof_system/flavor/flavor.hpp" #include "barretenberg/transcript/transcript_wrappers.hpp" #include "relations/arithmetic_relation.hpp" @@ -14,13 +15,13 @@ #include #include +#include #include #include #include using namespace honk; using namespace honk::sumcheck; -using Transcript = transcript::StandardTranscript; using FF = barretenberg::fr; const size_t NUM_POLYNOMIALS = bonk::StandardArithmetization::NUM_POLYNOMIALS; using POLYNOMIAL = bonk::StandardArithmetization::POLYNOMIAL; @@ -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(#225)(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,19 @@ TEST(Sumcheck, PolynomialNormalization) lagrange_first, lagrange_last); - auto transcript = produce_mocked_transcript(multivariate_d, num_public_inputs); + auto transcript = ProverTranscript::init_empty(); auto sumcheck = Sumcheck, ArithmeticRelation, GrandProductComputationRelation, GrandProductInitializationRelation>(multivariate_n, transcript); - sumcheck.execute_prover(full_polynomials); + auto [multivariate_query, evaluations] = 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"); - FF u_2 = transcript.get_challenge_field_element("u_2"); + FF u_0 = multivariate_query[0]; + FF u_1 = multivariate_query[1]; + FF u_2 = multivariate_query[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 @@ -247,7 +191,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 +240,16 @@ TEST(Sumcheck, Prover) lagrange_first, lagrange_last); - auto transcript = produce_mocked_transcript(multivariate_d, num_public_inputs); + auto transcript = ProverTranscript::init_empty(); auto sumcheck = Sumcheck, ArithmeticRelation, GrandProductComputationRelation, GrandProductInitializationRelation>(multivariate_n, transcript); - 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 [multivariate_query, evaluations] = sumcheck.execute_prover(full_polynomials, {}); + FF u_0 = multivariate_query[0]; + FF u_1 = multivariate_query[1]; std::vector expected_values; for (auto& polynomial : full_polynomials) { // using knowledge of inputs here to derive the evaluation @@ -316,10 +259,9 @@ 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(evaluations[poly_idx], expected_values[poly_idx]); } }; run_test(/* is_random_input=*/false); @@ -331,7 +273,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 }; @@ -371,24 +312,28 @@ TEST(Sumcheck, ProverAndVerifier) lagrange_first, lagrange_last); - auto transcript = produce_mocked_transcript(multivariate_d, num_public_inputs); + auto prover_transcript = ProverTranscript::init_empty(); auto sumcheck_prover = Sumcheck, ArithmeticRelation, GrandProductComputationRelation, - GrandProductInitializationRelation>(multivariate_n, transcript); + GrandProductInitializationRelation>(multivariate_n, prover_transcript); + + auto prover_output = sumcheck_prover.execute_prover(full_polynomials, {}); - sumcheck_prover.execute_prover(full_polynomials); + auto verifier_transcript = VerifierTranscript::init_empty(prover_transcript); auto sumcheck_verifier = Sumcheck, ArithmeticRelation, GrandProductComputationRelation, - GrandProductInitializationRelation>(transcript); + GrandProductInitializationRelation>(multivariate_n, verifier_transcript); - bool verified = sumcheck_verifier.execute_verifier(); - ASSERT_TRUE(verified); + std::optional verifier_output = sumcheck_verifier.execute_verifier({}); + + ASSERT_TRUE(verifier_output.has_value()); + ASSERT_EQ(prover_output, *verifier_output); } // TODO(#225): make the inputs to this test more interesting, e.g. num_public_inputs > 0 and non-trivial permutations @@ -397,7 +342,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; @@ -443,24 +387,27 @@ TEST(Sumcheck, ProverAndVerifierLonger) lagrange_first, lagrange_last); - auto transcript = produce_mocked_transcript(multivariate_d, num_public_inputs); + auto prover_transcript = ProverTranscript::init_empty(); auto sumcheck_prover = Sumcheck, ArithmeticRelation, GrandProductComputationRelation, - GrandProductInitializationRelation>(multivariate_n, transcript); + GrandProductInitializationRelation>(multivariate_n, prover_transcript); + + auto prover_output = sumcheck_prover.execute_prover(full_polynomials, {}); - sumcheck_prover.execute_prover(full_polynomials); + auto verifier_transcript = VerifierTranscript::init_empty(prover_transcript); auto sumcheck_verifier = Sumcheck, ArithmeticRelation, GrandProductComputationRelation, - GrandProductInitializationRelation>(transcript); + GrandProductInitializationRelation>(multivariate_n, verifier_transcript); + + std::optional verifier_output = sumcheck_verifier.execute_verifier({}); - 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/barretenberg/honk/sumcheck/sumcheck_output.hpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck_output.hpp new file mode 100644 index 0000000000..b25a2e4a34 --- /dev/null +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck_output.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +namespace honk::sumcheck { + +/** + * @brief Contains the multi-linear `evaluations` of the polynomials at the `evaluation_point`. + * These are computed by the prover and need to be checked using a multi-linear PCS like Gemini. + */ +template struct SumcheckOutput { + // u = (u_0, ..., u_{d-1}) + std::vector multivariate_query; + // Evaluations in `u` of the polynomials used in Sumcheck + std::array evaluations; + + bool operator==(const SumcheckOutput& other) const = default; +}; +} // namespace honk::sumcheck diff --git a/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp index fa1d5750e0..45d6c787fb 100644 --- a/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp @@ -175,7 +175,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. @@ -191,7 +192,7 @@ 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<>(); @@ -207,7 +208,7 @@ template class... Relation * checked against the final value of the target total sum, defined as sigma_d. */ // TODO(#224)(Cody): Input should be an array? - FF compute_full_honk_relation_purported_value(std::vector& purported_evaluations, + FF compute_full_honk_relation_purported_value(std::span purported_evaluations, const RelationParameters& relation_parameters, const PowUnivariate& pow_univariate) { @@ -304,7 +305,7 @@ template class... Relation */ template // TODO(#224)(Cody): Input should be an array? - void accumulate_relation_evaluations(std::vector& purported_evaluations, + void accumulate_relation_evaluations(std::span purported_evaluations, const RelationParameters& relation_parameters) { std::get(relations).add_full_relation_value_contribution( diff --git a/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp index 95f2677239..f39c66f2c8 100644 --- a/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp @@ -128,7 +128,7 @@ static Univariate compute_round_univariate( lagrange_last); PowUnivariate pow_zeta(relation_parameters.zeta); Univariate round_univariate = - round.compute_univariate(full_polynomials, relation_parameters, pow_zeta); + round.compute_univariate(full_polynomials, relation_parameters, pow_zeta, relation_parameters.alpha); return round_univariate; } From 03c04223b689324c7e70b7bca5a758f4c3390805 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Thu, 16 Mar 2023 21:14:50 +0000 Subject: [PATCH 03/10] tests passing --- .../composer/standard_honk_composer.test.cpp | 3 - .../barretenberg/honk/proof_system/prover.cpp | 3 - .../honk/proof_system/verifier.cpp | 4 +- .../honk/sumcheck/relations/relation.hpp | 4 +- .../honk/sumcheck/relations/relation.test.cpp | 4 +- .../barretenberg/honk/sumcheck/sumcheck.hpp | 5 +- .../honk/sumcheck/sumcheck.test.cpp | 24 +++++- .../honk/sumcheck/sumcheck_round.hpp | 5 +- .../honk/sumcheck/sumcheck_round.test.cpp | 79 ++++++++++--------- 9 files changed, 73 insertions(+), 58 deletions(-) diff --git a/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp b/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp index ea544cde58..2913d3c1d0 100644 --- a/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp +++ b/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp @@ -335,7 +335,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 +342,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, diff --git a/cpp/src/barretenberg/honk/proof_system/prover.cpp b/cpp/src/barretenberg/honk/proof_system/prover.cpp index 835028ba72..654f2ec75a 100644 --- a/cpp/src/barretenberg/honk/proof_system/prover.cpp +++ b/cpp/src/barretenberg/honk/proof_system/prover.cpp @@ -276,11 +276,8 @@ template void Prover::execute_relation_check_round sumcheck::GrandProductComputationRelation, sumcheck::GrandProductInitializationRelation>; - // transcript.apply_fiat_shamir("alpha"); - auto sumcheck = Sumcheck(key->circuit_size, transcript); - // TODO(luke): should sumcheck_output be replaced by simply multivariate_query? sumcheck_output = sumcheck.execute_prover(prover_polynomials, relation_parameters); } diff --git a/cpp/src/barretenberg/honk/proof_system/verifier.cpp b/cpp/src/barretenberg/honk/proof_system/verifier.cpp index 7baee96270..8259c85df0 100644 --- a/cpp/src/barretenberg/honk/proof_system/verifier.cpp +++ b/cpp/src/barretenberg/honk/proof_system/verifier.cpp @@ -23,6 +23,7 @@ #include "barretenberg/honk/utils/power_polynomial.hpp" #include "barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp" #include "barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp" +#include using namespace barretenberg; using namespace honk::sumcheck; @@ -148,9 +149,6 @@ template bool Verifier::verify_pro auto [multivariate_query, multivariate_evaluations] = *sumcheck_output; - // auto multivariate_query = sumcheck_output.multivariate_query; - // auto multivariate_evaluations = sumcheck_output.evaluations; - // Execute Gemini/Shplonk verification: // Construct inputs for Gemini verifier: diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp b/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp index 143246544a..c3411bb5ee 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp @@ -4,8 +4,8 @@ namespace honk::sumcheck { // TODO(#226)(Adrian): Remove zeta, alpha as they are not used by the relations. template struct RelationParameters { - FF zeta; - FF alpha; + // FF zeta; + // FF alpha; FF beta; FF gamma; FF public_input_delta; diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/relation.test.cpp b/cpp/src/barretenberg/honk/sumcheck/relations/relation.test.cpp index b169924f08..070cf142f7 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/relation.test.cpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/relation.test.cpp @@ -93,9 +93,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() }; } diff --git a/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp index 5ecca794fe..f426469216 100644 --- a/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp @@ -15,6 +15,8 @@ #include #include "barretenberg/honk/proof_system/prover.hpp" #include "barretenberg/honk/sumcheck/sumcheck_output.hpp" +#include + namespace honk::sumcheck { template class... Relations> class Sumcheck { @@ -180,7 +182,6 @@ template class... Relations> cl auto [alpha, zeta] = transcript.get_challenges("Sumcheck:alpha", "Sumcheck:zeta"); - // const auto relation_parameters = retrieve_proof_parameters(); PowUnivariate pow_univariate(zeta); // All but final round. // target_total_sum is initialized to zero then mutated in place. @@ -216,7 +217,7 @@ template class... Relations> cl transcript.template receive_from_prover>("Sumcheck:evaluations"); FF full_honk_relation_purported_value = round.compute_full_honk_relation_purported_value( - purported_evaluations, relation_parameters, pow_univariate); + purported_evaluations, relation_parameters, pow_univariate, alpha); verified = verified && (full_honk_relation_purported_value == round.target_total_sum); if (!verified) { return std::nullopt; diff --git a/cpp/src/barretenberg/honk/sumcheck/sumcheck.test.cpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck.test.cpp index 233470f19e..3d8cd4b23e 100644 --- a/cpp/src/barretenberg/honk/sumcheck/sumcheck.test.cpp +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck.test.cpp @@ -241,6 +241,7 @@ TEST(Sumcheck, Prover) lagrange_last); auto transcript = ProverTranscript::init_empty(); + auto sumcheck = Sumcheck, ArithmeticRelation, @@ -269,6 +270,8 @@ TEST(Sumcheck, Prover) } // TODO(#223)(Cody): write standalone test of the verifier. +// Note(luke): This test (and ProverAndVerifierLonger) are slighly misleading in that they include the grand product +// realtions but do not test their correctness due to the choice of zero polynomials for sigma, id etc. TEST(Sumcheck, ProverAndVerifier) { const size_t multivariate_d(1); @@ -311,6 +314,12 @@ TEST(Sumcheck, ProverAndVerifier) id_3, lagrange_first, lagrange_last); + // Set aribitrary random relation parameters + sumcheck::RelationParameters relation_parameters{ + .beta = FF::random_element(), + .gamma = FF::random_element(), + .public_input_delta = FF::one(), + }; auto prover_transcript = ProverTranscript::init_empty(); @@ -320,7 +329,7 @@ TEST(Sumcheck, ProverAndVerifier) GrandProductComputationRelation, GrandProductInitializationRelation>(multivariate_n, prover_transcript); - auto prover_output = sumcheck_prover.execute_prover(full_polynomials, {}); + auto prover_output = sumcheck_prover.execute_prover(full_polynomials, relation_parameters); auto verifier_transcript = VerifierTranscript::init_empty(prover_transcript); @@ -330,7 +339,7 @@ TEST(Sumcheck, ProverAndVerifier) GrandProductComputationRelation, GrandProductInitializationRelation>(multivariate_n, verifier_transcript); - std::optional verifier_output = sumcheck_verifier.execute_verifier({}); + std::optional verifier_output = sumcheck_verifier.execute_verifier(relation_parameters); ASSERT_TRUE(verifier_output.has_value()); ASSERT_EQ(prover_output, *verifier_output); @@ -387,6 +396,13 @@ TEST(Sumcheck, ProverAndVerifierLonger) lagrange_first, lagrange_last); + // Set aribitrary random relation parameters + sumcheck::RelationParameters relation_parameters{ + .beta = FF::random_element(), + .gamma = FF::random_element(), + .public_input_delta = FF::one(), + }; + auto prover_transcript = ProverTranscript::init_empty(); auto sumcheck_prover = Sumcheck(multivariate_n, prover_transcript); - auto prover_output = sumcheck_prover.execute_prover(full_polynomials, {}); + auto prover_output = sumcheck_prover.execute_prover(full_polynomials, relation_parameters); auto verifier_transcript = VerifierTranscript::init_empty(prover_transcript); @@ -405,7 +421,7 @@ TEST(Sumcheck, ProverAndVerifierLonger) GrandProductComputationRelation, GrandProductInitializationRelation>(multivariate_n, verifier_transcript); - std::optional verifier_output = sumcheck_verifier.execute_verifier({}); + std::optional verifier_output = sumcheck_verifier.execute_verifier(relation_parameters); EXPECT_EQ(verifier_output.has_value(), expect_verified); }; diff --git a/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp index 45d6c787fb..021d14730f 100644 --- a/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp @@ -210,7 +210,8 @@ template class... Relation // TODO(#224)(Cody): Input should be an array? FF compute_full_honk_relation_purported_value(std::span purported_evaluations, const RelationParameters& relation_parameters, - const PowUnivariate& pow_univariate) + const PowUnivariate& pow_univariate, + const FF alpha) { accumulate_relation_evaluations<>(purported_evaluations, relation_parameters); @@ -219,7 +220,7 @@ template class... Relation FF output = 0; for (auto& evals : evaluations) { output += evals * running_challenge; - running_challenge *= relation_parameters.alpha; + running_challenge *= alpha; } output *= pow_univariate.partial_evaluation_constant; diff --git a/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp index f39c66f2c8..eac7240906 100644 --- a/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp @@ -78,7 +78,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 +127,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, relation_parameters.alpha); + 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,14 +179,15 @@ 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); @@ -213,14 +216,15 @@ static FF compute_full_purported_value(std::array& input_va ArithmeticRelation, GrandProductComputationRelation, GrandProductInitializationRelation>(relations); - PowUnivariate pow_univariate(relation_parameters.zeta); - FF full_purported_value = - round.compute_full_honk_relation_purported_value(purported_evaluations, relation_parameters, pow_univariate); + PowUnivariate pow_univariate(1); + FF full_purported_value = round.compute_full_honk_relation_purported_value( + purported_evaluations, relation_parameters, pow_univariate, 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 +255,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,35 +269,39 @@ 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; for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { input_polynomials[i] = { 1, 2 }; } + const FF alpha = 1; 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, 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]); } // 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, alpha); EXPECT_EQ(round_univariate, expected_round_univariate); }; }; @@ -309,28 +317,27 @@ 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; for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { input_values[i] = FF(2); } + const FF alpha = 1; 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, 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); }; }; From be3bc288db7992cde7359f69cc681475651dddb4 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Fri, 17 Mar 2023 18:35:02 +0000 Subject: [PATCH 04/10] tests added for transcript and new manifest concept --- .../barretenberg/honk/pcs/gemini/gemini.hpp | 25 +- .../barretenberg/honk/pcs/kzg/kzg.test.cpp | 1 - .../honk/pcs/shplonk/shplonk_single.hpp | 1 + .../barretenberg/honk/proof_system/prover.cpp | 2 +- .../barretenberg/honk/proof_system/prover.hpp | 1 - .../honk/proof_system/verifier.cpp | 13 +- .../honk/sumcheck/relations/relation.hpp | 2 - .../barretenberg/honk/sumcheck/sumcheck.hpp | 59 +---- .../honk/transcript/transcript.hpp | 52 ++++- .../honk/transcript/transcript.test.cpp | 216 ++++++++++++++++++ 10 files changed, 282 insertions(+), 90 deletions(-) create mode 100644 cpp/src/barretenberg/honk/transcript/transcript.test.cpp diff --git a/cpp/src/barretenberg/honk/pcs/gemini/gemini.hpp b/cpp/src/barretenberg/honk/pcs/gemini/gemini.hpp index 1486fddad7..cfd49fbae9 100644 --- a/cpp/src/barretenberg/honk/pcs/gemini/gemini.hpp +++ b/cpp/src/barretenberg/honk/pcs/gemini/gemini.hpp @@ -262,6 +262,7 @@ template class MultilinearReductionScheme { { const size_t num_variables = mle_opening_point.size(); + // Get polynomials Fold_i, i = 1,...,m-1 from transcript std::vector commitments; commitments.reserve(num_variables - 1); for (size_t i = 0; i < num_variables - 1; ++i) { @@ -305,30 +306,6 @@ template class MultilinearReductionScheme { 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/barretenberg/honk/pcs/kzg/kzg.test.cpp b/cpp/src/barretenberg/honk/pcs/kzg/kzg.test.cpp index 73f97255a5..664f437cbe 100644 --- a/cpp/src/barretenberg/honk/pcs/kzg/kzg.test.cpp +++ b/cpp/src/barretenberg/honk/pcs/kzg/kzg.test.cpp @@ -62,7 +62,6 @@ TYPED_TEST(BilinearAccumulationTest, GeminiShplonkKzgWithShift) using Gemini = gemini::MultilinearReductionScheme; using KZG = UnivariateOpeningScheme; using Fr = typename TypeParam::Fr; - // using Transcript = ProverTranscript; using Commitment = typename TypeParam::Commitment; using Polynomial = typename barretenberg::Polynomial; diff --git a/cpp/src/barretenberg/honk/pcs/shplonk/shplonk_single.hpp b/cpp/src/barretenberg/honk/pcs/shplonk/shplonk_single.hpp index aa6e4f1065..4467128dc4 100644 --- a/cpp/src/barretenberg/honk/pcs/shplonk/shplonk_single.hpp +++ b/cpp/src/barretenberg/honk/pcs/shplonk/shplonk_single.hpp @@ -118,6 +118,7 @@ template class SingleBatchOpeningScheme { const size_t num_claims = claims.size(); const Fr nu = transcript.get_challenge("Shplonk:nu"); + auto Q_commitment = transcript.template receive_from_prover("Shplonk:Q"); const Fr z_challenge = transcript.get_challenge("Shplonk:z"); diff --git a/cpp/src/barretenberg/honk/proof_system/prover.cpp b/cpp/src/barretenberg/honk/proof_system/prover.cpp index 654f2ec75a..4f2501e2d1 100644 --- a/cpp/src/barretenberg/honk/proof_system/prover.cpp +++ b/cpp/src/barretenberg/honk/proof_system/prover.cpp @@ -271,7 +271,7 @@ template void Prover::execute_relation_check_round // queue.flush_queue(); // NOTE: Don't remove; we may reinstate the queue using Sumcheck = sumcheck::Sumcheck, sumcheck::ArithmeticRelation, sumcheck::GrandProductComputationRelation, sumcheck::GrandProductInitializationRelation>; diff --git a/cpp/src/barretenberg/honk/proof_system/prover.hpp b/cpp/src/barretenberg/honk/proof_system/prover.hpp index f75971c12b..b962c6c5b2 100644 --- a/cpp/src/barretenberg/honk/proof_system/prover.hpp +++ b/cpp/src/barretenberg/honk/proof_system/prover.hpp @@ -88,7 +88,6 @@ template class Prover { pcs::gemini::ProverOutput gemini_output; pcs::shplonk::ProverOutput shplonk_output; - using Transcript = ProverTranscript; using Gemini = pcs::gemini::MultilinearReductionScheme; using Shplonk = pcs::shplonk::SingleBatchOpeningScheme; using KZG = pcs::kzg::UnivariateOpeningScheme; diff --git a/cpp/src/barretenberg/honk/proof_system/verifier.cpp b/cpp/src/barretenberg/honk/proof_system/verifier.cpp index 8259c85df0..cf528dec4f 100644 --- a/cpp/src/barretenberg/honk/proof_system/verifier.cpp +++ b/cpp/src/barretenberg/honk/proof_system/verifier.cpp @@ -23,7 +23,6 @@ #include "barretenberg/honk/utils/power_polynomial.hpp" #include "barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp" #include "barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp" -#include using namespace barretenberg; using namespace honk::sumcheck; @@ -80,7 +79,6 @@ template bool Verifier::verify_pro using FF = typename program_settings::fr; using Commitment = barretenberg::g1::element; using CommitmentAffine = barretenberg::g1::affine_element; - using Transcript = VerifierTranscript; using Gemini = pcs::gemini::MultilinearReductionScheme; using Shplonk = pcs::shplonk::SingleBatchOpeningScheme; using KZG = pcs::kzg::UnivariateOpeningScheme; @@ -90,12 +88,9 @@ template bool Verifier::verify_pro constexpr auto program_width = program_settings::program_width; - // const size_t log_n(numeric::get_msb(key->circuit_size)); - - VerifierTranscript transcript{ proof.proof_data }; + VerifierTranscript transcript{ proof.proof_data }; - // TODO(Adrian): Change the initialization of the transcript to take the VK hash? Also need to add the - // commitments... + // TODO(Adrian): Change the initialization of the transcript to take the VK hash? const auto circuit_size = transcript.template receive_from_prover("circuit_size"); const auto public_input_size = transcript.template receive_from_prover("public_input_size"); @@ -136,13 +131,13 @@ template bool Verifier::verify_pro // Execute Sumcheck Verifier auto sumcheck = Sumcheck, ArithmeticRelation, GrandProductComputationRelation, GrandProductInitializationRelation>(circuit_size, transcript); std::optional sumcheck_output = sumcheck.execute_verifier(relation_parameters); - // Abort if sumcheck failed and returned nothing + // If Sumcheck does not return an output, sumcheck verification has failed if (!sumcheck_output.has_value()) { return false; } diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp b/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp index c3411bb5ee..8218aaa3f0 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp @@ -4,8 +4,6 @@ namespace honk::sumcheck { // TODO(#226)(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/barretenberg/honk/sumcheck/sumcheck.hpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp index f426469216..6330c87d2c 100644 --- a/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp @@ -31,9 +31,6 @@ template class... Relations> cl const size_t multivariate_d; SumcheckRound round; - // TODO(luke): The prover and verifier need a different type of trascript which is why Adrian made it an input to - // those respective functions and removed it as a template parameter. This could be done fairly minimally. - /** * * @brief (folded_polynomials) Suppose the Honk polynomials (multilinear in d variables) are called P_1, ..., P_N. @@ -64,11 +61,8 @@ template class... Relations> cl * TODO(#224)(Cody): might want to just do C-style multidimensional array? for guaranteed adjacency? */ std::array, NUM_POLYNOMIALS> folded_polynomials; - // the - std::vector multivariate_query; // multivariate query point u = (u_0, ..., u_{d-1}) - std::array multivariate_evaluations; // multivariate evaluations at 'u' - // prover instantiates sumcheck with circuit size and transcript + // prover instantiates sumcheck with circuit size and a prover transcript Sumcheck(size_t multivariate_n, ProverTranscript& transcript) : transcript(transcript) , multivariate_n(multivariate_n) @@ -78,50 +72,14 @@ template class... Relations> cl for (auto& polynomial : folded_polynomials) { polynomial.resize(multivariate_n >> 1); } - multivariate_query.reserve(multivariate_d); }; - // verifier instantiates with transcript alone + // verifier instantiates sumcheck with circuit size and a verifier transcript explicit Sumcheck(size_t multivariate_n, VerifierTranscript& transcript) : transcript(transcript) , multivariate_n(multivariate_n) , 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; - } + , round(std::tuple(Relations()...)){}; /** * @brief Compute univariate restriction place in transcript, generate challenge, fold,... repeat until final round, @@ -132,15 +90,15 @@ template class... Relations> cl SumcheckOutput execute_prover( auto full_polynomials, const RelationParameters& relation_parameters) // pass by value, not by reference { - // First round - // This populates folded_polynomials. - auto [alpha, zeta] = transcript.get_challenges("Sumcheck:alpha", "Sumcheck:zeta"); - // const auto relation_parameters = retrieve_proof_parameters(); PowUnivariate pow_univariate(zeta); - // TODO(luke): this logic can just be incorporated into the loop below w/ round_idx = 0 + std::vector multivariate_query; + multivariate_query.reserve(multivariate_d); + + // First round + // This populates folded_polynomials. auto round_univariate = round.compute_univariate(full_polynomials, relation_parameters, pow_univariate, alpha); transcript.send_to_verifier("Sumcheck:univariate_0", round_univariate); FF round_challenge = transcript.get_challenge("Sumcheck:u_0"); @@ -163,6 +121,7 @@ template class... Relations> cl } // Final round: Extract multivariate evaluations from folded_polynomials and add to transcript + std::array multivariate_evaluations; for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { multivariate_evaluations[i] = folded_polynomials[i][0]; } diff --git a/cpp/src/barretenberg/honk/transcript/transcript.hpp b/cpp/src/barretenberg/honk/transcript/transcript.hpp index 46883e3458..3d229fdb0c 100644 --- a/cpp/src/barretenberg/honk/transcript/transcript.hpp +++ b/cpp/src/barretenberg/honk/transcript/transcript.hpp @@ -10,10 +10,38 @@ #include #include #include +#include #include +#include namespace honk { +// class TranscriptSummary { +// struct RoundData { +// std::vector challenge_label; +// std::vector> entries; +// }; + +// std::map manifest; + +// void print() { +// for (size_t i = 0; i < manifest.size(); ++i) { +// info("Round: ", i); +// info("challenge: ", manifest[i].challenge_label[0]); +// for (auto& entry : manifest[i].entries) { +// info("\t", entry.first); +// } +// } +// } +// }; + +struct RoundData { + std::vector challenge_label; + std::vector> entries; + + bool operator==(const RoundData& other) const = default; +}; + /** * @brief Common transcript functionality for both parties. Stores the data for the current round, as well as the * manifest. @@ -28,6 +56,7 @@ template class BaseTranscript { size_t round_number = 0; std::array previous_challenge_buffer{}; std::vector current_round_data; + std::map manifest; /** * @brief Compute c_next = H( Compress(c_prev || round_buffer) ) @@ -73,6 +102,8 @@ template class BaseTranscript { { (void)label; + manifest[round_number].entries.emplace_back(label, element_bytes.size()); + current_round_data.insert(current_round_data.end(), element_bytes.begin(), element_bytes.end()); } @@ -93,7 +124,9 @@ template class BaseTranscript { 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, ...); + // ((void)labels, ...); + // std::array challenge_labels = labels...; + manifest[round_number].challenge_label = { labels... }; // Compute the new challenge buffer from which we derive the challenges. auto next_challenge_buffer = get_next_challenge_buffer(); @@ -122,6 +155,21 @@ template class BaseTranscript { } FF get_challenge(const std::string& label) { return get_challenges(label)[0]; } + + [[nodiscard]] std::map get_manifest() const { return manifest; }; + + void print() + { + for (auto& round : manifest) { + info("Round: ", round.first); + for (auto& label : round.second.challenge_label) { + info("\tchallenge: ", label); + } + for (auto& entry : round.second.entries) { + info("\telement (", entry.second, "): ", entry.first); + } + } + } }; template class ProverTranscript : public BaseTranscript { @@ -156,7 +204,7 @@ template class ProverTranscript : public BaseTranscript { static ProverTranscript init_empty() { ProverTranscript transcript; - constexpr uint32_t init{ 42 }; + constexpr uint32_t init{ 42 }; // arbitrary transcript.send_to_verifier("Init", init); return transcript; }; diff --git a/cpp/src/barretenberg/honk/transcript/transcript.test.cpp b/cpp/src/barretenberg/honk/transcript/transcript.test.cpp new file mode 100644 index 0000000000..7baf0a284a --- /dev/null +++ b/cpp/src/barretenberg/honk/transcript/transcript.test.cpp @@ -0,0 +1,216 @@ +#include "transcript.hpp" +#include "barretenberg/ecc/curves/bn254/g1.hpp" +#include "barretenberg/honk/composer/standard_honk_composer.hpp" +#include "barretenberg/honk/sumcheck/polynomials/univariate.hpp" +#include "barretenberg/numeric/bitop/get_msb.hpp" +#include "barretenberg/proof_system/flavor/flavor.hpp" +// #include "standard_honk_composer.hpp" +#include +#include +#include +#include + +using namespace honk; + +template class TranscriptTest : public testing::Test { + public: + /** + * @brief Construct a manifest for a standard Honk proof with n = 8, (d = 3) + * + * @return std::map + */ + std::map construct_standard_honk_manifest(size_t circuit_size) + { + std::map manifest_expected; + + size_t log_n(numeric::get_msb(circuit_size)); + + size_t max_relation_length = 5; + size_t size_FF = sizeof(FF); + size_t size_G = 2 * size_FF; + size_t size_uni = max_relation_length * size_FF; + size_t size_evals = StandardArithmetization::NUM_POLYNOMIALS * size_FF; + + // clang-format off + + size_t round = 0; + manifest_expected[round].entries = { {"circuit_size", 4}, + {"public_input_size", 4}, + {"public_input_0", size_FF}, + {"W_1", size_G}, + {"W_2", size_G}, + {"W_3", size_G} }; + manifest_expected[round].challenge_label = {"beta", "gamma"}; + + round++; + manifest_expected[round].entries = { {"Z_PERM", size_G} }; + manifest_expected[round].challenge_label = {"Sumcheck:alpha", "Sumcheck:zeta"}; + + for (size_t i = 0; i < log_n; ++i) { + round++; + std::string idx = std::to_string(i); + manifest_expected[round].entries = { {"Sumcheck:univariate_" + idx, size_uni} }; + manifest_expected[round].challenge_label = {"Sumcheck:u_" + idx}; + } + + round++; + manifest_expected[round].entries = { {"multivariate_evaluations", size_evals} }; + manifest_expected[round].challenge_label = {"rho"}; + + round++; + for (size_t i = 1; i < log_n; ++i) { + std::string idx = std::to_string(i); + manifest_expected[round].entries.emplace_back ("Gemini:FOLD_" + idx, size_G ); + } + manifest_expected[round].challenge_label = {"Gemini:r"}; + + round++; + for (size_t i = 0; i < log_n; ++i) { + std::string idx = std::to_string(i); + manifest_expected[round].entries.emplace_back ("Gemini:a_" + idx, size_FF ); + } + manifest_expected[round].challenge_label = {"Shplonk:nu"}; + + round++; + manifest_expected[round].entries = { {"Shplonk:Q", size_G} }; + manifest_expected[round].challenge_label = {"Shplonk:z"}; + + round++; + manifest_expected[round].entries = { {"KZG:W", size_G} }; + manifest_expected[round].challenge_label = {}; // no challenge + + // clang-format on + + return manifest_expected; + } +}; + +using FieldTypes = testing::Types; +TYPED_TEST_SUITE(TranscriptTest, FieldTypes); + +/** + * @brief Ensure consistency between the manifests generated by the standard honk prover and verfier for a simple + * circuit of size n = 8 + * + */ +TYPED_TEST(TranscriptTest, StandardHonkManifest) +{ + auto composer = StandardHonkComposer(); + fr a = 1; + composer.circuit_constructor.add_variable(a); + composer.circuit_constructor.add_public_variable(a); + + auto prover = composer.create_prover(); + plonk::proof proof = prover.construct_proof(); + auto verifier = composer.create_verifier(); + bool verified = verifier.verify_proof(proof); + ASSERT_TRUE(verified); + + auto manifest_expected = TestFixture::construct_standard_honk_manifest(prover.key->circuit_size); + + // Uncomment to see a summary printout of the transcript + // prover.transcript.print(); + + ASSERT_EQ(prover.transcript.get_manifest(), manifest_expected); +} + +/** + * @brief Test and demonstrate the basic functionality of the prover and verifier transcript + * + */ +TYPED_TEST(TranscriptTest, ProverAndVerifierBasic) +{ + constexpr size_t LENGTH = 8; + + using Fr = barretenberg::fr; + using Univariate = honk::sumcheck::Univariate; + using Commitment = barretenberg::g1::affine_element; + + std::array evaluations; + for (auto& eval : evaluations) { + eval = Fr::random_element(); + } + + // Add some junk to the transcript and compute challenges + uint32_t data = 25; + auto scalar = Fr::random_element(); + auto commitment = Commitment::one(); + auto univariate = Univariate(evaluations); + + // Instantiate a prover transcript and mock an example protocol + ProverTranscript prover_transcript; + + // round 0 + prover_transcript.send_to_verifier("data", data); + Fr alpha = prover_transcript.get_challenge("alpha"); + + // round 1 + prover_transcript.send_to_verifier("scalar", scalar); + prover_transcript.send_to_verifier("commitment", commitment); + Fr beta = prover_transcript.get_challenge("beta"); + + // round 2 + prover_transcript.send_to_verifier("univariate", univariate); + auto [gamma, delta] = prover_transcript.get_challenges("gamma", "delta"); + + // Instantiate a verifier transcript from the raw bytes of the prover transcript; receive data and generate + // challenges according to the example protocol + VerifierTranscript verifier_transcript(prover_transcript.proof_data); + + // round 0 + auto data_received = verifier_transcript.template receive_from_prover("data"); + Fr verifier_alpha = verifier_transcript.get_challenge("alpha"); + + // round 1 + auto scalar_received = verifier_transcript.template receive_from_prover("scalar"); + auto commitment_received = verifier_transcript.template receive_from_prover("commitment"); + Fr verifier_beta = verifier_transcript.get_challenge("beta"); + + // round 2 + auto univariate_received = verifier_transcript.template receive_from_prover("univariate"); + auto [verifier_gamma, verifier_delta] = verifier_transcript.get_challenges("gamma", "delta"); + + // Check the correctness of the elements received by the verifier + EXPECT_EQ(data_received, data); + EXPECT_EQ(scalar_received, scalar); + EXPECT_EQ(commitment_received, commitment); + EXPECT_EQ(univariate_received, univariate); + + // Check consistency of prover and verifier challenges + EXPECT_EQ(alpha, verifier_alpha); + EXPECT_EQ(beta, verifier_beta); + EXPECT_EQ(gamma, verifier_gamma); + EXPECT_EQ(delta, verifier_delta); + + // Check consistency of the generated manifests + EXPECT_EQ(prover_transcript.get_manifest(), verifier_transcript.get_manifest()); +} + +/** + * @brief Demonstrate that the verifier is not explicitly restricted from deviating from the 'manifest' + * + */ +TYPED_TEST(TranscriptTest, VerifierMistake) +{ + using Fr = barretenberg::fr; + + auto scalar_1 = Fr::random_element(); + auto scalar_2 = Fr::random_element(); + + ProverTranscript prover_transcript; + + prover_transcript.send_to_verifier("scalar1", scalar_1); + prover_transcript.send_to_verifier("scalar2", scalar_2); + auto prover_alpha = prover_transcript.get_challenge("alpha"); + + VerifierTranscript verifier_transcript(prover_transcript.proof_data); + + verifier_transcript.template receive_from_prover("scalar1"); + // accidentally skip receipt of "scalar2"... + // but then generate a challenge anyway + auto verifier_alpha = verifier_transcript.get_challenge("alpha"); + + // Challenges will not agree and neither will the manifests + EXPECT_NE(prover_alpha, verifier_alpha); + EXPECT_NE(prover_transcript.get_manifest(), verifier_transcript.get_manifest()); +} \ No newline at end of file From 0df6f441c86f63d1e4649324c16849f29ba37f76 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Fri, 17 Mar 2023 18:38:22 +0000 Subject: [PATCH 05/10] update verifier trascript to fix wasm build --- cpp/src/barretenberg/honk/transcript/transcript.hpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cpp/src/barretenberg/honk/transcript/transcript.hpp b/cpp/src/barretenberg/honk/transcript/transcript.hpp index 3d229fdb0c..95eb54e857 100644 --- a/cpp/src/barretenberg/honk/transcript/transcript.hpp +++ b/cpp/src/barretenberg/honk/transcript/transcript.hpp @@ -214,12 +214,11 @@ 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_; + size_t num_bytes_read_ = 0; 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) @@ -240,10 +239,10 @@ template class VerifierTranscript : public BaseTranscript { template T receive_from_prover(const std::string& label) { constexpr size_t element_size = sizeof(T); - ASSERT(read_iterator_ + element_size <= proof_data_.end()); + ASSERT(num_bytes_read_ + element_size <= proof_data_.size()); - std::span element_bytes{ read_iterator_, element_size }; - read_iterator_ += element_size; + auto element_bytes = std::span{ proof_data_ }.subspan(num_bytes_read_, element_size); + num_bytes_read_ += element_size; BaseTranscript::consume_prover_element_bytes(label, element_bytes); From 83418191d530cffa9489edfa1869b474bd21eb8c Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Fri, 17 Mar 2023 19:40:32 +0000 Subject: [PATCH 06/10] improvements to the manifest concept --- .../honk/transcript/transcript.hpp | 86 +++++++++---------- .../honk/transcript/transcript.test.cpp | 75 ++++++++-------- 2 files changed, 79 insertions(+), 82 deletions(-) diff --git a/cpp/src/barretenberg/honk/transcript/transcript.hpp b/cpp/src/barretenberg/honk/transcript/transcript.hpp index 95eb54e857..9bbfc3fcca 100644 --- a/cpp/src/barretenberg/honk/transcript/transcript.hpp +++ b/cpp/src/barretenberg/honk/transcript/transcript.hpp @@ -16,30 +16,40 @@ namespace honk { -// class TranscriptSummary { -// struct RoundData { -// std::vector challenge_label; -// std::vector> entries; -// }; - -// std::map manifest; - -// void print() { -// for (size_t i = 0; i < manifest.size(); ++i) { -// info("Round: ", i); -// info("challenge: ", manifest[i].challenge_label[0]); -// for (auto& entry : manifest[i].entries) { -// info("\t", entry.first); -// } -// } -// } -// }; - -struct RoundData { - std::vector challenge_label; - std::vector> entries; - - bool operator==(const RoundData& other) const = default; +class TranscriptManifest { + struct RoundData { + std::vector challenge_label; + std::vector> entries; + + bool operator==(const RoundData& other) const = default; + }; + + std::map manifest; + + public: + void print() + { + for (auto& round : manifest) { + info("Round: ", round.first); + for (auto& label : round.second.challenge_label) { + info("\tchallenge: ", label); + } + for (auto& entry : round.second.entries) { + info("\telement (", entry.second, "): ", entry.first); + } + } + } + + template void add_challenge(size_t round, Strings&... labels) + { + manifest[round].challenge_label = { labels... }; + } + void add_entry(size_t round, std::string element_label, size_t element_size) + { + manifest[round].entries.emplace_back(element_label, element_size); + } + + bool operator==(const TranscriptManifest& other) const = default; }; /** @@ -56,7 +66,9 @@ template class BaseTranscript { size_t round_number = 0; std::array previous_challenge_buffer{}; std::vector current_round_data; - std::map manifest; + + // "Manifest" object that records a summary of the transcript interactions + TranscriptManifest manifest; /** * @brief Compute c_next = H( Compress(c_prev || round_buffer) ) @@ -102,7 +114,8 @@ template class BaseTranscript { { (void)label; - manifest[round_number].entries.emplace_back(label, element_bytes.size()); + // Add an entry to the current round of the manifest + manifest.add_entry(round_number, label, element_bytes.size()); current_round_data.insert(current_round_data.end(), element_bytes.begin(), element_bytes.end()); } @@ -123,10 +136,8 @@ template class BaseTranscript { // 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, ...); - // std::array challenge_labels = labels...; - manifest[round_number].challenge_label = { labels... }; + // Add challenge labels for current round to the manifest + manifest.add_challenge(round_number, labels...); // Compute the new challenge buffer from which we derive the challenges. auto next_challenge_buffer = get_next_challenge_buffer(); @@ -156,20 +167,9 @@ template class BaseTranscript { FF get_challenge(const std::string& label) { return get_challenges(label)[0]; } - [[nodiscard]] std::map get_manifest() const { return manifest; }; + [[nodiscard]] TranscriptManifest get_manifest() const { return manifest; }; - void print() - { - for (auto& round : manifest) { - info("Round: ", round.first); - for (auto& label : round.second.challenge_label) { - info("\tchallenge: ", label); - } - for (auto& entry : round.second.entries) { - info("\telement (", entry.second, "): ", entry.first); - } - } - } + void print() { manifest.print(); } }; template class ProverTranscript : public BaseTranscript { diff --git a/cpp/src/barretenberg/honk/transcript/transcript.test.cpp b/cpp/src/barretenberg/honk/transcript/transcript.test.cpp index 7baf0a284a..db65578ad7 100644 --- a/cpp/src/barretenberg/honk/transcript/transcript.test.cpp +++ b/cpp/src/barretenberg/honk/transcript/transcript.test.cpp @@ -15,13 +15,13 @@ using namespace honk; template class TranscriptTest : public testing::Test { public: /** - * @brief Construct a manifest for a standard Honk proof with n = 8, (d = 3) + * @brief Construct a manifest for a standard Honk proof * - * @return std::map + * @return TranscriptManifest */ - std::map construct_standard_honk_manifest(size_t circuit_size) + TranscriptManifest construct_standard_honk_manifest(size_t circuit_size) { - std::map manifest_expected; + TranscriptManifest manifest_expected; size_t log_n(numeric::get_msb(circuit_size)); @@ -31,55 +31,52 @@ template class TranscriptTest : public testing::Test { size_t size_uni = max_relation_length * size_FF; size_t size_evals = StandardArithmetization::NUM_POLYNOMIALS * size_FF; - // clang-format off - size_t round = 0; - manifest_expected[round].entries = { {"circuit_size", 4}, - {"public_input_size", 4}, - {"public_input_0", size_FF}, - {"W_1", size_G}, - {"W_2", size_G}, - {"W_3", size_G} }; - manifest_expected[round].challenge_label = {"beta", "gamma"}; + manifest_expected.add_entry(round, "circuit_size", 4); + manifest_expected.add_entry(round, "public_input_size", 4); + manifest_expected.add_entry(round, "public_input_0", size_FF); + manifest_expected.add_entry(round, "W_1", size_G); + manifest_expected.add_entry(round, "W_2", size_G); + manifest_expected.add_entry(round, "W_3", size_G); + manifest_expected.add_challenge(round, "beta", "gamma"); round++; - manifest_expected[round].entries = { {"Z_PERM", size_G} }; - manifest_expected[round].challenge_label = {"Sumcheck:alpha", "Sumcheck:zeta"}; - + manifest_expected.add_entry(round, "Z_PERM", size_G); + manifest_expected.add_challenge(round, "Sumcheck:alpha", "Sumcheck:zeta"); + for (size_t i = 0; i < log_n; ++i) { round++; std::string idx = std::to_string(i); - manifest_expected[round].entries = { {"Sumcheck:univariate_" + idx, size_uni} }; - manifest_expected[round].challenge_label = {"Sumcheck:u_" + idx}; + manifest_expected.add_entry(round, "Sumcheck:univariate_" + idx, size_uni); + std::string label = "Sumcheck:u_" + idx; + manifest_expected.add_challenge(round, label); } round++; - manifest_expected[round].entries = { {"multivariate_evaluations", size_evals} }; - manifest_expected[round].challenge_label = {"rho"}; + manifest_expected.add_entry(round, "multivariate_evaluations", size_evals); + manifest_expected.add_challenge(round, "rho"); round++; for (size_t i = 1; i < log_n; ++i) { std::string idx = std::to_string(i); - manifest_expected[round].entries.emplace_back ("Gemini:FOLD_" + idx, size_G ); + manifest_expected.add_entry(round, "Gemini:FOLD_" + idx, size_G); } - manifest_expected[round].challenge_label = {"Gemini:r"}; + manifest_expected.add_challenge(round, "Gemini:r"); round++; for (size_t i = 0; i < log_n; ++i) { std::string idx = std::to_string(i); - manifest_expected[round].entries.emplace_back ("Gemini:a_" + idx, size_FF ); + manifest_expected.add_entry(round, "Gemini:a_" + idx, size_FF); } - manifest_expected[round].challenge_label = {"Shplonk:nu"}; + manifest_expected.add_challenge(round, "Shplonk:nu"); round++; - manifest_expected[round].entries = { {"Shplonk:Q", size_G} }; - manifest_expected[round].challenge_label = {"Shplonk:z"}; - - round++; - manifest_expected[round].entries = { {"KZG:W", size_G} }; - manifest_expected[round].challenge_label = {}; // no challenge + manifest_expected.add_entry(round, "Shplonk:Q", size_G); + manifest_expected.add_challenge(round, "Shplonk:z"); - // clang-format on + round++; + manifest_expected.add_entry(round, "KZG:W", size_G); + manifest_expected.add_challenge(round); // no challenge return manifest_expected; } @@ -100,18 +97,18 @@ TYPED_TEST(TranscriptTest, StandardHonkManifest) composer.circuit_constructor.add_variable(a); composer.circuit_constructor.add_public_variable(a); + // Automatically generate a transcript manifest by constructing a proof auto prover = composer.create_prover(); plonk::proof proof = prover.construct_proof(); - auto verifier = composer.create_verifier(); - bool verified = verifier.verify_proof(proof); - ASSERT_TRUE(verified); + // Check that the prover generated manifest agrees with the expectation auto manifest_expected = TestFixture::construct_standard_honk_manifest(prover.key->circuit_size); - - // Uncomment to see a summary printout of the transcript - // prover.transcript.print(); - ASSERT_EQ(prover.transcript.get_manifest(), manifest_expected); + + // If the proof verifies, the verifier manifest must have matched that of the prover + auto verifier = composer.create_verifier(); + bool verified = verifier.verify_proof(proof); + ASSERT_TRUE(verified); } /** @@ -187,7 +184,7 @@ TYPED_TEST(TranscriptTest, ProverAndVerifierBasic) } /** - * @brief Demonstrate that the verifier is not explicitly restricted from deviating from the 'manifest' + * @brief Demonstrate extent to which verifier transcript is flexible / constrained * */ TYPED_TEST(TranscriptTest, VerifierMistake) From fa7cb558ed5de0de02b98d435dbaba0eb2e9ac6f Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Fri, 17 Mar 2023 20:09:17 +0000 Subject: [PATCH 07/10] comment updates --- cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp | 2 ++ cpp/src/barretenberg/honk/transcript/transcript.hpp | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp index 6330c87d2c..5cc7bede83 100644 --- a/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp @@ -134,6 +134,8 @@ template class... Relations> cl * @brief Extract round univariate, check sum, generate challenge, compute next target sum..., repeat until final * round, then use purported evaluations to generate purported full Honk relation value and check against final * target sum. + * + * @details If verification fails, returns std::nullopt, otherwise returns SumcheckOutput */ std::optional> execute_verifier(const RelationParameters& relation_parameters) { diff --git a/cpp/src/barretenberg/honk/transcript/transcript.hpp b/cpp/src/barretenberg/honk/transcript/transcript.hpp index 9bbfc3fcca..f2e3815566 100644 --- a/cpp/src/barretenberg/honk/transcript/transcript.hpp +++ b/cpp/src/barretenberg/honk/transcript/transcript.hpp @@ -201,6 +201,12 @@ template class ProverTranscript : public BaseTranscript { BaseTranscript::consume_prover_element_bytes(label, element_bytes); } + /** + * @brief For testing: initializes transcript with some arbitrary data so that a challenge can be generated after + * initialization + * + * @return ProverTranscript + */ static ProverTranscript init_empty() { ProverTranscript transcript; @@ -221,6 +227,13 @@ template class VerifierTranscript : public BaseTranscript { : proof_data_(proof_data.begin(), proof_data.end()) {} + /** + * @brief For testing: initializes transcript based on proof data then receives junk data produced by + * ProverTranscript::init_empty() + * + * @param transcript + * @return VerifierTranscript + */ static VerifierTranscript init_empty(const ProverTranscript& transcript) { VerifierTranscript verifier_transcript{ transcript.proof_data }; From 3539c4f6fe8eecd6a1279f17b522fd02ec66da95 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Tue, 21 Mar 2023 16:14:19 +0000 Subject: [PATCH 08/10] addressing PR review comments from Cody --- .../barretenberg/honk/pcs/gemini/gemini.hpp | 2 +- .../barretenberg/honk/proof_system/prover.cpp | 2 +- .../honk/proof_system/verifier.cpp | 5 +- .../honk/proof_system/verifier.hpp | 1 + .../barretenberg/honk/sumcheck/sumcheck.hpp | 2 +- .../honk/sumcheck/sumcheck_output.hpp | 4 +- .../honk/transcript/transcript.hpp | 18 ++++-- .../honk/transcript/transcript.test.cpp | 60 +++++++++++++++---- 8 files changed, 71 insertions(+), 23 deletions(-) diff --git a/cpp/src/barretenberg/honk/pcs/gemini/gemini.hpp b/cpp/src/barretenberg/honk/pcs/gemini/gemini.hpp index cfd49fbae9..9e3924a947 100644 --- a/cpp/src/barretenberg/honk/pcs/gemini/gemini.hpp +++ b/cpp/src/barretenberg/honk/pcs/gemini/gemini.hpp @@ -279,7 +279,7 @@ template class MultilinearReductionScheme { 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)); + auto eval = transcript.template receive_from_prover("Gemini:a_" + std::to_string(i)); evaluations.emplace_back(eval); } diff --git a/cpp/src/barretenberg/honk/proof_system/prover.cpp b/cpp/src/barretenberg/honk/proof_system/prover.cpp index 4f2501e2d1..7f13772c11 100644 --- a/cpp/src/barretenberg/honk/proof_system/prover.cpp +++ b/cpp/src/barretenberg/honk/proof_system/prover.cpp @@ -305,7 +305,7 @@ template void Prover::execute_univariatization_rou // Compute d+1 Fold polynomials and their evaluations gemini_output = Gemini::reduce_prove(commitment_key, - sumcheck_output.multivariate_query, + sumcheck_output.challenge_point, std::move(batched_poly_unshifted), std::move(batched_poly_to_be_shifted), transcript); diff --git a/cpp/src/barretenberg/honk/proof_system/verifier.cpp b/cpp/src/barretenberg/honk/proof_system/verifier.cpp index cf528dec4f..a555c687de 100644 --- a/cpp/src/barretenberg/honk/proof_system/verifier.cpp +++ b/cpp/src/barretenberg/honk/proof_system/verifier.cpp @@ -2,6 +2,7 @@ #include "barretenberg/common/throw_or_abort.hpp" #include #include +#include "barretenberg/honk/transcript/transcript.hpp" #include "barretenberg/plonk/proof_system/constants.hpp" #include "./verifier.hpp" #include "barretenberg/plonk/proof_system/public_inputs/public_inputs.hpp" @@ -88,7 +89,7 @@ template bool Verifier::verify_pro constexpr auto program_width = program_settings::program_width; - VerifierTranscript transcript{ proof.proof_data }; + transcript = VerifierTranscript{ proof.proof_data }; // TODO(Adrian): Change the initialization of the transcript to take the VK hash? const auto circuit_size = transcript.template receive_from_prover("circuit_size"); @@ -103,7 +104,7 @@ template bool Verifier::verify_pro 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)); + auto public_input_i = transcript.template receive_from_prover("public_input_" + std::to_string(i)); public_inputs.emplace_back(public_input_i); } diff --git a/cpp/src/barretenberg/honk/proof_system/verifier.hpp b/cpp/src/barretenberg/honk/proof_system/verifier.hpp index 8a1614dbd6..217dffb5b6 100644 --- a/cpp/src/barretenberg/honk/proof_system/verifier.hpp +++ b/cpp/src/barretenberg/honk/proof_system/verifier.hpp @@ -28,6 +28,7 @@ template class Verifier { std::map kate_g1_elements; std::map kate_fr_elements; std::shared_ptr kate_verification_key; + VerifierTranscript transcript; }; extern template class Verifier; diff --git a/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp index 5cc7bede83..0ab709c525 100644 --- a/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp @@ -125,7 +125,7 @@ template class... Relations> cl for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { multivariate_evaluations[i] = folded_polynomials[i][0]; } - transcript.send_to_verifier("multivariate_evaluations", multivariate_evaluations); + transcript.send_to_verifier("Sumcheck:evaluations", multivariate_evaluations); return { multivariate_query, multivariate_evaluations }; }; diff --git a/cpp/src/barretenberg/honk/sumcheck/sumcheck_output.hpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck_output.hpp index b25a2e4a34..b10b5b4ac8 100644 --- a/cpp/src/barretenberg/honk/sumcheck/sumcheck_output.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck_output.hpp @@ -5,12 +5,12 @@ namespace honk::sumcheck { /** - * @brief Contains the multi-linear `evaluations` of the polynomials at the `evaluation_point`. + * @brief Contains the multi-linear evaluations of the polynomials at the challenge point 'u'. * These are computed by the prover and need to be checked using a multi-linear PCS like Gemini. */ template struct SumcheckOutput { // u = (u_0, ..., u_{d-1}) - std::vector multivariate_query; + std::vector challenge_point; // Evaluations in `u` of the polynomials used in Sumcheck std::array evaluations; diff --git a/cpp/src/barretenberg/honk/transcript/transcript.hpp b/cpp/src/barretenberg/honk/transcript/transcript.hpp index f2e3815566..e6b1417bde 100644 --- a/cpp/src/barretenberg/honk/transcript/transcript.hpp +++ b/cpp/src/barretenberg/honk/transcript/transcript.hpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace honk { @@ -49,6 +50,10 @@ class TranscriptManifest { manifest[round].entries.emplace_back(element_label, element_size); } + [[nodiscard]] size_t size() const { return manifest.size(); } + + RoundData operator[](const size_t& round) { return manifest[round]; }; + bool operator==(const TranscriptManifest& other) const = default; }; @@ -64,7 +69,7 @@ template class BaseTranscript { static constexpr size_t MIN_BYTES_PER_CHALLENGE = 128 / 8; // 128 bit challenges size_t round_number = 0; - std::array previous_challenge_buffer{}; + std::array previous_challenge_buffer{}; // default-initialized to zeros std::vector current_round_data; // "Manifest" object that records a summary of the transcript interactions @@ -193,8 +198,9 @@ template class ProverTranscript : public BaseTranscript { 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. + // TODO(Adrian): Ensure that serialization of affine elements (including point at infinity) is consistent. + // TODO(Adrian): Consider restricting serialization (via concepts) to types T for which sizeof(T) reliably + // returns the size of T in bytes. (E.g. this is true for std::array but not for std::vector). auto element_bytes = to_buffer(element); proof_data.insert(proof_data.end(), element_bytes.begin(), element_bytes.end()); @@ -219,10 +225,12 @@ template class ProverTranscript : public BaseTranscript { template class VerifierTranscript : public BaseTranscript { /// Contains the raw data sent by the prover. - const std::vector proof_data_; + std::vector proof_data_; size_t num_bytes_read_ = 0; public: + VerifierTranscript() = default; + explicit VerifierTranscript(const std::vector& proof_data) : proof_data_(proof_data.begin(), proof_data.end()) {} @@ -244,8 +252,6 @@ template class VerifierTranscript : public BaseTranscript { /** * @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 */ diff --git a/cpp/src/barretenberg/honk/transcript/transcript.test.cpp b/cpp/src/barretenberg/honk/transcript/transcript.test.cpp index db65578ad7..bbd487cf0a 100644 --- a/cpp/src/barretenberg/honk/transcript/transcript.test.cpp +++ b/cpp/src/barretenberg/honk/transcript/transcript.test.cpp @@ -17,6 +17,10 @@ template class TranscriptTest : public testing::Test { /** * @brief Construct a manifest for a standard Honk proof * + * @details This is where we define the "Manifest" for a Standard Honk proof. The tests in this suite are intented + * to warn the developer if the Prover/Verifier has deviated from this manifest, however, the Transcript class is + * not otherwise contrained to follow the manifest. + * * @return TranscriptManifest */ TranscriptManifest construct_standard_honk_manifest(size_t circuit_size) @@ -53,7 +57,7 @@ template class TranscriptTest : public testing::Test { } round++; - manifest_expected.add_entry(round, "multivariate_evaluations", size_evals); + manifest_expected.add_entry(round, "Sumcheck:evaluations", size_evals); manifest_expected.add_challenge(round, "rho"); round++; @@ -86,12 +90,13 @@ using FieldTypes = testing::Types; TYPED_TEST_SUITE(TranscriptTest, FieldTypes); /** - * @brief Ensure consistency between the manifests generated by the standard honk prover and verfier for a simple - * circuit of size n = 8 + * @brief Ensure consistency between the manifest hard coded in this testing suite and the one generated by the + * standard honk prover over the course of proof construction. * */ -TYPED_TEST(TranscriptTest, StandardHonkManifest) +TYPED_TEST(TranscriptTest, ProverManifestConsistency) { + // Construct a simple circuit of size n = 8 (i.e. the minimum circuit size) auto composer = StandardHonkComposer(); fr a = 1; composer.circuit_constructor.add_variable(a); @@ -101,14 +106,49 @@ TYPED_TEST(TranscriptTest, StandardHonkManifest) auto prover = composer.create_prover(); plonk::proof proof = prover.construct_proof(); - // Check that the prover generated manifest agrees with the expectation + // Check that the prover generated manifest agrees with the manifest hard coded in this suite auto manifest_expected = TestFixture::construct_standard_honk_manifest(prover.key->circuit_size); - ASSERT_EQ(prover.transcript.get_manifest(), manifest_expected); + auto prover_manifest = prover.transcript.get_manifest(); - // If the proof verifies, the verifier manifest must have matched that of the prover + // Note: a manifest can be printed using manifest.print() + for (size_t round = 0; round < manifest_expected.size(); ++round) { + ASSERT_EQ(prover_manifest[round], manifest_expected[round]) << "Prover manifest discrepency in round " << round; + ; + } +} + +/** + * @brief Ensure consistency between the manifest generated by the standard honk prover over the course of proof + * construction and the one generated by the verifier over the course of proof verification. + * + */ +TYPED_TEST(TranscriptTest, VerifierManifestConsistency) +{ + // Construct a simple circuit of size n = 8 (i.e. the minimum circuit size) + auto composer = StandardHonkComposer(); + fr a = 1; + composer.circuit_constructor.add_variable(a); + composer.circuit_constructor.add_public_variable(a); + + // Automatically generate a transcript manifest in the prover by constructing a proof + auto prover = composer.create_prover(); + plonk::proof proof = prover.construct_proof(); + + // Automatically generate a transcript manifest in the verifier by verifying a proof auto verifier = composer.create_verifier(); - bool verified = verifier.verify_proof(proof); - ASSERT_TRUE(verified); + verifier.verify_proof(proof); + prover.transcript.print(); + verifier.transcript.print(); + + // Check consistency between the manifests generated by the prover and verifier + auto prover_manifest = prover.transcript.get_manifest(); + auto verifier_manifest = verifier.transcript.get_manifest(); + + // Note: a manifest can be printed using manifest.print() + for (size_t round = 0; round < prover_manifest.size(); ++round) { + ASSERT_EQ(prover_manifest[round], verifier_manifest[round]) + << "Prover/Verifier manifest discrepency in round " << round; + } } /** @@ -207,7 +247,7 @@ TYPED_TEST(TranscriptTest, VerifierMistake) // but then generate a challenge anyway auto verifier_alpha = verifier_transcript.get_challenge("alpha"); - // Challenges will not agree and neither will the manifests + // Challenges will not agree but neither will the manifests EXPECT_NE(prover_alpha, verifier_alpha); EXPECT_NE(prover_transcript.get_manifest(), verifier_transcript.get_manifest()); } \ No newline at end of file From 662fa6dd24bdaf9d7ada0a6a0b05999dd0e7c2ec Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Tue, 21 Mar 2023 18:15:22 +0000 Subject: [PATCH 09/10] change query to challenge in a few places --- .../barretenberg/honk/sumcheck/sumcheck.hpp | 18 +++++++++--------- .../honk/sumcheck/sumcheck.test.cpp | 14 +++++++------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp index 0ab709c525..0dad08391d 100644 --- a/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp @@ -94,15 +94,15 @@ template class... Relations> cl PowUnivariate pow_univariate(zeta); - std::vector multivariate_query; - multivariate_query.reserve(multivariate_d); + std::vector multivariate_challenge; + multivariate_challenge.reserve(multivariate_d); // First round // This populates folded_polynomials. auto round_univariate = round.compute_univariate(full_polynomials, relation_parameters, pow_univariate, alpha); transcript.send_to_verifier("Sumcheck:univariate_0", round_univariate); FF round_challenge = transcript.get_challenge("Sumcheck:u_0"); - multivariate_query.emplace_back(round_challenge); + multivariate_challenge.emplace_back(round_challenge); fold(full_polynomials, multivariate_n, round_challenge); pow_univariate.partially_evaluate(round_challenge); round.round_size = round.round_size >> 1; // TODO(#224)(Cody): Maybe fold should do this and release memory? @@ -114,7 +114,7 @@ template class... Relations> cl round_univariate = round.compute_univariate(folded_polynomials, relation_parameters, pow_univariate, alpha); transcript.send_to_verifier("Sumcheck:univariate_" + std::to_string(round_idx), round_univariate); FF round_challenge = transcript.get_challenge("Sumcheck:u_" + std::to_string(round_idx)); - multivariate_query.emplace_back(round_challenge); + multivariate_challenge.emplace_back(round_challenge); fold(folded_polynomials, round.round_size, round_challenge); pow_univariate.partially_evaluate(round_challenge); round.round_size = round.round_size >> 1; @@ -127,7 +127,7 @@ template class... Relations> cl } transcript.send_to_verifier("Sumcheck:evaluations", multivariate_evaluations); - return { multivariate_query, multivariate_evaluations }; + return { multivariate_challenge, multivariate_evaluations }; }; /** @@ -151,8 +151,8 @@ template class... Relations> cl throw_or_abort("Number of variables in multivariate is 0."); } - std::vector multivariate_query; - multivariate_query.reserve(multivariate_d); + std::vector multivariate_challenge; + multivariate_challenge.reserve(multivariate_d); for (size_t round_idx = 0; round_idx < multivariate_d; round_idx++) { // Obtain the round univariate from the transcript @@ -163,7 +163,7 @@ template class... Relations> cl bool checked = round.check_sum(round_univariate, pow_univariate); verified = verified && checked; FF round_challenge = transcript.get_challenge("Sumcheck:u_" + std::to_string(round_idx)); - multivariate_query.emplace_back(round_challenge); + multivariate_challenge.emplace_back(round_challenge); round.compute_next_target_sum(round_univariate, round_challenge, pow_univariate); pow_univariate.partially_evaluate(round_challenge); @@ -184,7 +184,7 @@ template class... Relations> cl return std::nullopt; } - return SumcheckOutput{ multivariate_query, purported_evaluations }; + return SumcheckOutput{ multivariate_challenge, purported_evaluations }; }; // TODO(#224)(Cody): Rename. fold is not descriptive, and it's already in use in the Gemini context. diff --git a/cpp/src/barretenberg/honk/sumcheck/sumcheck.test.cpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck.test.cpp index 3d8cd4b23e..b36dc8d504 100644 --- a/cpp/src/barretenberg/honk/sumcheck/sumcheck.test.cpp +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck.test.cpp @@ -148,11 +148,11 @@ TEST(Sumcheck, PolynomialNormalization) GrandProductComputationRelation, GrandProductInitializationRelation>(multivariate_n, transcript); - auto [multivariate_query, evaluations] = sumcheck.execute_prover(full_polynomials, {}); + auto [multivariate_challenge, evaluations] = sumcheck.execute_prover(full_polynomials, {}); - FF u_0 = multivariate_query[0]; - FF u_1 = multivariate_query[1]; - FF u_2 = multivariate_query[2]; + FF u_0 = multivariate_challenge[0]; + FF u_1 = multivariate_challenge[1]; + FF u_2 = multivariate_challenge[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 @@ -248,9 +248,9 @@ TEST(Sumcheck, Prover) GrandProductComputationRelation, GrandProductInitializationRelation>(multivariate_n, transcript); - auto [multivariate_query, evaluations] = sumcheck.execute_prover(full_polynomials, {}); - FF u_0 = multivariate_query[0]; - FF u_1 = multivariate_query[1]; + auto [multivariate_challenge, evaluations] = sumcheck.execute_prover(full_polynomials, {}); + FF u_0 = multivariate_challenge[0]; + FF u_1 = multivariate_challenge[1]; std::vector expected_values; for (auto& polynomial : full_polynomials) { // using knowledge of inputs here to derive the evaluation From 91c9b661bbf8b50aad4a92ac742a5968edd71791 Mon Sep 17 00:00:00 2001 From: codygunton Date: Fri, 24 Mar 2023 21:03:45 +0000 Subject: [PATCH 10/10] Added note on pre-hashing. --- cpp/src/barretenberg/honk/transcript/transcript.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cpp/src/barretenberg/honk/transcript/transcript.hpp b/cpp/src/barretenberg/honk/transcript/transcript.hpp index e6b1417bde..f4846d454c 100644 --- a/cpp/src/barretenberg/honk/transcript/transcript.hpp +++ b/cpp/src/barretenberg/honk/transcript/transcript.hpp @@ -95,8 +95,10 @@ template class BaseTranscript { } 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. + // 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. + // Note: this pre-hashing is an efficiency trick that may be discareded if using a SNARK-friendly or in contexts + // (eg smart contract verification) where the cost of elliptic curve operations is high. std::vector compressed_buffer = to_buffer(crypto::pedersen::compress_native(full_buffer)); // Use a strong hash function to derive the new challenge_buffer.