From f3571c6753ae0ed818f8d4ecb15c9ac6f0f8ac10 Mon Sep 17 00:00:00 2001 From: maramihali Date: Tue, 14 May 2024 20:54:00 +0100 Subject: [PATCH 01/40] exploooring --- .vscode/settings.json | 2 +- .../cpp/src/barretenberg/eccvm/eccvm_circuit_builder.hpp | 3 ++- barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp | 7 +------ barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp | 6 ++---- .../cpp/src/barretenberg/vm/generated/avm_flavor.hpp | 2 -- .../cpp/src/barretenberg/vm/generated/spike_flavor.hpp | 2 -- 6 files changed, 6 insertions(+), 16 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index ebb2f31ee3a..f4f60cd5b9d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -166,5 +166,5 @@ "**/l1-contracts/lib/**": true, "**/barretenberg/cpp/build*/**": true }, - "cmake.sourceDirectory": "/mnt/user-data/adam/aztec-packages/barretenberg/acir_tests/headless-test/node_modules/bare-fs" + "cmake.sourceDirectory": "/workspaces/aztec-packages/barretenberg/cpp" } diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_circuit_builder.hpp index 7f49af86030..dc490f897dc 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_circuit_builder.hpp @@ -212,9 +212,10 @@ class ECCVMCircuitBuilder { return result; } + // why are this nodiscard?? [[nodiscard]] size_t get_num_gates() const { - // (issue #2218) + // (issue #2218): ? return op_queue->get_num_rows(); } diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp index 759353edb0a..04140dfe9cc 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp @@ -16,7 +16,7 @@ #include "barretenberg/relations/ecc_vm/ecc_wnaf_relation.hpp" #include "barretenberg/relations/relation_parameters.hpp" -// NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members) +// NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members) ? namespace bb { @@ -298,11 +298,6 @@ class ECCVMFlavor { }; public: - /** - * @brief A container for polynomials produced after the first round of sumcheck. - * @todo TODO(#394) Use polynomial classes for guaranteed memory alignment. - */ - using FoldedPolynomials = AllEntities>; /** * @brief A field element for each entity of the flavor. These entities represent the prover polynomials diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp index 6b9e01b7069..20c6b720533 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp @@ -17,10 +17,7 @@ bool ECCVMVerifier::verify_proof(const HonkProof& proof) CommitmentLabels commitment_labels; const auto circuit_size = transcript->template receive_from_prover("circuit_size"); - - if (circuit_size != key->circuit_size) { - return false; - } + ASSERT(circuit_size == key->circuit_size); // Utility for extracting commitments from transcript const auto receive_commitment = [&](const std::string& label) { @@ -115,6 +112,7 @@ bool ECCVMVerifier::verify_proof(const HonkProof& proof) gamma * (gamma + beta_sqr) * (gamma + beta_sqr + beta_sqr) * (gamma + beta_sqr + beta_sqr + beta_sqr); relation_parameters.eccvm_set_permutation_delta = relation_parameters.eccvm_set_permutation_delta.invert(); + // these are the derived stuff only // Get commitment to permutation and lookup grand products commitments.lookup_inverses = receive_commitment(commitment_labels.lookup_inverses); commitments.z_perm = receive_commitment(commitment_labels.z_perm); diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/avm_flavor.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/avm_flavor.hpp index bf208bc727e..55c27f13671 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/avm_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/avm_flavor.hpp @@ -2048,8 +2048,6 @@ class AvmFlavor { using VerificationKey = VerificationKey_, VerifierCommitmentKey>; - using FoldedPolynomials = AllEntities>; - class AllValues : public AllEntities { public: using Base = AllEntities; diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/spike_flavor.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/spike_flavor.hpp index 14e81c04dda..e1725c5b93a 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/spike_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/spike_flavor.hpp @@ -102,8 +102,6 @@ class SpikeFlavor { using VerificationKey = VerificationKey_, VerifierCommitmentKey>; - using FoldedPolynomials = AllEntities>; - class AllValues : public AllEntities { public: using Base = AllEntities; From 927b78b2b6def848e5718b2df9e6a9695396b354 Mon Sep 17 00:00:00 2001 From: maramihali Date: Tue, 14 May 2024 21:27:32 +0100 Subject: [PATCH 02/40] start doing recursive work --- .../src/barretenberg/eccvm/eccvm_flavor.hpp | 10 +- .../src/barretenberg/eccvm/eccvm_verifier.cpp | 97 ++---------- .../src/barretenberg/eccvm/eccvm_verifier.hpp | 1 - .../eccvm_recursive_flavor.hpp | 147 ++++++++++++++++++ .../eccvm_recursive_verifier.cpp | 123 +++++++++++++++ .../eccvm_recursive_verifier.hpp | 31 ++++ .../goblin_translator_flavor.hpp | 6 - .../barretenberg/vm/generated/avm_flavor.hpp | 2 - .../vm/generated/spike_flavor.hpp | 2 - 9 files changed, 316 insertions(+), 103 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp index 04140dfe9cc..916ed0c8c08 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp @@ -72,7 +72,9 @@ class ECCVMFlavor { using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); - private: +// WORKTODO make the specifiers good + + public: /** * @brief A base class labelling precomputed entities and (ordered) subsets of interest. * @details Used to build the proving key and verification key. @@ -309,12 +311,6 @@ class ECCVMFlavor { using Base::Base; }; - /** - * @brief A container for polynomials produced after the first round of sumcheck. - * @todo TODO(#394) Use polynomial classes for guaranteed memory alignment. - */ - using RowPolynomials = AllEntities; - /** * @brief A container for storing the partially evaluated multivariates produced by sumcheck. */ diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp index 20c6b720533..76a4cbdc7ac 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp @@ -1,4 +1,4 @@ -#include "./eccvm_verifier.hpp" +#include "./eccvm_recursive_verifier.hpp" #include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" @@ -12,101 +12,28 @@ bool ECCVMVerifier::verify_proof(const HonkProof& proof) using ZeroMorph = ZeroMorphVerifier_; RelationParameters relation_parameters; - transcript = std::make_shared(proof); + + StdlibProof stdlib_proof = bb::convert_proof_to_witness(builder, proof); + transcript = std::make_shared(stdlib_proof); + + VerifierCommitments commitments{ key }; CommitmentLabels commitment_labels; const auto circuit_size = transcript->template receive_from_prover("circuit_size"); ASSERT(circuit_size == key->circuit_size); - // Utility for extracting commitments from transcript - const auto receive_commitment = [&](const std::string& label) { - return transcript->template receive_from_prover(label); - }; - - // Get commitments to VM wires - commitments.transcript_add = receive_commitment(commitment_labels.transcript_add); - commitments.transcript_mul = receive_commitment(commitment_labels.transcript_mul); - commitments.transcript_eq = receive_commitment(commitment_labels.transcript_eq); - commitments.transcript_collision_check = receive_commitment(commitment_labels.transcript_collision_check); - commitments.transcript_msm_transition = receive_commitment(commitment_labels.transcript_msm_transition); - commitments.transcript_pc = receive_commitment(commitment_labels.transcript_pc); - commitments.transcript_msm_count = receive_commitment(commitment_labels.transcript_msm_count); - commitments.transcript_Px = receive_commitment(commitment_labels.transcript_Px); - commitments.transcript_Py = receive_commitment(commitment_labels.transcript_Py); - commitments.transcript_z1 = receive_commitment(commitment_labels.transcript_z1); - commitments.transcript_z2 = receive_commitment(commitment_labels.transcript_z2); - commitments.transcript_z1zero = receive_commitment(commitment_labels.transcript_z1zero); - commitments.transcript_z2zero = receive_commitment(commitment_labels.transcript_z2zero); - commitments.transcript_op = receive_commitment(commitment_labels.transcript_op); - commitments.transcript_accumulator_x = receive_commitment(commitment_labels.transcript_accumulator_x); - commitments.transcript_accumulator_y = receive_commitment(commitment_labels.transcript_accumulator_y); - commitments.transcript_msm_x = receive_commitment(commitment_labels.transcript_msm_x); - commitments.transcript_msm_y = receive_commitment(commitment_labels.transcript_msm_y); - commitments.precompute_pc = receive_commitment(commitment_labels.precompute_pc); - commitments.precompute_point_transition = receive_commitment(commitment_labels.precompute_point_transition); - commitments.precompute_round = receive_commitment(commitment_labels.precompute_round); - commitments.precompute_scalar_sum = receive_commitment(commitment_labels.precompute_scalar_sum); - commitments.precompute_s1hi = receive_commitment(commitment_labels.precompute_s1hi); - commitments.precompute_s1lo = receive_commitment(commitment_labels.precompute_s1lo); - commitments.precompute_s2hi = receive_commitment(commitment_labels.precompute_s2hi); - commitments.precompute_s2lo = receive_commitment(commitment_labels.precompute_s2lo); - commitments.precompute_s3hi = receive_commitment(commitment_labels.precompute_s3hi); - commitments.precompute_s3lo = receive_commitment(commitment_labels.precompute_s3lo); - commitments.precompute_s4hi = receive_commitment(commitment_labels.precompute_s4hi); - commitments.precompute_s4lo = receive_commitment(commitment_labels.precompute_s4lo); - commitments.precompute_skew = receive_commitment(commitment_labels.precompute_skew); - commitments.precompute_dx = receive_commitment(commitment_labels.precompute_dx); - commitments.precompute_dy = receive_commitment(commitment_labels.precompute_dy); - commitments.precompute_tx = receive_commitment(commitment_labels.precompute_tx); - commitments.precompute_ty = receive_commitment(commitment_labels.precompute_ty); - commitments.msm_transition = receive_commitment(commitment_labels.msm_transition); - commitments.msm_add = receive_commitment(commitment_labels.msm_add); - commitments.msm_double = receive_commitment(commitment_labels.msm_double); - commitments.msm_skew = receive_commitment(commitment_labels.msm_skew); - commitments.msm_accumulator_x = receive_commitment(commitment_labels.msm_accumulator_x); - commitments.msm_accumulator_y = receive_commitment(commitment_labels.msm_accumulator_y); - commitments.msm_pc = receive_commitment(commitment_labels.msm_pc); - commitments.msm_size_of_msm = receive_commitment(commitment_labels.msm_size_of_msm); - commitments.msm_count = receive_commitment(commitment_labels.msm_count); - commitments.msm_round = receive_commitment(commitment_labels.msm_round); - commitments.msm_add1 = receive_commitment(commitment_labels.msm_add1); - commitments.msm_add2 = receive_commitment(commitment_labels.msm_add2); - commitments.msm_add3 = receive_commitment(commitment_labels.msm_add3); - commitments.msm_add4 = receive_commitment(commitment_labels.msm_add4); - commitments.msm_x1 = receive_commitment(commitment_labels.msm_x1); - commitments.msm_y1 = receive_commitment(commitment_labels.msm_y1); - commitments.msm_x2 = receive_commitment(commitment_labels.msm_x2); - commitments.msm_y2 = receive_commitment(commitment_labels.msm_y2); - commitments.msm_x3 = receive_commitment(commitment_labels.msm_x3); - commitments.msm_y3 = receive_commitment(commitment_labels.msm_y3); - commitments.msm_x4 = receive_commitment(commitment_labels.msm_x4); - commitments.msm_y4 = receive_commitment(commitment_labels.msm_y4); - commitments.msm_collision_x1 = receive_commitment(commitment_labels.msm_collision_x1); - commitments.msm_collision_x2 = receive_commitment(commitment_labels.msm_collision_x2); - commitments.msm_collision_x3 = receive_commitment(commitment_labels.msm_collision_x3); - commitments.msm_collision_x4 = receive_commitment(commitment_labels.msm_collision_x4); - commitments.msm_lambda1 = receive_commitment(commitment_labels.msm_lambda1); - commitments.msm_lambda2 = receive_commitment(commitment_labels.msm_lambda2); - commitments.msm_lambda3 = receive_commitment(commitment_labels.msm_lambda3); - commitments.msm_lambda4 = receive_commitment(commitment_labels.msm_lambda4); - commitments.msm_slice1 = receive_commitment(commitment_labels.msm_slice1); - commitments.msm_slice2 = receive_commitment(commitment_labels.msm_slice2); - commitments.msm_slice3 = receive_commitment(commitment_labels.msm_slice3); - commitments.msm_slice4 = receive_commitment(commitment_labels.msm_slice4); - commitments.transcript_accumulator_empty = receive_commitment(commitment_labels.transcript_accumulator_empty); - commitments.transcript_reset_accumulator = receive_commitment(commitment_labels.transcript_reset_accumulator); - commitments.precompute_select = receive_commitment(commitment_labels.precompute_select); - commitments.lookup_read_counts_0 = receive_commitment(commitment_labels.lookup_read_counts_0); - commitments.lookup_read_counts_1 = receive_commitment(commitment_labels.lookup_read_counts_1); - + for(auto [comm, label]: zip_view(commitments.get_wires(), commitment_labels.get_wires())) { + comm = transcript->template receive_from_prover(label); + } + // Get challenge for sorted list batching and wire four memory records auto [beta, gamma] = transcript->template get_challenges("beta", "gamma"); + // there is an issue somewhere to simplify this :-? relation_parameters.gamma = gamma; - auto beta_sqr = beta * beta; relation_parameters.beta = beta; - relation_parameters.beta_sqr = beta_sqr; + relation_parameters.beta_sqr = beta * beta; relation_parameters.beta_cube = beta_sqr * beta; relation_parameters.eccvm_set_permutation_delta = gamma * (gamma + beta_sqr) * (gamma + beta_sqr + beta_sqr) * (gamma + beta_sqr + beta_sqr + beta_sqr); diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.hpp index 5ef29beff58..10e49d08a76 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.hpp @@ -26,7 +26,6 @@ class ECCVMVerifier { std::shared_ptr key; std::map commitments; - std::map pcs_fr_elements; std::shared_ptr transcript; }; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp new file mode 100644 index 00000000000..9169da1f481 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp @@ -0,0 +1,147 @@ +#pragma once +#include "barretenberg/commitment_schemes/ipa/ipa.hpp" +#include "barretenberg/common/std_array.hpp" +#include "barretenberg/ecc/curves/bn254/bn254.hpp" +#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" +#include "barretenberg/eccvm/eccvm_circuit_builder.hpp" +#include "barretenberg/flavor/flavor.hpp" +#include "barretenberg/flavor/flavor_macros.hpp" +#include "barretenberg/flavor/relation_definitions.hpp" +#include "barretenberg/polynomials/univariate.hpp" +#include "barretenberg/relations/ecc_vm/ecc_lookup_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_msm_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_point_table_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_set_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_transcript_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_wnaf_relation.hpp" +#include "barretenberg/relations/relation_parameters.hpp" + +// NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members) ? + +namespace bb { + +template class ECCVMRecursiveFlavor_ { + public: + using CircuitBuilder = BuilderType; // determines the arithmetisation of recursive verifier + + // I need to somehow make this bn254 hashtag confusion + // the native stuff becomes nonnative and the nonnative stuff becomes native and everything is in circuit land is probably what's going on + using CycleGroup = bb::g1; + using Curve = curve::Grumpkin; + using G1 = typename Curve::Group; + using PCS = IPA; + using FF = typename G1::subgroup_field; + using Polynomial = bb::Polynomial; + using GroupElement = typename G1::element; + using Commitment = typename G1::affine_element; + using CommitmentKey = bb::CommitmentKey; + using VerifierCommitmentKey = bb::VerifierCommitmentKey; + using RelationSeparator = FF; + using MSM = bb::eccvm::MSM; + + using NativeFlavor = ECCVMFlavor; + using NativeVerificationKey = NativeFlavor::VerificationKey; + + +static constexpr size_t NUM_WIRES = ECCCVMFlavor::NUM_WIRES; + // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often + // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. + // Note: this number does not include the individual sorted list polynomials. + static constexpr size_t NUM_ALL_ENTITIES = ECCCVMFlavor::NUM_ALL_ENTITIES; + // The number of polynomials precomputed to describe a circuit and to aid a prover in constructing a satisfying + // assignment of witnesses. We again choose a neutral name. + static constexpr size_t NUM_PRECOMPUTED_ENTITIES = ECCCVMFlavor::NUM_PRECOMPUTED_ENTITIES; + // The total number of witness entities not including shifts. + static constexpr size_t NUM_WITNESS_ENTITIES = ECCCVMFlavor::NUM_WITNESS_ENTITIES; + + // define the tuple of Relations that comprise the Sumcheck relation + // Reuse the Relations from ECCVM + using Relations = ECCVMFlavor::Relations_; + + // think these two are not needed for recursive verifier land + // using GrandProductRelations = std::tuple>; + // using LookupRelation = ECCVMLookupRelation; + static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); + + // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` + // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation + // length = 3 + static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 1; + static constexpr size_t NUM_RELATIONS = std::tuple_size::value; + + // Instantiate the BarycentricData needed to extend each Relation Univariate + + // define the containers for storing the contributions from each relation in Sumcheck + using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); + using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); + + + public: + + /** + * @brief A field element for each entity of the flavor. These entities represent the prover polynomials + * evaluated at one point. + */ + class AllValues : public ECCVMFlavor::AllEntities { + public: + using Base = ECCVMFlavor::AllEntities; + using Base::Base; + }; + + + + /** + * @brief The verification key is responsible for storing the the commitments to the precomputed (non-witnessk) + * polynomials used by the verifier. + * + * @note Note the discrepancy with what sort of data is stored here vs in the proving key. We may want to + * resolve that, and split out separate PrecomputedPolynomials/Commitments data for clarity but also for + * portability of our circuits. + */ + class VerificationKey : VerificationKey_, VerifierCommitmentKey> { + public: + + VerificationKey(const size_t circuit_size, const size_t num_public_inputs) + { + this->circuit_size = circuit_size; + this->log_circuit_size = numeric::get_msb(circuit_size); + this->num_public_inputs = num_public_inputs; + }; + + /** + * @brief Construct a new Verification Key with stdlib types from a provided native verification + * key + * + * @param builder + * @param native_key Native verification key from which to extract the precomputed commitments + */ + + VerificationKey(CircuitBuilder* builder, const std::shared_ptr& native_key) + { + this->pcs_verification_key = native_key->pcs_verification_key; + this->circuit_size = native_key->circuit_size; + this->log_circuit_size = numeric::get_msb(this->circuit_size); + this->num_public_inputs = native_key->num_public_inputs; + this->pub_inputs_offset = native_key->pub_inputs_offset; + + for (auto [native_commitment, commitment] : + zip_view(native_key->get_all(), this->get_all())) { + commitment = Commitment::from_witness(builder, native_commitment); + } + } + }; + + /** + * @brief A container for the witness commitments. + */ + using WitnessCommitments = ECCVMFlavor::WitnessEntities; + + using CommitmentLabels = ECCVMFlavor::CommitmentLabels; + // Reuse the VerifierCommitments from ECCVM + using VerifierCommitments = ECCVMFlavor::VerifierCommitments_; + // Reuse the transcript from ECCVM + using Transcript = bb::BaseTranscript>; + +};// NOLINTEND(cppcoreguidelines-avoid-const-or-ref-data-members) + +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp new file mode 100644 index 00000000000..bca72149c68 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp @@ -0,0 +1,123 @@ +#include "./eccvm_verifier.hpp" +#include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" +#include "barretenberg/sumcheck/sumcheck.hpp" + +namespace bb { + + template +ECCVMRecursiveVerifier_::ECCVMRecursiveVerifier_( + Builder* builder, const std::shared_ptr& native_verifier_key) + : key(std::make_shared(builder, native_verifier_key)) + , builder(builder) +{} + +/** + * @brief This function verifies an ECCVM Honk proof for given program settings. + */ +template +std::array ECCVMRecursiveVerifier_::verify_proof(const HonkProof& proof) +{ + using ZeroMorph = ZeroMorphVerifier_; + + RelationParameters relation_parameters; + transcript = std::make_shared(proof); + VerifierCommitments commitments{ key }; + CommitmentLabels commitment_labels; + + const auto circuit_size = transcript->template receive_from_prover("circuit_size"); + ASSERT(circuit_size == key->circuit_size); + + for(auto [comm, label]: zip_view(commitments.get_wires(), commitment_labels.get_wires())) { + comm = transcript->template receive_from_prover(label); + } + + // Get challenge for sorted list batching and wire four memory records + auto [beta, gamma] = transcript->template get_challenges("beta", "gamma"); + + // there is an issue somewhere to simplify this :-? + relation_parameters.gamma = gamma; + relation_parameters.beta = beta; + relation_parameters.beta_sqr = beta * beta; + relation_parameters.beta_cube = beta_sqr * beta; + relation_parameters.eccvm_set_permutation_delta = + gamma * (gamma + beta_sqr) * (gamma + beta_sqr + beta_sqr) * (gamma + beta_sqr + beta_sqr + beta_sqr); + relation_parameters.eccvm_set_permutation_delta = relation_parameters.eccvm_set_permutation_delta.invert(); + + // these are the derived stuff only + // Get commitment to permutation and lookup grand products + commitments.lookup_inverses = receive_commitment(commitment_labels.lookup_inverses); + commitments.z_perm = receive_commitment(commitment_labels.z_perm); + + // Execute Sumcheck Verifier + const size_t log_circuit_size = numeric::get_msb(circuit_size); + auto sumcheck = SumcheckVerifier(log_circuit_size, transcript); + FF alpha = transcript->template get_challenge("Sumcheck:alpha"); + std::vector gate_challenges(static_cast(numeric::get_msb(key->circuit_size))); + for (size_t idx = 0; idx < gate_challenges.size(); idx++) { + gate_challenges[idx] = transcript->template get_challenge("Sumcheck:gate_challenge_" + std::to_string(idx)); + } + + auto [multivariate_challenge, claimed_evaluations, sumcheck_verified] = + sumcheck.verify(relation_parameters, alpha, gate_challenges); + + // If Sumcheck did not verify, return false + if (sumcheck_verified.has_value() && !sumcheck_verified.value()) { + return false; + } + + bool multivariate_opening_verified = ZeroMorph::verify(commitments.get_unshifted(), + commitments.get_to_be_shifted(), + claimed_evaluations.get_unshifted(), + claimed_evaluations.get_shifted(), + multivariate_challenge, + key->pcs_verification_key, + transcript); + // Execute transcript consistency univariate opening round + // TODO(#768): Find a better way to do this. See issue for details. + bool univariate_opening_verified = false; + { + auto hack_commitment = receive_commitment("Translation:hack_commitment"); + + FF evaluation_challenge_x = transcript->template get_challenge("Translation:evaluation_challenge_x"); + + // Construct arrays of commitments and evaluations to be batched + const size_t NUM_UNIVARIATES = 6; + std::array transcript_commitments = { + commitments.transcript_op, commitments.transcript_Px, commitments.transcript_Py, + commitments.transcript_z1, commitments.transcript_z2, hack_commitment + }; + std::array transcript_evaluations = { + transcript->template receive_from_prover("Translation:op"), + transcript->template receive_from_prover("Translation:Px"), + transcript->template receive_from_prover("Translation:Py"), + transcript->template receive_from_prover("Translation:z1"), + transcript->template receive_from_prover("Translation:z2"), + transcript->template receive_from_prover("Translation:hack_evaluation") + }; + + // Get another challenge for batching the univariate claims + FF ipa_batching_challenge = transcript->template get_challenge("Translation:ipa_batching_challenge"); + + // Construct batched commitment and batched evaluation + auto batched_commitment = transcript_commitments[0]; + auto batched_transcript_eval = transcript_evaluations[0]; + auto batching_scalar = ipa_batching_challenge; + for (size_t idx = 1; idx < transcript_commitments.size(); ++idx) { + batched_commitment = batched_commitment + transcript_commitments[idx] * batching_scalar; + batched_transcript_eval += batching_scalar * transcript_evaluations[idx]; + batching_scalar *= ipa_batching_challenge; + } + + // Construct and verify batched opening claim + OpeningClaim batched_univariate_claim = { { evaluation_challenge_x, batched_transcript_eval }, + batched_commitment }; + univariate_opening_verified = + PCS::reduce_verify(key->pcs_verification_key, batched_univariate_claim, transcript); + } + + return sumcheck_verified.value() && multivariate_opening_verified && univariate_opening_verified; +} + +template class ECCVMRecursiveVerifier_>; +template class ECCVMRecursiveVerifier_>; +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp new file mode 100644 index 00000000000..c77b35b7e6a --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp @@ -0,0 +1,31 @@ +#pragma once +#include "barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp" + +namespace bb { +template class ECCVMRecursiveVerifier_ { + using Flavor = ECCVMFlavor; + using FF = typename Flavor::FF; + using Curve = typename Flavor::Curve; + using Commitment = typename Flavor::Commitment; + using CommitmentLabels = typename Flavor::CommitmentLabels; + using VerificationKey = typename Flavor::VerificationKey; + using NativeVerificationKey = typename Flavor::NativeVerificationKey; + using VerifierCommitmentKey = typename Flavor::VerifierCommitmentKey; + using Builder = typename Flavor::CircuitBuilder; + using PCS = typename Flavor::PCS; + using Transcript = bb::BaseTranscript>; + using PairingPoints = std::array; + using VerifierCommitments = typename Flavor::VerifierCommitments; // dunno if I need thos + public: + explicit ECCVMRecursiveVerifier_(Builder* builder, + const std::shared_ptr& native_verifier_key); + + PairingPoints verify_proof(const HonkProof& proof); + + std::shared_ptr key; + std::map commitments; + + Builder* builder; + std::shared_ptr transcript; +}; +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_flavor.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_flavor.hpp index ff7cc5fffa8..34a3f4d001d 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_flavor.hpp @@ -871,12 +871,6 @@ class GoblinTranslatorFlavor { */ using ProverPolynomialIds = AllEntities; - /** - * @brief A container for polynomials produced after the first round of sumcheck. - * @todo TODO(#394) Use polynomial classes for guaranteed memory alignment. - */ - using RowPolynomials = AllEntities; - /** * @brief A container for storing the partially evaluated multivariates produced by sumcheck. */ diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/avm_flavor.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/avm_flavor.hpp index 55c27f13671..205ee1ada25 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/avm_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/avm_flavor.hpp @@ -2094,8 +2094,6 @@ class AvmFlavor { } }; - using RowPolynomials = AllEntities; - class PartiallyEvaluatedMultivariates : public AllEntities { public: PartiallyEvaluatedMultivariates() = default; diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/spike_flavor.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/spike_flavor.hpp index e1725c5b93a..b901eedb6f2 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/spike_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/spike_flavor.hpp @@ -148,8 +148,6 @@ class SpikeFlavor { } }; - using RowPolynomials = AllEntities; - class PartiallyEvaluatedMultivariates : public AllEntities { public: PartiallyEvaluatedMultivariates() = default; From 8b3a8cde507ffef84b5308326787867dc5bc04d7 Mon Sep 17 00:00:00 2001 From: maramihali Date: Tue, 14 May 2024 21:27:32 +0100 Subject: [PATCH 03/40] start doing recursive work --- barretenberg/cpp/src/CMakeLists.txt | 2 + .../src/barretenberg/eccvm/eccvm_flavor.hpp | 36 ++--- .../src/barretenberg/eccvm/eccvm_verifier.cpp | 95 ++---------- .../src/barretenberg/eccvm/eccvm_verifier.hpp | 1 - .../eccvm_recursive_flavor.hpp | 137 ++++++++++++++++++ .../eccvm_recursive_verifier.cpp | 131 +++++++++++++++++ .../eccvm_recursive_verifier.hpp | 31 ++++ .../goblin_translator_flavor.hpp | 6 - .../barretenberg/vm/generated/avm_flavor.hpp | 2 - .../vm/generated/spike_flavor.hpp | 2 - 10 files changed, 327 insertions(+), 116 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp diff --git a/barretenberg/cpp/src/CMakeLists.txt b/barretenberg/cpp/src/CMakeLists.txt index a9809f12c61..85135034bb9 100644 --- a/barretenberg/cpp/src/CMakeLists.txt +++ b/barretenberg/cpp/src/CMakeLists.txt @@ -61,6 +61,7 @@ add_subdirectory(barretenberg/crypto) add_subdirectory(barretenberg/dsl) add_subdirectory(barretenberg/ecc) add_subdirectory(barretenberg/eccvm) +add_subdirectory(barretenberg/eccvm_recursion) add_subdirectory(barretenberg/env) add_subdirectory(barretenberg/execution_trace) add_subdirectory(barretenberg/examples) @@ -117,6 +118,7 @@ set(BARRETENBERG_TARGET_OBJECTS $ $ $ + $ $ $ $ diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp index 04140dfe9cc..49238d8fd67 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp @@ -50,13 +50,14 @@ class ECCVMFlavor { using GrandProductRelations = std::tuple>; // define the tuple of Relations that comprise the Sumcheck relation - using Relations = std::tuple, - ECCVMPointTableRelation, - ECCVMWnafRelation, - ECCVMMSMRelation, - ECCVMSetRelation, - ECCVMLookupRelation>; - + template + using Relations_ = std::tuple, + ECCVMPointTableRelation, + ECCVMWnafRelation, + ECCVMMSMRelation, + ECCVMSetRelation, + ECCVMLookupRelation>; + using Relations = Relations_; using LookupRelation = ECCVMLookupRelation; static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); @@ -72,7 +73,9 @@ class ECCVMFlavor { using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); - private: + // WORKTODO make the specifiers good + + public: /** * @brief A base class labelling precomputed entities and (ordered) subsets of interest. * @details Used to build the proving key and verification key. @@ -298,7 +301,6 @@ class ECCVMFlavor { }; public: - /** * @brief A field element for each entity of the flavor. These entities represent the prover polynomials * evaluated at one point. @@ -309,12 +311,6 @@ class ECCVMFlavor { using Base::Base; }; - /** - * @brief A container for polynomials produced after the first round of sumcheck. - * @todo TODO(#394) Use polynomial classes for guaranteed memory alignment. - */ - using RowPolynomials = AllEntities; - /** * @brief A container for storing the partially evaluated multivariates produced by sumcheck. */ @@ -624,14 +620,11 @@ class ECCVMFlavor { */ class VerificationKey : public VerificationKey_, VerifierCommitmentKey> { public: - std::vector public_inputs; - VerificationKey(const size_t circuit_size, const size_t num_public_inputs) : VerificationKey_(circuit_size, num_public_inputs) {} VerificationKey(const std::shared_ptr& proving_key) - : public_inputs(proving_key->public_inputs) { this->pcs_verification_key = std::make_shared(proving_key->circuit_size); this->circuit_size = proving_key->circuit_size; @@ -743,9 +736,10 @@ class ECCVMFlavor { }; }; - class VerifierCommitments : public AllEntities { + template + class VerifierCommitments_ : public AllEntities { public: - VerifierCommitments(const std::shared_ptr& verification_key) + VerifierCommitments_(const std::shared_ptr& verification_key) { this->lagrange_first = verification_key->lagrange_first; this->lagrange_second = verification_key->lagrange_second; @@ -753,6 +747,8 @@ class ECCVMFlavor { } }; + using VerifierCommitments = VerifierCommitments_; + /** * @brief Derived class that defines proof structure for ECCVM proofs, as well as supporting functions. * diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp index 20c6b720533..b3f93adf632 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp @@ -19,94 +19,18 @@ bool ECCVMVerifier::verify_proof(const HonkProof& proof) const auto circuit_size = transcript->template receive_from_prover("circuit_size"); ASSERT(circuit_size == key->circuit_size); - // Utility for extracting commitments from transcript - const auto receive_commitment = [&](const std::string& label) { - return transcript->template receive_from_prover(label); - }; - - // Get commitments to VM wires - commitments.transcript_add = receive_commitment(commitment_labels.transcript_add); - commitments.transcript_mul = receive_commitment(commitment_labels.transcript_mul); - commitments.transcript_eq = receive_commitment(commitment_labels.transcript_eq); - commitments.transcript_collision_check = receive_commitment(commitment_labels.transcript_collision_check); - commitments.transcript_msm_transition = receive_commitment(commitment_labels.transcript_msm_transition); - commitments.transcript_pc = receive_commitment(commitment_labels.transcript_pc); - commitments.transcript_msm_count = receive_commitment(commitment_labels.transcript_msm_count); - commitments.transcript_Px = receive_commitment(commitment_labels.transcript_Px); - commitments.transcript_Py = receive_commitment(commitment_labels.transcript_Py); - commitments.transcript_z1 = receive_commitment(commitment_labels.transcript_z1); - commitments.transcript_z2 = receive_commitment(commitment_labels.transcript_z2); - commitments.transcript_z1zero = receive_commitment(commitment_labels.transcript_z1zero); - commitments.transcript_z2zero = receive_commitment(commitment_labels.transcript_z2zero); - commitments.transcript_op = receive_commitment(commitment_labels.transcript_op); - commitments.transcript_accumulator_x = receive_commitment(commitment_labels.transcript_accumulator_x); - commitments.transcript_accumulator_y = receive_commitment(commitment_labels.transcript_accumulator_y); - commitments.transcript_msm_x = receive_commitment(commitment_labels.transcript_msm_x); - commitments.transcript_msm_y = receive_commitment(commitment_labels.transcript_msm_y); - commitments.precompute_pc = receive_commitment(commitment_labels.precompute_pc); - commitments.precompute_point_transition = receive_commitment(commitment_labels.precompute_point_transition); - commitments.precompute_round = receive_commitment(commitment_labels.precompute_round); - commitments.precompute_scalar_sum = receive_commitment(commitment_labels.precompute_scalar_sum); - commitments.precompute_s1hi = receive_commitment(commitment_labels.precompute_s1hi); - commitments.precompute_s1lo = receive_commitment(commitment_labels.precompute_s1lo); - commitments.precompute_s2hi = receive_commitment(commitment_labels.precompute_s2hi); - commitments.precompute_s2lo = receive_commitment(commitment_labels.precompute_s2lo); - commitments.precompute_s3hi = receive_commitment(commitment_labels.precompute_s3hi); - commitments.precompute_s3lo = receive_commitment(commitment_labels.precompute_s3lo); - commitments.precompute_s4hi = receive_commitment(commitment_labels.precompute_s4hi); - commitments.precompute_s4lo = receive_commitment(commitment_labels.precompute_s4lo); - commitments.precompute_skew = receive_commitment(commitment_labels.precompute_skew); - commitments.precompute_dx = receive_commitment(commitment_labels.precompute_dx); - commitments.precompute_dy = receive_commitment(commitment_labels.precompute_dy); - commitments.precompute_tx = receive_commitment(commitment_labels.precompute_tx); - commitments.precompute_ty = receive_commitment(commitment_labels.precompute_ty); - commitments.msm_transition = receive_commitment(commitment_labels.msm_transition); - commitments.msm_add = receive_commitment(commitment_labels.msm_add); - commitments.msm_double = receive_commitment(commitment_labels.msm_double); - commitments.msm_skew = receive_commitment(commitment_labels.msm_skew); - commitments.msm_accumulator_x = receive_commitment(commitment_labels.msm_accumulator_x); - commitments.msm_accumulator_y = receive_commitment(commitment_labels.msm_accumulator_y); - commitments.msm_pc = receive_commitment(commitment_labels.msm_pc); - commitments.msm_size_of_msm = receive_commitment(commitment_labels.msm_size_of_msm); - commitments.msm_count = receive_commitment(commitment_labels.msm_count); - commitments.msm_round = receive_commitment(commitment_labels.msm_round); - commitments.msm_add1 = receive_commitment(commitment_labels.msm_add1); - commitments.msm_add2 = receive_commitment(commitment_labels.msm_add2); - commitments.msm_add3 = receive_commitment(commitment_labels.msm_add3); - commitments.msm_add4 = receive_commitment(commitment_labels.msm_add4); - commitments.msm_x1 = receive_commitment(commitment_labels.msm_x1); - commitments.msm_y1 = receive_commitment(commitment_labels.msm_y1); - commitments.msm_x2 = receive_commitment(commitment_labels.msm_x2); - commitments.msm_y2 = receive_commitment(commitment_labels.msm_y2); - commitments.msm_x3 = receive_commitment(commitment_labels.msm_x3); - commitments.msm_y3 = receive_commitment(commitment_labels.msm_y3); - commitments.msm_x4 = receive_commitment(commitment_labels.msm_x4); - commitments.msm_y4 = receive_commitment(commitment_labels.msm_y4); - commitments.msm_collision_x1 = receive_commitment(commitment_labels.msm_collision_x1); - commitments.msm_collision_x2 = receive_commitment(commitment_labels.msm_collision_x2); - commitments.msm_collision_x3 = receive_commitment(commitment_labels.msm_collision_x3); - commitments.msm_collision_x4 = receive_commitment(commitment_labels.msm_collision_x4); - commitments.msm_lambda1 = receive_commitment(commitment_labels.msm_lambda1); - commitments.msm_lambda2 = receive_commitment(commitment_labels.msm_lambda2); - commitments.msm_lambda3 = receive_commitment(commitment_labels.msm_lambda3); - commitments.msm_lambda4 = receive_commitment(commitment_labels.msm_lambda4); - commitments.msm_slice1 = receive_commitment(commitment_labels.msm_slice1); - commitments.msm_slice2 = receive_commitment(commitment_labels.msm_slice2); - commitments.msm_slice3 = receive_commitment(commitment_labels.msm_slice3); - commitments.msm_slice4 = receive_commitment(commitment_labels.msm_slice4); - commitments.transcript_accumulator_empty = receive_commitment(commitment_labels.transcript_accumulator_empty); - commitments.transcript_reset_accumulator = receive_commitment(commitment_labels.transcript_reset_accumulator); - commitments.precompute_select = receive_commitment(commitment_labels.precompute_select); - commitments.lookup_read_counts_0 = receive_commitment(commitment_labels.lookup_read_counts_0); - commitments.lookup_read_counts_1 = receive_commitment(commitment_labels.lookup_read_counts_1); + for (auto [comm, label] : zip_view(commitments.get_wires(), commitment_labels.get_wires())) { + comm = transcript->template receive_from_prover(label); + } // Get challenge for sorted list batching and wire four memory records auto [beta, gamma] = transcript->template get_challenges("beta", "gamma"); - relation_parameters.gamma = gamma; + // there is an issue somewhere to simplify this :-? auto beta_sqr = beta * beta; + relation_parameters.gamma = gamma; relation_parameters.beta = beta; - relation_parameters.beta_sqr = beta_sqr; + relation_parameters.beta_sqr = beta * beta; relation_parameters.beta_cube = beta_sqr * beta; relation_parameters.eccvm_set_permutation_delta = gamma * (gamma + beta_sqr) * (gamma + beta_sqr + beta_sqr) * (gamma + beta_sqr + beta_sqr + beta_sqr); @@ -114,8 +38,9 @@ bool ECCVMVerifier::verify_proof(const HonkProof& proof) // these are the derived stuff only // Get commitment to permutation and lookup grand products - commitments.lookup_inverses = receive_commitment(commitment_labels.lookup_inverses); - commitments.z_perm = receive_commitment(commitment_labels.z_perm); + commitments.lookup_inverses = + transcript->template receive_from_prover(commitment_labels.lookup_inverses); + commitments.z_perm = transcript->template receive_from_prover(commitment_labels.z_perm); // Execute Sumcheck Verifier const size_t log_circuit_size = numeric::get_msb(circuit_size); @@ -145,7 +70,7 @@ bool ECCVMVerifier::verify_proof(const HonkProof& proof) // TODO(#768): Find a better way to do this. See issue for details. bool univariate_opening_verified = false; { - auto hack_commitment = receive_commitment("Translation:hack_commitment"); + auto hack_commitment = transcript->template receive_from_prover("Translation:hack_commitment"); FF evaluation_challenge_x = transcript->template get_challenge("Translation:evaluation_challenge_x"); diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.hpp index 5ef29beff58..10e49d08a76 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.hpp @@ -26,7 +26,6 @@ class ECCVMVerifier { std::shared_ptr key; std::map commitments; - std::map pcs_fr_elements; std::shared_ptr transcript; }; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp new file mode 100644 index 00000000000..e1b1d130e2e --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp @@ -0,0 +1,137 @@ +#pragma once +#include "barretenberg/commitment_schemes/ipa/ipa.hpp" +#include "barretenberg/common/std_array.hpp" +#include "barretenberg/eccvm/eccvm_flavor.hpp" +#include "barretenberg/flavor/flavor.hpp" +#include "barretenberg/flavor/flavor_macros.hpp" +#include "barretenberg/flavor/relation_definitions.hpp" +#include "barretenberg/polynomials/univariate.hpp" +#include "barretenberg/relations/ecc_vm/ecc_lookup_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_msm_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_point_table_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_set_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_transcript_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_wnaf_relation.hpp" +#include "barretenberg/relations/relation_parameters.hpp" +#include "barretenberg/stdlib/honk_recursion/transcript/transcript.hpp" +#include "barretenberg/stdlib/primitives/curves/bn254SimulatingGrumpkin.hpp" + +// NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members) ? + +namespace bb { + +template class ECCVMRecursiveFlavor_ { + public: + using CircuitBuilder = BuilderType; // determines the arithmetisation of recursive verifier + + // I need to somehow make this bn254 hashtag confusion + // the native stuff becomes nonnative and the nonnative stuff becomes native and everything is in circuit land is + // probably what's going on + using Curve = stdlib::bn254SimulatingGrumpkin; + using G1 = typename Curve::Group; + using PCS = IPA; + using FF = typename Curve::ScalarField; + using GroupElement = typename Curve::Element; + using Commitment = typename Curve::Element; + using CommitmentKey = bb::CommitmentKey; + using VerifierCommitmentKey = bb::VerifierCommitmentKey; + using RelationSeparator = FF; + using NativeFlavor = ECCVMFlavor; + using NativeVerificationKey = NativeFlavor::VerificationKey; + + static constexpr size_t NUM_WIRES = ECCVMFlavor::NUM_WIRES; + // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often + // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. + // Note: this number does not include the individual sorted list polynomials. + static constexpr size_t NUM_ALL_ENTITIES = ECCVMFlavor::NUM_ALL_ENTITIES; + // The number of polynomials precomputed to describe a circuit and to aid a prover in constructing a satisfying + // assignment of witnesses. We again choose a neutral name. + static constexpr size_t NUM_PRECOMPUTED_ENTITIES = ECCVMFlavor::NUM_PRECOMPUTED_ENTITIES; + // The total number of witness entities not including shifts. + static constexpr size_t NUM_WITNESS_ENTITIES = ECCVMFlavor::NUM_WITNESS_ENTITIES; + + // define the tuple of Relations that comprise the Sumcheck relation + // Reuse the Relations from ECCVM + using Relations = ECCVMFlavor::Relations_; + + // think these two are not needed for recursive verifier land + // using GrandProductRelations = std::tuple>; + // using LookupRelation = ECCVMLookupRelation; + static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); + + // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` + // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation + // length = 3 + static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 1; + static constexpr size_t NUM_RELATIONS = std::tuple_size::value; + + // Instantiate the BarycentricData needed to extend each Relation Univariate + + // define the containers for storing the contributions from each relation in Sumcheck + using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); + using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); + + public: + /** + * @brief A field element for each entity of the flavor. These entities represent the prover polynomials + * evaluated at one point. + */ + class AllValues : public ECCVMFlavor::AllEntities { + public: + using Base = ECCVMFlavor::AllEntities; + using Base::Base; + }; + + /** + * @brief The verification key is responsible for storing the the commitments to the precomputed (non-witnessk) + * polynomials used by the verifier. + * + * @note Note the discrepancy with what sort of data is stored here vs in the proving key. We may want to + * resolve that, and split out separate PrecomputedPolynomials/Commitments data for clarity but also for + * portability of our circuits. + */ + class VerificationKey : VerificationKey_, VerifierCommitmentKey> { + public: + VerificationKey(const size_t circuit_size, const size_t num_public_inputs) + { + this->circuit_size = circuit_size; + this->log_circuit_size = numeric::get_msb(circuit_size); + this->num_public_inputs = num_public_inputs; + }; + + /** + * @brief Construct a new Verification Key with stdlib types from a provided native verification + * key + * + * @param builder + * @param native_key Native verification key from which to extract the precomputed commitments + */ + + VerificationKey(CircuitBuilder* builder, const std::shared_ptr& native_key) + { + this->pcs_verification_key = native_key->pcs_verification_key; + this->circuit_size = native_key->circuit_size; + this->log_circuit_size = numeric::get_msb(this->circuit_size); + this->num_public_inputs = native_key->num_public_inputs; + this->pub_inputs_offset = native_key->pub_inputs_offset; + + for (auto [native_commitment, commitment] : zip_view(native_key->get_all(), this->get_all())) { + commitment = Commitment::from_witness(builder, native_commitment); + } + } + }; + + /** + * @brief A container for the witness commitments. + */ + using WitnessCommitments = ECCVMFlavor::WitnessEntities; + + using CommitmentLabels = ECCVMFlavor::CommitmentLabels; + // Reuse the VerifierCommitments from ECCVM + using VerifierCommitments = ECCVMFlavor::VerifierCommitments_; + // Reuse the transcript from ECCVM + using Transcript = bb::BaseTranscript>; + +}; // NOLINTEND(cppcoreguidelines-avoid-const-or-ref-data-members) + +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp new file mode 100644 index 00000000000..c738d6d482f --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp @@ -0,0 +1,131 @@ +#include "./eccvm_recursive_verifier.hpp" +#include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" +#include "barretenberg/sumcheck/sumcheck.hpp" +#include "barretenberg/transcript/transcript.hpp" + +namespace bb { + +template +ECCVMRecursiveVerifier_::ECCVMRecursiveVerifier_( + Builder* builder, const std::shared_ptr& native_verifier_key) + : key(std::make_shared(builder, native_verifier_key)) + , builder(builder) +{} + +/** + * @brief This function verifies an ECCVM Honk proof for given program settings. + */ +template +std::array ECCVMRecursiveVerifier_::verify_proof(const HonkProof& proof) +{ + + using ZeroMorph = ZeroMorphVerifier_; + + RelationParameters relation_parameters; + + StdlibProof stdlib_proof = bb::convert_proof_to_witness(builder, proof); + transcript = std::make_shared(stdlib_proof); + + VerifierCommitments commitments{ key }; + CommitmentLabels commitment_labels; + + const auto circuit_size = transcript->template receive_from_prover("circuit_size"); + ASSERT(circuit_size == key->circuit_size); + + for (auto [comm, label] : zip_view(commitments.get_wires(), commitment_labels.get_wires())) { + comm = transcript->template receive_from_prover(label); + } + + // Get challenge for sorted list batching and wire four memory records + auto [beta, gamma] = transcript->template get_challenges("beta", "gamma"); + + // there is an issue somewhere to simplify this :-? + auto beta_sqr = beta * beta; + + relation_parameters.gamma = gamma; + relation_parameters.beta = beta; + relation_parameters.beta_sqr = beta * beta; + relation_parameters.beta_cube = beta_sqr * beta; + relation_parameters.eccvm_set_permutation_delta = + gamma * (gamma + beta_sqr) * (gamma + beta_sqr + beta_sqr) * (gamma + beta_sqr + beta_sqr + beta_sqr); + relation_parameters.eccvm_set_permutation_delta = relation_parameters.eccvm_set_permutation_delta.invert(); + + // these are the derived stuff only + // Get commitment to permutation and lookup grand products + commitments.lookup_inverses = + transcript->template receive_from_prover(commitment_labels.lookup_inverses); + commitments.z_perm = transcript->template receive_from_prover(commitment_labels.z_perm); + + // Execute Sumcheck Verifier + const size_t log_circuit_size = numeric::get_msb(circuit_size); + auto sumcheck = SumcheckVerifier(log_circuit_size, transcript); + FF alpha = transcript->template get_challenge("Sumcheck:alpha"); + std::vector gate_challenges(static_cast(numeric::get_msb(key->circuit_size))); + for (size_t idx = 0; idx < gate_challenges.size(); idx++) { + gate_challenges[idx] = transcript->template get_challenge("Sumcheck:gate_challenge_" + std::to_string(idx)); + } + + auto [multivariate_challenge, claimed_evaluations, sumcheck_verified] = + sumcheck.verify(relation_parameters, alpha, gate_challenges); + + // If Sumcheck did not verify, return false + if (sumcheck_verified.has_value() && !sumcheck_verified.value()) { + return false; + } + + bool multivariate_opening_verified = ZeroMorph::verify(commitments.get_unshifted(), + commitments.get_to_be_shifted(), + claimed_evaluations.get_unshifted(), + claimed_evaluations.get_shifted(), + multivariate_challenge, + key->pcs_verification_key, + transcript); + // Execute transcript consistency univariate opening round + // TODO(#768): Find a better way to do this. See issue for details. + bool univariate_opening_verified = false; + { + auto hack_commitment = transcript->template receive_from_prover("Translation:hack_commitment"); + + FF evaluation_challenge_x = transcript->template get_challenge("Translation:evaluation_challenge_x"); + + // Construct arrays of commitments and evaluations to be batched + const size_t NUM_UNIVARIATES = 6; + std::array transcript_commitments = { + commitments.transcript_op, commitments.transcript_Px, commitments.transcript_Py, + commitments.transcript_z1, commitments.transcript_z2, hack_commitment + }; + std::array transcript_evaluations = { + transcript->template receive_from_prover("Translation:op"), + transcript->template receive_from_prover("Translation:Px"), + transcript->template receive_from_prover("Translation:Py"), + transcript->template receive_from_prover("Translation:z1"), + transcript->template receive_from_prover("Translation:z2"), + transcript->template receive_from_prover("Translation:hack_evaluation") + }; + + // Get another challenge for batching the univariate claims + FF ipa_batching_challenge = transcript->template get_challenge("Translation:ipa_batching_challenge"); + + // Construct batched commitment and batched evaluation + auto batched_commitment = transcript_commitments[0]; + auto batched_transcript_eval = transcript_evaluations[0]; + auto batching_scalar = ipa_batching_challenge; + for (size_t idx = 1; idx < transcript_commitments.size(); ++idx) { + batched_commitment = batched_commitment + transcript_commitments[idx] * batching_scalar; + batched_transcript_eval += batching_scalar * transcript_evaluations[idx]; + batching_scalar *= ipa_batching_challenge; + } + + // Construct and verify batched opening claim + OpeningClaim batched_univariate_claim = { { evaluation_challenge_x, batched_transcript_eval }, + batched_commitment }; + univariate_opening_verified = + PCS::reduce_verify(key->pcs_verification_key, batched_univariate_claim, transcript); + } + + return sumcheck_verified.value() && multivariate_opening_verified && univariate_opening_verified; +} + +template class ECCVMRecursiveVerifier_>; +// template class ECCVMRecursiveVerifier_>; +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp new file mode 100644 index 00000000000..07bdb1de924 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp @@ -0,0 +1,31 @@ +#pragma once +#include "barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp" + +namespace bb { +template class ECCVMRecursiveVerifier_ { + using FF = typename Flavor::FF; + using Curve = typename Flavor::Curve; + using Commitment = typename Flavor::Commitment; + using CommitmentLabels = typename Flavor::CommitmentLabels; + using VerificationKey = typename Flavor::VerificationKey; + using NativeVerificationKey = typename Flavor::NativeVerificationKey; + using VerifierCommitmentKey = typename Flavor::VerifierCommitmentKey; + using Builder = typename Flavor::CircuitBuilder; + using PCS = typename Flavor::PCS; + using Transcript = bb::BaseTranscript>; + using GroupElement = typename Flavor::GroupElement; + using PairingPoints = std::array; + using VerifierCommitments = typename Flavor::VerifierCommitments; // dunno if I need thos + public: + explicit ECCVMRecursiveVerifier_(Builder* builder, + const std::shared_ptr& native_verifier_key); + + PairingPoints verify_proof(const HonkProof& proof); + + std::shared_ptr key; + std::map commitments; + + Builder* builder; + std::shared_ptr transcript; +}; +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_flavor.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_flavor.hpp index ff7cc5fffa8..34a3f4d001d 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_flavor.hpp @@ -871,12 +871,6 @@ class GoblinTranslatorFlavor { */ using ProverPolynomialIds = AllEntities; - /** - * @brief A container for polynomials produced after the first round of sumcheck. - * @todo TODO(#394) Use polynomial classes for guaranteed memory alignment. - */ - using RowPolynomials = AllEntities; - /** * @brief A container for storing the partially evaluated multivariates produced by sumcheck. */ diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/avm_flavor.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/avm_flavor.hpp index 55c27f13671..205ee1ada25 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/avm_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/avm_flavor.hpp @@ -2094,8 +2094,6 @@ class AvmFlavor { } }; - using RowPolynomials = AllEntities; - class PartiallyEvaluatedMultivariates : public AllEntities { public: PartiallyEvaluatedMultivariates() = default; diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/spike_flavor.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/spike_flavor.hpp index e1725c5b93a..b901eedb6f2 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/spike_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/spike_flavor.hpp @@ -148,8 +148,6 @@ class SpikeFlavor { } }; - using RowPolynomials = AllEntities; - class PartiallyEvaluatedMultivariates : public AllEntities { public: PartiallyEvaluatedMultivariates() = default; From 0e368c1c6f085d19170821b68f7c89b34a12f27b Mon Sep 17 00:00:00 2001 From: maramihali Date: Mon, 20 May 2024 11:07:16 +0000 Subject: [PATCH 04/40] stuff --- .../eccvm_recursive_flavor.hpp | 286 +++++++----------- .../goblin_translator_flavor.hpp | 3 - 2 files changed, 108 insertions(+), 181 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp index bb90c873385..e1b1d130e2e 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp @@ -1,13 +1,7 @@ #pragma once #include "barretenberg/commitment_schemes/ipa/ipa.hpp" #include "barretenberg/common/std_array.hpp" -<<<<<<< HEAD #include "barretenberg/eccvm/eccvm_flavor.hpp" - ======= -#include "barretenberg/ecc/curves/bn254/bn254.hpp" -#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" -#include "barretenberg/eccvm/eccvm_circuit_builder.hpp" - >>>>>>> 927b78b2b6def848e5718b2df9e6a9695396b354 #include "barretenberg/flavor/flavor.hpp" #include "barretenberg/flavor/flavor_macros.hpp" #include "barretenberg/flavor/relation_definitions.hpp" @@ -19,189 +13,125 @@ #include "barretenberg/relations/ecc_vm/ecc_transcript_relation.hpp" #include "barretenberg/relations/ecc_vm/ecc_wnaf_relation.hpp" #include "barretenberg/relations/relation_parameters.hpp" - <<<<<<< HEAD #include "barretenberg/stdlib/honk_recursion/transcript/transcript.hpp" #include "barretenberg/stdlib/primitives/curves/bn254SimulatingGrumpkin.hpp" - ======= ->>>>>>> 927b78b2b6def848e5718b2df9e6a9695396b354 - // NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members) ? - - namespace bb -{ - - template class ECCVMRecursiveFlavor_ { +// NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members) ? + +namespace bb { + +template class ECCVMRecursiveFlavor_ { + public: + using CircuitBuilder = BuilderType; // determines the arithmetisation of recursive verifier + + // I need to somehow make this bn254 hashtag confusion + // the native stuff becomes nonnative and the nonnative stuff becomes native and everything is in circuit land is + // probably what's going on + using Curve = stdlib::bn254SimulatingGrumpkin; + using G1 = typename Curve::Group; + using PCS = IPA; + using FF = typename Curve::ScalarField; + using GroupElement = typename Curve::Element; + using Commitment = typename Curve::Element; + using CommitmentKey = bb::CommitmentKey; + using VerifierCommitmentKey = bb::VerifierCommitmentKey; + using RelationSeparator = FF; + using NativeFlavor = ECCVMFlavor; + using NativeVerificationKey = NativeFlavor::VerificationKey; + + static constexpr size_t NUM_WIRES = ECCVMFlavor::NUM_WIRES; + // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often + // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. + // Note: this number does not include the individual sorted list polynomials. + static constexpr size_t NUM_ALL_ENTITIES = ECCVMFlavor::NUM_ALL_ENTITIES; + // The number of polynomials precomputed to describe a circuit and to aid a prover in constructing a satisfying + // assignment of witnesses. We again choose a neutral name. + static constexpr size_t NUM_PRECOMPUTED_ENTITIES = ECCVMFlavor::NUM_PRECOMPUTED_ENTITIES; + // The total number of witness entities not including shifts. + static constexpr size_t NUM_WITNESS_ENTITIES = ECCVMFlavor::NUM_WITNESS_ENTITIES; + + // define the tuple of Relations that comprise the Sumcheck relation + // Reuse the Relations from ECCVM + using Relations = ECCVMFlavor::Relations_; + + // think these two are not needed for recursive verifier land + // using GrandProductRelations = std::tuple>; + // using LookupRelation = ECCVMLookupRelation; + static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); + + // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` + // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation + // length = 3 + static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 1; + static constexpr size_t NUM_RELATIONS = std::tuple_size::value; + + // Instantiate the BarycentricData needed to extend each Relation Univariate + + // define the containers for storing the contributions from each relation in Sumcheck + using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); + using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); + + public: + /** + * @brief A field element for each entity of the flavor. These entities represent the prover polynomials + * evaluated at one point. + */ + class AllValues : public ECCVMFlavor::AllEntities { public: - using CircuitBuilder = BuilderType; // determines the arithmetisation of recursive verifier - - // I need to somehow make this bn254 hashtag confusion -<<<<<<< HEAD - // the native stuff becomes nonnative and the nonnative stuff becomes native and everything is in circuit land - // is probably what's going on - using Curve = stdlib::bn254SimulatingGrumpkin; - using G1 = typename Curve::Group; - using PCS = IPA; - using FF = typename Curve::ScalarField; - using GroupElement = typename Curve::Element; - using Commitment = typename Curve::Element; - using CommitmentKey = bb::CommitmentKey; - using VerifierCommitmentKey = bb::VerifierCommitmentKey; - using RelationSeparator = FF; - using NativeFlavor = ECCVMFlavor; - using NativeVerificationKey = NativeFlavor::VerificationKey; - - static constexpr size_t NUM_WIRES = ECCVMFlavor::NUM_WIRES; - // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We - // often need containers of this size to hold related data, so we choose a name more agnostic than - // `NUM_POLYNOMIALS`. Note: this number does not include the individual sorted list polynomials. - static constexpr size_t NUM_ALL_ENTITIES = ECCVMFlavor::NUM_ALL_ENTITIES; - // The number of polynomials precomputed to describe a circuit and to aid a prover in constructing a satisfying - // assignment of witnesses. We again choose a neutral name. - static constexpr size_t NUM_PRECOMPUTED_ENTITIES = ECCVMFlavor::NUM_PRECOMPUTED_ENTITIES; - // The total number of witness entities not including shifts. - static constexpr size_t NUM_WITNESS_ENTITIES = ECCVMFlavor::NUM_WITNESS_ENTITIES; -======= - // the native stuff becomes nonnative and the nonnative stuff becomes native and everything is in circuit land - // is probably what's going on - using CycleGroup = bb::g1; - using Curve = curve::Grumpkin; - using G1 = typename Curve::Group; - using PCS = IPA; - using FF = typename G1::subgroup_field; - using Polynomial = bb::Polynomial; - using GroupElement = typename G1::element; - using Commitment = typename G1::affine_element; - using CommitmentKey = bb::CommitmentKey; - using VerifierCommitmentKey = bb::VerifierCommitmentKey; - using RelationSeparator = FF; - using MSM = bb::eccvm::MSM; - - using NativeFlavor = ECCVMFlavor; - using NativeVerificationKey = NativeFlavor::VerificationKey; - - static constexpr size_t NUM_WIRES = ECCCVMFlavor::NUM_WIRES; - // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We - // often need containers of this size to hold related data, so we choose a name more agnostic than - // `NUM_POLYNOMIALS`. Note: this number does not include the individual sorted list polynomials. - static constexpr size_t NUM_ALL_ENTITIES = ECCCVMFlavor::NUM_ALL_ENTITIES; - // The number of polynomials precomputed to describe a circuit and to aid a prover in constructing a satisfying - // assignment of witnesses. We again choose a neutral name. - static constexpr size_t NUM_PRECOMPUTED_ENTITIES = ECCCVMFlavor::NUM_PRECOMPUTED_ENTITIES; - // The total number of witness entities not including shifts. - static constexpr size_t NUM_WITNESS_ENTITIES = ECCCVMFlavor::NUM_WITNESS_ENTITIES; ->>>>>>> 927b78b2b6def848e5718b2df9e6a9695396b354 - - // define the tuple of Relations that comprise the Sumcheck relation - // Reuse the Relations from ECCVM - using Relations = ECCVMFlavor::Relations_; - - // think these two are not needed for recursive verifier land - // using GrandProductRelations = std::tuple>; - // using LookupRelation = ECCVMLookupRelation; - static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); - - // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` - // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation - // length = 3 - static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 1; - static constexpr size_t NUM_RELATIONS = std::tuple_size::value; - - // Instantiate the BarycentricData needed to extend each Relation Univariate - - // define the containers for storing the contributions from each relation in Sumcheck - using SumcheckTupleOfTuplesOfUnivariates = - decltype(create_sumcheck_tuple_of_tuples_of_univariates()); - using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); - -<<<<<<< HEAD - - public: -======= - + using Base = ECCVMFlavor::AllEntities; + using Base::Base; + }; + + /** + * @brief The verification key is responsible for storing the the commitments to the precomputed (non-witnessk) + * polynomials used by the verifier. + * + * @note Note the discrepancy with what sort of data is stored here vs in the proving key. We may want to + * resolve that, and split out separate PrecomputedPolynomials/Commitments data for clarity but also for + * portability of our circuits. + */ + class VerificationKey : VerificationKey_, VerifierCommitmentKey> { public: - ->>>>>>> 927b78b2b6def848e5718b2df9e6a9695396b354 - /** - * @brief A field element for each entity of the flavor. These entities represent the prover polynomials - * evaluated at one point. - */ - class AllValues : public ECCVMFlavor::AllEntities { - public: - using Base = ECCVMFlavor::AllEntities; - using Base::Base; + VerificationKey(const size_t circuit_size, const size_t num_public_inputs) + { + this->circuit_size = circuit_size; + this->log_circuit_size = numeric::get_msb(circuit_size); + this->num_public_inputs = num_public_inputs; }; -<<<<<<< HEAD -======= - - ->>>>>>> 927b78b2b6def848e5718b2df9e6a9695396b354 /** - * @brief The verification key is responsible for storing the the commitments to the precomputed (non-witnessk) - * polynomials used by the verifier. + * @brief Construct a new Verification Key with stdlib types from a provided native verification + * key * - * @note Note the discrepancy with what sort of data is stored here vs in the proving key. We may want to - * resolve that, and split out separate PrecomputedPolynomials/Commitments data for clarity but also for - * portability of our circuits. + * @param builder + * @param native_key Native verification key from which to extract the precomputed commitments */ - class VerificationKey : VerificationKey_, VerifierCommitmentKey> { - public: -<<<<<<< HEAD - VerificationKey(const size_t circuit_size, const size_t num_public_inputs) -======= - VerificationKey(const size_t circuit_size, const size_t num_public_inputs) ->>>>>>> 927b78b2b6def848e5718b2df9e6a9695396b354 - { - this->circuit_size = circuit_size; - this->log_circuit_size = numeric::get_msb(circuit_size); - this->num_public_inputs = num_public_inputs; - }; - - /** - * @brief Construct a new Verification Key with stdlib types from a provided native verification - * key - * - * @param builder - * @param native_key Native verification key from which to extract the precomputed commitments - */ - - VerificationKey(CircuitBuilder* builder, const std::shared_ptr& native_key) - { - this->pcs_verification_key = native_key->pcs_verification_key; - this->circuit_size = native_key->circuit_size; - this->log_circuit_size = numeric::get_msb(this->circuit_size); - this->num_public_inputs = native_key->num_public_inputs; - this->pub_inputs_offset = native_key->pub_inputs_offset; - -<<<<<<< HEAD - for (auto [native_commitment, commitment] : zip_view(native_key->get_all(), this->get_all())) { -======= - for (auto [native_commitment, commitment] : zip_view(native_key->get_all(), this->get_all())) { ->>>>>>> 927b78b2b6def848e5718b2df9e6a9695396b354 - commitment = Commitment::from_witness(builder, native_commitment); - } + + VerificationKey(CircuitBuilder* builder, const std::shared_ptr& native_key) + { + this->pcs_verification_key = native_key->pcs_verification_key; + this->circuit_size = native_key->circuit_size; + this->log_circuit_size = numeric::get_msb(this->circuit_size); + this->num_public_inputs = native_key->num_public_inputs; + this->pub_inputs_offset = native_key->pub_inputs_offset; + + for (auto [native_commitment, commitment] : zip_view(native_key->get_all(), this->get_all())) { + commitment = Commitment::from_witness(builder, native_commitment); } - }; + } + }; -<<<<<<< HEAD - /** - ======= - /** - >>>>>>> 927b78b2b6def848e5718b2df9e6a9695396b354 - * @brief A container for the witness commitments. - */ - using WitnessCommitments = ECCVMFlavor::WitnessEntities; - - using CommitmentLabels = ECCVMFlavor::CommitmentLabels; - // Reuse the VerifierCommitments from ECCVM - using VerifierCommitments = ECCVMFlavor::VerifierCommitments_; - // Reuse the transcript from ECCVM - using Transcript = bb::BaseTranscript>; - -<<<<<<< HEAD - }; // NOLINTEND(cppcoreguidelines-avoid-const-or-ref-data-members) -======= - }; // NOLINTEND(cppcoreguidelines-avoid-const-or-ref-data-members) ->>>>>>> 927b78b2b6def848e5718b2df9e6a9695396b354 + /** + * @brief A container for the witness commitments. + */ + using WitnessCommitments = ECCVMFlavor::WitnessEntities; + + using CommitmentLabels = ECCVMFlavor::CommitmentLabels; + // Reuse the VerifierCommitments from ECCVM + using VerifierCommitments = ECCVMFlavor::VerifierCommitments_; + // Reuse the transcript from ECCVM + using Transcript = bb::BaseTranscript>; + +}; // NOLINTEND(cppcoreguidelines-avoid-const-or-ref-data-members) } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_flavor.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_flavor.hpp index 34a3f4d001d..beb1a9f27c5 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_flavor.hpp @@ -845,13 +845,10 @@ class GoblinTranslatorFlavor { */ class VerificationKey : public VerificationKey_, VerifierCommitmentKey> { public: - std::vector public_inputs; - VerificationKey(const size_t circuit_size, const size_t num_public_inputs) : VerificationKey_(circuit_size, num_public_inputs) {} VerificationKey(const std::shared_ptr& proving_key) - : public_inputs(proving_key->public_inputs) { this->pcs_verification_key = std::make_shared(); this->circuit_size = proving_key->circuit_size; From e51273220fc73a2fd3137cc40cc7e2db3a441ca2 Mon Sep 17 00:00:00 2001 From: maramihali Date: Mon, 20 May 2024 11:07:28 +0000 Subject: [PATCH 05/40] stuff --- .../eccvm_recursive_verifier.cpp | 41 +------------------ 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp index cf9733598ab..58a3703b626 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp @@ -1,4 +1,3 @@ -<<<<<<< HEAD #include "./eccvm_recursive_verifier.hpp" #include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" @@ -7,15 +6,6 @@ namespace bb { template -======= -#include "./eccvm_verifier.hpp" -#include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" -#include "barretenberg/sumcheck/sumcheck.hpp" - -namespace bb { - - template ->>>>>>> 927b78b2b6def848e5718b2df9e6a9695396b354 ECCVMRecursiveVerifier_::ECCVMRecursiveVerifier_( Builder* builder, const std::shared_ptr& native_verifier_key) : key(std::make_shared(builder, native_verifier_key)) @@ -28,7 +18,6 @@ ECCVMRecursiveVerifier_::ECCVMRecursiveVerifier_( template std::array ECCVMRecursiveVerifier_::verify_proof(const HonkProof& proof) { -<<<<<<< HEAD using ZeroMorph = ZeroMorphVerifier_; @@ -37,25 +26,13 @@ std::array ECCVMRecursiveVerifier_::ve StdlibProof stdlib_proof = bb::convert_proof_to_witness(builder, proof); transcript = std::make_shared(stdlib_proof); -======= - using ZeroMorph = ZeroMorphVerifier_; - - RelationParameters relation_parameters; - transcript = std::make_shared(proof); ->>>>>>> 927b78b2b6def848e5718b2df9e6a9695396b354 VerifierCommitments commitments{ key }; CommitmentLabels commitment_labels; const auto circuit_size = transcript->template receive_from_prover("circuit_size"); - ASSERT(circuit_size == key->circuit_size); - -<<<<<<< HEAD - for (auto [comm, label] : zip_view(commitments.get_wires(), commitment_labels.get_wires())) { - comm = transcript->template receive_from_prover(label); - } ======= - for(auto [comm, label]: zip_view(commitments.get_wires(), commitment_labels.get_wires())) { + for (auto [comm, label] : zip_view(commitments.get_wires(), commitment_labels.get_wires())) { comm = transcript->template receive_from_prover(label); } @@ -64,11 +41,8 @@ std::array ECCVMRecursiveVerifier_::ve auto [beta, gamma] = transcript->template get_challenges("beta", "gamma"); // there is an issue somewhere to simplify this :-? -<<<<<<< HEAD auto beta_sqr = beta * beta; -======= ->>>>>>> 927b78b2b6def848e5718b2df9e6a9695396b354 relation_parameters.gamma = gamma; relation_parameters.beta = beta; relation_parameters.beta_sqr = beta * beta; @@ -79,14 +53,9 @@ std::array ECCVMRecursiveVerifier_::ve // these are the derived stuff only // Get commitment to permutation and lookup grand products -<<<<<<< HEAD commitments.lookup_inverses = transcript->template receive_from_prover(commitment_labels.lookup_inverses); commitments.z_perm = transcript->template receive_from_prover(commitment_labels.z_perm); -======= - commitments.lookup_inverses = receive_commitment(commitment_labels.lookup_inverses); - commitments.z_perm = receive_commitment(commitment_labels.z_perm); ->>>>>>> 927b78b2b6def848e5718b2df9e6a9695396b354 // Execute Sumcheck Verifier const size_t log_circuit_size = numeric::get_msb(circuit_size); @@ -116,11 +85,7 @@ std::array ECCVMRecursiveVerifier_::ve // TODO(#768): Find a better way to do this. See issue for details. bool univariate_opening_verified = false; { -<<<<<<< HEAD auto hack_commitment = transcript->template receive_from_prover("Translation:hack_commitment"); -======= - auto hack_commitment = receive_commitment("Translation:hack_commitment"); ->>>>>>> 927b78b2b6def848e5718b2df9e6a9695396b354 FF evaluation_challenge_x = transcript->template get_challenge("Translation:evaluation_challenge_x"); @@ -163,9 +128,5 @@ std::array ECCVMRecursiveVerifier_::ve } template class ECCVMRecursiveVerifier_>; -<<<<<<< HEAD // template class ECCVMRecursiveVerifier_>; -======= -template class ECCVMRecursiveVerifier_>; ->>>>>>> 927b78b2b6def848e5718b2df9e6a9695396b354 } // namespace bb From 1ea0798a1d0dbc537f8be9905336080b24f52819 Mon Sep 17 00:00:00 2001 From: maramihali Date: Tue, 21 May 2024 11:04:25 +0000 Subject: [PATCH 06/40] AAAAAAAAA --- .../commitment_schemes/ipa/ipa.hpp | 5 ++-- .../zeromorph/zeromorph.hpp | 2 +- .../scalar_multiplication/runtime_states.cpp | 7 ++++++ .../src/barretenberg/eccvm/eccvm_verifier.cpp | 5 ++-- .../eccvm_recursive_flavor.hpp | 6 +++-- .../eccvm_recursive_verifier.cpp | 10 +++----- .../eccvm_recursive_verifier.hpp | 3 +-- .../cpp/src/barretenberg/flavor/flavor.hpp | 1 + .../ultra_recursive_flavor.hpp | 25 +++++++++++++++---- 9 files changed, 41 insertions(+), 23 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index ca7872285c6..437642d56a3 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -333,10 +333,9 @@ template class IPA { *10. Compute \f$C_{right}=a_{0}G_{s}+a_{0}b_{0}U\f$ *11. Check that \f$C_{right} = C_0\f$. If they match, return true. Otherwise return false. */ - template static VerifierAccumulator reduce_verify_internal(const std::shared_ptr& vk, const OpeningClaim& opening_claim, - const std::shared_ptr& transcript) + auto& transcript) { // Step 1. // Receive polynomial_degree + 1 = d from the prover @@ -500,7 +499,7 @@ template class IPA { // implemented static VerifierAccumulator reduce_verify(const std::shared_ptr& vk, const OpeningClaim& opening_claim, - const std::shared_ptr& transcript) + const auto& transcript) { return reduce_verify_internal(vk, opening_claim, transcript); } diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp index 4811d32407a..f4b4064bd60 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp @@ -777,7 +777,7 @@ template class ZeroMorphVerifier_ { RefSpan unshifted_evaluations, RefSpan shifted_evaluations, std::span multivariate_challenge, - const std::shared_ptr>& vk, + auto& vk, auto& transcript, const std::vector>& concatenation_group_commitments = {}, RefSpan concatenated_evaluations = {}) diff --git a/barretenberg/cpp/src/barretenberg/ecc/scalar_multiplication/runtime_states.cpp b/barretenberg/cpp/src/barretenberg/ecc/scalar_multiplication/runtime_states.cpp index a86acbbf44e..e5d9a697c40 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/scalar_multiplication/runtime_states.cpp +++ b/barretenberg/cpp/src/barretenberg/ecc/scalar_multiplication/runtime_states.cpp @@ -4,6 +4,7 @@ #include "barretenberg/common/slab_allocator.hpp" #include "barretenberg/common/thread.hpp" #include "barretenberg/numeric/bitop/get_msb.hpp" +#include "barretenberg/stdlib/primitives/curves/bn254SimulatingGrumpkin.hpp" // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast) namespace bb::scalar_multiplication { @@ -204,8 +205,14 @@ template pippenger_runtime_state::~pippenger_runtime_sta template struct affine_product_runtime_state; template struct affine_product_runtime_state; +template struct affine_product_runtime_state>; +template struct affine_product_runtime_state>; + template struct pippenger_runtime_state; template struct pippenger_runtime_state; +template struct pippenger_runtime_state>; +template struct pippenger_runtime_state>; + } // namespace bb::scalar_multiplication // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp index f505786d20d..56a09c2ad89 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp @@ -1,4 +1,4 @@ -#include "./eccvm_recursive_verifier.hpp" +#include "./eccvm_verifier.hpp" #include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" @@ -13,8 +13,7 @@ bool ECCVMVerifier::verify_proof(const HonkProof& proof) RelationParameters relation_parameters; - StdlibProof stdlib_proof = bb::convert_proof_to_witness(builder, proof); - transcript = std::make_shared(stdlib_proof); + transcript = std::make_shared(proof); VerifierCommitments commitments{ key }; CommitmentLabels commitment_labels; diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp index e1b1d130e2e..a6358612728 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp @@ -34,11 +34,12 @@ template class ECCVMRecursiveFlavor_ { using GroupElement = typename Curve::Element; using Commitment = typename Curve::Element; using CommitmentKey = bb::CommitmentKey; - using VerifierCommitmentKey = bb::VerifierCommitmentKey; using RelationSeparator = FF; using NativeFlavor = ECCVMFlavor; using NativeVerificationKey = NativeFlavor::VerificationKey; + using VerifierCommitmentKey = bb::VerifierCommitmentKey; + static constexpr size_t NUM_WIRES = ECCVMFlavor::NUM_WIRES; // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. @@ -90,7 +91,8 @@ template class ECCVMRecursiveFlavor_ { * resolve that, and split out separate PrecomputedPolynomials/Commitments data for clarity but also for * portability of our circuits. */ - class VerificationKey : VerificationKey_, VerifierCommitmentKey> { + class VerificationKey + : public VerificationKey_, VerifierCommitmentKey> { public: VerificationKey(const size_t circuit_size, const size_t num_public_inputs) { diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp index 58a3703b626..332082533ff 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp @@ -15,8 +15,7 @@ ECCVMRecursiveVerifier_::ECCVMRecursiveVerifier_( /** * @brief This function verifies an ECCVM Honk proof for given program settings. */ -template -std::array ECCVMRecursiveVerifier_::verify_proof(const HonkProof& proof) +template bool ECCVMRecursiveVerifier_::verify_proof(const HonkProof& proof) { using ZeroMorph = ZeroMorphVerifier_; @@ -30,13 +29,10 @@ std::array ECCVMRecursiveVerifier_::ve CommitmentLabels commitment_labels; const auto circuit_size = transcript->template receive_from_prover("circuit_size"); - -======= for (auto [comm, label] : zip_view(commitments.get_wires(), commitment_labels.get_wires())) { comm = transcript->template receive_from_prover(label); } - ->>>>>>> 927b78b2b6def848e5718b2df9e6a9695396b354 + // Get challenge for sorted list batching and wire four memory records auto [beta, gamma] = transcript->template get_challenges("beta", "gamma"); @@ -49,7 +45,7 @@ std::array ECCVMRecursiveVerifier_::ve relation_parameters.beta_cube = beta_sqr * beta; relation_parameters.eccvm_set_permutation_delta = gamma * (gamma + beta_sqr) * (gamma + beta_sqr + beta_sqr) * (gamma + beta_sqr + beta_sqr + beta_sqr); - relation_parameters.eccvm_set_permutation_delta = relation_parameters.eccvm_set_permutation_delta.invert(); + relation_parameters.eccvm_set_permutation_delta = FF::one() / relation_parameters.eccvm_set_permutation_delta; // these are the derived stuff only // Get commitment to permutation and lookup grand products diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp index 07bdb1de924..9d37d32ca7c 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp @@ -14,13 +14,12 @@ template class ECCVMRecursiveVerifier_ { using PCS = typename Flavor::PCS; using Transcript = bb::BaseTranscript>; using GroupElement = typename Flavor::GroupElement; - using PairingPoints = std::array; using VerifierCommitments = typename Flavor::VerifierCommitments; // dunno if I need thos public: explicit ECCVMRecursiveVerifier_(Builder* builder, const std::shared_ptr& native_verifier_key); - PairingPoints verify_proof(const HonkProof& proof); + bool verify_proof(const HonkProof& proof); // return type?! std::shared_ptr key; std::map commitments; diff --git a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp index b3be9e6483f..f0228469040 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp @@ -176,6 +176,7 @@ class ProvingKeyAvm_ : public PrecomputedPolynomials, public WitnessPolynomials * @brief Base verification key class. * * @tparam PrecomputedEntities An instance of PrecomputedEntities_ with affine_element data type and handle type. + * @tparam VerifierCommitmentKey The PCS verification key (should this really be here??) */ template class VerificationKey_ : public PrecomputedCommitments { diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp index bf220475341..246c45150b1 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp @@ -55,11 +55,9 @@ template class UltraRecursiveFlavor_ { using FF = typename Curve::ScalarField; using NativeFlavor = UltraFlavor; using NativeVerificationKey = NativeFlavor::VerificationKey; + using NativeVerifierCommitmentKey = NativeFlavor::VerifierCommitmentKey - // Note(luke): Eventually this may not be needed at all - using VerifierCommitmentKey = bb::VerifierCommitmentKey; - - static constexpr size_t NUM_WIRES = UltraFlavor::NUM_WIRES; + static constexpr size_t NUM_WIRES = UltraFlavor::NUM_WIRES; // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. // Note: this number does not include the individual sorted list polynomials. @@ -262,6 +260,23 @@ template class UltraRecursiveFlavor_ { }; public: + class VerifierCrs : public bb::srs::factories::VerifierCrs { + public: + VerifierCrs(const std::shared_ptr>& native_verifier_crs) + { + num_points = native_verifier_crs->num_points; + native_points = native_verifier_crs->get_monomial_points(); + } + + private: + size_t num_points; + Curve::AffineElement first_g1; + std::shared_ptr monomials_; + } + // WORKTODO: Should this + class VerifierCommitmentKey : public bb::VerifierCommitmentKey { + VerifierCommitmentKey(const std::shared_ptr native_pcs_verification_key) {} + }; /** * @brief The verification key is responsible for storing the the commitments to the precomputed (non-witnessk) * polynomials used by the verifier. @@ -286,7 +301,7 @@ template class UltraRecursiveFlavor_ { */ VerificationKey(CircuitBuilder* builder, const std::shared_ptr& native_key) { - this->pcs_verification_key = native_key->pcs_verification_key; + this->pcs_verification_key = VerifierCommitmentKey(native_key->pcs_verification_key); this->circuit_size = native_key->circuit_size; this->log_circuit_size = numeric::get_msb(this->circuit_size); this->num_public_inputs = native_key->num_public_inputs; From d3a94e4b664b206f3ac878b6fe5c1953d0a2cfa2 Mon Sep 17 00:00:00 2001 From: maramihali Date: Tue, 21 May 2024 12:22:20 +0000 Subject: [PATCH 07/40] stuff --- .../eccvm_recursive_flavor.hpp | 11 ++--- .../curves/bn254SimulatingGrumpkin.hpp | 43 ------------------- 2 files changed, 6 insertions(+), 48 deletions(-) delete mode 100644 barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254SimulatingGrumpkin.hpp diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp index a6358612728..d1bc43cbd46 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp @@ -38,8 +38,6 @@ template class ECCVMRecursiveFlavor_ { using NativeFlavor = ECCVMFlavor; using NativeVerificationKey = NativeFlavor::VerificationKey; - using VerifierCommitmentKey = bb::VerifierCommitmentKey; - static constexpr size_t NUM_WIRES = ECCVMFlavor::NUM_WIRES; // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. @@ -69,7 +67,6 @@ template class ECCVMRecursiveFlavor_ { // Instantiate the BarycentricData needed to extend each Relation Univariate // define the containers for storing the contributions from each relation in Sumcheck - using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); public: @@ -83,12 +80,16 @@ template class ECCVMRecursiveFlavor_ { using Base::Base; }; + class VerifierCommitmentKey : public bb::VerifierCommitmentKey { + VerifierCommitmentKey(const std::shared_ptr native_pcs_verifiaction_key) {} + }; + /** * @brief The verification key is responsible for storing the the commitments to the precomputed (non-witnessk) * polynomials used by the verifier. * * @note Note the discrepancy with what sort of data is stored here vs in the proving key. We may want to - * resolve that, and split out separate PrecomputedPolynomials/Commitments data for clarity but also for + * resolve that, and split out separate PrecomputedPolynom ials/Commitments data for clarity but also for * portability of our circuits. */ class VerificationKey @@ -111,7 +112,7 @@ template class ECCVMRecursiveFlavor_ { VerificationKey(CircuitBuilder* builder, const std::shared_ptr& native_key) { - this->pcs_verification_key = native_key->pcs_verification_key; + this->pcs_verification_key = VerifierCommitmentKey(native_key->pcs_verification_key); this->circuit_size = native_key->circuit_size; this->log_circuit_size = numeric::get_msb(this->circuit_size); this->num_public_inputs = native_key->num_public_inputs; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254SimulatingGrumpkin.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254SimulatingGrumpkin.hpp deleted file mode 100644 index c11ea20566f..00000000000 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254SimulatingGrumpkin.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -#include "../bigfield/bigfield.hpp" -#include "../biggroup/biggroup.hpp" -#include "../field/field.hpp" -#include "barretenberg/ecc/curves/types.hpp" - -namespace bb::stdlib { - -template struct bn254SimulatingGrumpkin { - static constexpr bb::CurveType type = bb::CurveType::BN254; - // TODO(#673): This flag is temporary. It is needed in the verifier classes (GeminiVerifier, etc.) while these - // classes are instantiated with "native" curve types. Eventually, the verifier classes will be instantiated only - // with stdlib types, and "native" verification will be acheived via a simulated builder. - static constexpr bool is_stdlib_type = true; - - // Corresponding native types (used exclusively for testing) - using ScalarFieldNative = curve::Grumpkin::ScalarField; - using BaseFieldNative = curve::Grumpkin::BaseField; - using GroupNative = curve::Grumpkin::Group; - using ElementNative = GroupNative::element; - using AffineElementNative = GroupNative::affine_element; - - // Stdlib types corresponding to those defined in the native description of the curve. - // Note: its useful to have these type names match the native analog exactly so that components that digest a Curve - // (e.g. Gemini) can be agnostic as to whether they're operating on native or stdlib types. - using ScalarField = bigfield; - using BaseField = field_t; - using Group = element; // will Group native work? - using Element = Group; - using AffineElement = Group; - - // Additional types with no analog in the native description of the curve - using Builder = CircuitBuilder; - using witness_ct = witness_t; - using public_witness_ct = public_witness_t; - using byte_array_ct = byte_array; - using bool_ct = bool_t; - using uint32_ct = stdlib::uint32; - - using bigfr_ct = bigfield; - using g1_bigfr_ct = element; -}; -} // namespace bb::stdlib From 3a0d99f2cc4b7a2506b5a70f43c66dbffa3409e2 Mon Sep 17 00:00:00 2001 From: maramihali Date: Wed, 22 May 2024 09:13:24 +0000 Subject: [PATCH 08/40] stuff --- .../commitment_schemes/ipa/ipa.hpp | 127 +++++++++++++++++- .../commitment_schemes/verification_key.hpp | 2 + .../scalar_multiplication/runtime_states.cpp | 5 - .../eccvm_recursive_flavor.hpp | 17 ++- .../stdlib/primitives/curves/bn254.hpp | 3 +- 5 files changed, 141 insertions(+), 13 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index 437642d56a3..19a2fab9504 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -127,7 +127,7 @@ template class IPA { * 7. Compute \f$\vec{b}_{i-1}=\vec{b}_{i\_low}+u_{i-1}^{-1}\cdot \vec{b}_{i\_high}\f$​ * *7. Send the final \f$\vec{a}_{0} = (a_0)\f$ to the verifier - */ + */ template static void compute_opening_proof_internal(const std::shared_ptr& ck, const OpeningPair& opening_pair, @@ -336,6 +336,7 @@ template class IPA { static VerifierAccumulator reduce_verify_internal(const std::shared_ptr& vk, const OpeningClaim& opening_claim, auto& transcript) + requires(!Curve::is_stdlib_type) { // Step 1. // Receive polynomial_degree + 1 = d from the prover @@ -350,7 +351,8 @@ template class IPA { if (generator_challenge.is_zero()) { throw_or_abort("The generator challenge can't be zero"); } - auto aux_generator = Commitment::one() * generator_challenge; + + Commitment aux_generator = Commitment::one() * generator_challenge; auto log_poly_degree = static_cast(numeric::get_msb(poly_length)); // Step 3. @@ -370,7 +372,7 @@ template class IPA { auto element_L = transcript->template receive_from_prover("IPA:L_" + index); auto element_R = transcript->template receive_from_prover("IPA:R_" + index); round_challenges[i] = transcript->template get_challenge("IPA:round_challenge_" + index); - if (round_challenges[i].is_zero()) { + if (round_challenges[i].is_zero()) { // ??? throw_or_abort("Round challenges can't be zero"); } round_challenges_inv[i] = round_challenges[i].invert(); @@ -385,12 +387,14 @@ template class IPA { // Compute C₀ = C' + ∑_{j ∈ [k]} u_j^{-1}L_j + ∑_{j ∈ [k]} u_jR_j GroupElement LR_sums = bb::scalar_multiplication::pippenger_without_endomorphism_basis_points( &msm_scalars[0], &msm_elements[0], pippenger_size, vk->pippenger_runtime_state); + GroupElement C_zero = C_prime + LR_sums; // Step 6. // Compute b_zero where b_zero can be computed using the polynomial: // g(X) = ∏_{i ∈ [k]} (1 + u_{i-1}^{-1}.X^{2^{i-1}}). // b_zero = g(evaluation) = ∏_{i ∈ [k]} (1 + u_{i-1}^{-1}. (evaluation)^{2^{i-1}}) + Fr b_zero = Fr::one(); for (size_t i = 0; i < log_poly_degree; i++) { auto exponent = static_cast(Fr(2).pow(i)); @@ -448,7 +452,7 @@ template class IPA { // Step 8. // Compute G₀ - auto G_zero = bb::scalar_multiplication::pippenger_without_endomorphism_basis_points( + Commitment G_zero = bb::scalar_multiplication::pippenger_without_endomorphism_basis_points( &s_vec[0], &G_vec_local[0], poly_length, vk->pippenger_runtime_state); // Step 9. @@ -464,6 +468,121 @@ template class IPA { return (C_zero.normalize() == right_hand_side.normalize()); } + static VerifierAccumulator reduce_verify_internal(const std::shared_ptr& vk, + const OpeningClaim& opening_claim, + auto& transcript) + requires Curve::is_stdlib_type + { + // Step 1. + // Receive polynomial_degree + 1 = d from the prover + auto poly_length = static_cast(transcript->template receive_from_prover( + "IPA:poly_degree_plus_1")); // note this is base field because this is a uint32_t, which should map + // to a bb::fr, not a grumpkin::fr, which is a BaseField element for + // Grumpkin + // Step 2. + // Receive generator challenge u and compute auxiliary generator + const Fr generator_challenge = transcript->template get_challenge("IPA:generator_challenge"); + auto builder = generator_challenge.get_context(); + + if (generator_challenge.is_zero()) { + throw_or_abort("The generator challenge can't be zero"); + } + + Commitment aux_generator = Commitment::one(builder) * generator_challenge; + + auto log_poly_degree = static_cast(numeric::get_msb(poly_length)); + // Step 3. + // Compute C' = C + f(\beta) ⋅ U + GroupElement C_prime = opening_claim.commitment + (aux_generator * opening_claim.opening_pair.evaluation); + + auto pippenger_size = 2 * log_poly_degree; + std::vector round_challenges(log_poly_degree); + std::vector round_challenges_inv(log_poly_degree); + std::vector msm_elements(pippenger_size); + std::vector msm_scalars(pippenger_size); + + // Step 4. + // Receive all L_i and R_i and prepare for MSM + for (size_t i = 0; i < log_poly_degree; i++) { + std::string index = std::to_string(log_poly_degree - i - 1); + auto element_L = transcript->template receive_from_prover("IPA:L_" + index); + auto element_R = transcript->template receive_from_prover("IPA:R_" + index); + round_challenges[i] = transcript->template get_challenge("IPA:round_challenge_" + index); + if (round_challenges[i].is_zero()) { // ??? + throw_or_abort("Round challenges can't be zero"); + } + round_challenges_inv[i] = round_challenges[i].invert(); + + msm_elements[2 * i] = element_L; + msm_elements[2 * i + 1] = element_R; + msm_scalars[2 * i] = round_challenges_inv[i]; + msm_scalars[2 * i + 1] = round_challenges[i]; + } + + // Step 5. + // Compute C₀ = C' + ∑_{j ∈ [k]} u_j^{-1}L_j + ∑_{j ∈ [k]} u_jR_j + GroupElement LR_sums = GroupElement::batch_mul(msm_elements, msm_scalars); + + GroupElement C_zero = C_prime + LR_sums; + + // Step 6. + // Compute b_zero where b_zero can be computed using the polynomial: + // g(X) = ∏_{i ∈ [k]} (1 + u_{i-1}^{-1}.X^{2^{i-1}}). + // b_zero = g(evaluation) = ∏_{i ∈ [k]} (1 + u_{i-1}^{-1}. (evaluation)^{2^{i-1}}) + + Fr one = Fr::one(builder); + Fr b_zero = one; + for (size_t i = 0; i < log_poly_degree; i++) { + auto exponent = static_cast(Fr(2).pow(i)); + b_zero *= one + (round_challenges_inv[log_poly_degree - 1 - i] * + opening_claim.opening_pair.challenge.pow(exponent)); + } + + // Step 7. + // Construct vector s + std::vector s_vec(poly_length); + + for (size_t i = 0; i < poly_length; i++) { + Fr s_vec_scalar = one(); + for (size_t j = (log_poly_degree - 1); j != size_t(-1); j--) { + auto bit = (i >> j) & 1; + bool b = static_cast(bit); + if (b) { + s_vec_scalar *= round_challenges_inv[log_poly_degree - 1 - j]; + } + } + s_vec[i] = s_vec_scalar; + } + + auto* srs_elements = vk->get_monomial_points(); + + // Copy the G_vector to local memory. + std::vector G_vec_local(poly_length); + + // The SRS stored in the commitment key is the result after applying the pippenger point table so the + // values at odd indices contain the point {srs[i-1].x * beta, srs[i-1].y}, where beta is the endomorphism + // G_vec_local should use only the original SRS thus we extract only the even indices. + for (size_t i = 0; i < poly_length * 2; i += 2) { + G_vec_local[i >> 1] = srs_elements[i]; + } + + // Step 8. + // Compute G₀ + Commitment G_zero = Commitment::batch_mul(G_vec_local, s_vec); + + // Step 9. + // Receive a₀ from the prover + auto a_zero = transcript->template receive_from_prover("IPA:a_0"); + + // Step 10. + // Compute C_right + GroupElement right_hand_side = G_zero * a_zero + aux_generator * a_zero * b_zero; + + // Step 11. + // Check if C_right == C₀ + return (C_zero.normalize() == right_hand_side.normalize()); + } + public: /** * @brief Compute an inner product argument proof for opening a single polynomial at a single evaluation point. diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/verification_key.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/verification_key.hpp index 17511fe448f..b147f29934b 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/verification_key.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/verification_key.hpp @@ -96,4 +96,6 @@ template <> class VerifierCommitmentKey { std::shared_ptr> srs; }; +// put the verifier commitment key here + } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/ecc/scalar_multiplication/runtime_states.cpp b/barretenberg/cpp/src/barretenberg/ecc/scalar_multiplication/runtime_states.cpp index e5d9a697c40..2c5c7f4cd30 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/scalar_multiplication/runtime_states.cpp +++ b/barretenberg/cpp/src/barretenberg/ecc/scalar_multiplication/runtime_states.cpp @@ -205,14 +205,9 @@ template pippenger_runtime_state::~pippenger_runtime_sta template struct affine_product_runtime_state; template struct affine_product_runtime_state; -template struct affine_product_runtime_state>; -template struct affine_product_runtime_state>; template struct pippenger_runtime_state; template struct pippenger_runtime_state; -template struct pippenger_runtime_state>; -template struct pippenger_runtime_state>; - } // namespace bb::scalar_multiplication // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp index d1bc43cbd46..8c3e3e9c23b 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp @@ -14,7 +14,7 @@ #include "barretenberg/relations/ecc_vm/ecc_wnaf_relation.hpp" #include "barretenberg/relations/relation_parameters.hpp" #include "barretenberg/stdlib/honk_recursion/transcript/transcript.hpp" -#include "barretenberg/stdlib/primitives/curves/bn254SimulatingGrumpkin.hpp" +#include "barretenberg/stdlib/primitives/curves/bn254.hpp" // NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members) ? @@ -27,7 +27,7 @@ template class ECCVMRecursiveFlavor_ { // I need to somehow make this bn254 hashtag confusion // the native stuff becomes nonnative and the nonnative stuff becomes native and everything is in circuit land is // probably what's going on - using Curve = stdlib::bn254SimulatingGrumpkin; + using Curve = stdlib::bn254; using G1 = typename Curve::Group; using PCS = IPA; using FF = typename Curve::ScalarField; @@ -81,7 +81,18 @@ template class ECCVMRecursiveFlavor_ { }; class VerifierCommitmentKey : public bb::VerifierCommitmentKey { - VerifierCommitmentKey(const std::shared_ptr native_pcs_verifiaction_key) {} + public: + Commitment first_g1; + std::vector monomial_points; + + VerifierCommitmentKey(const std::shared_ptr native_pcs_verifiaction_key) + { + first_g1 = Commitment(native_pcs_verifiaction_key->srs->get_monomial_points()); + native_monomial_points = native_pcs_verifiaction_key->srs->get_monomial_points(); + for (auto comm : native_monomial_points) { + monomial_points.emplace_back(Commitment(native_monomial_points)); + } + } }; /** diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp index d437cee5044..2c6a82bf1cb 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp @@ -26,8 +26,9 @@ template struct bn254 { using ScalarField = field_t; using BaseField = bigfield; using Group = element; - using Element = Group; using AffineElement = Group; + // We dont need the projective form inside a circuit because + using Element = Group; // Additional types with no analog in the native description of the curve using Builder = CircuitBuilder; From 79e448aa61df6022499ff1ee419983f3211cf2f7 Mon Sep 17 00:00:00 2001 From: maramihali Date: Wed, 22 May 2024 12:19:46 +0000 Subject: [PATCH 09/40] implementation and tests --- barretenberg/cpp/src/CMakeLists.txt | 1 + .../barretenberg/commitment_schemes/ipa/ipa.hpp | 2 +- .../commitment_schemes/shplonk/shplonk.hpp | 2 +- .../commitment_schemes/verification_key.hpp | 15 +++++++++++---- .../commitment_schemes/zeromorph/zeromorph.hpp | 2 +- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/barretenberg/cpp/src/CMakeLists.txt b/barretenberg/cpp/src/CMakeLists.txt index a9809f12c61..715323bc13f 100644 --- a/barretenberg/cpp/src/CMakeLists.txt +++ b/barretenberg/cpp/src/CMakeLists.txt @@ -61,6 +61,7 @@ add_subdirectory(barretenberg/crypto) add_subdirectory(barretenberg/dsl) add_subdirectory(barretenberg/ecc) add_subdirectory(barretenberg/eccvm) +add_subdirectory(barretenberg/eccvm_recursion) add_subdirectory(barretenberg/env) add_subdirectory(barretenberg/execution_trace) add_subdirectory(barretenberg/examples) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index ca7872285c6..a6720608c81 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -424,7 +424,7 @@ template class IPA { /*finite_field_additions_per_iteration=*/0, /*finite_field_multiplications_per_iteration=*/log_poly_degree); - auto* srs_elements = vk->srs->get_monomial_points(); + auto* srs_elements = vk->get_monomial_points(); // Copy the G_vector to local memory. std::vector G_vec_local(poly_length); diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp index fb08f81d889..74b3b500e79 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp @@ -264,7 +264,7 @@ template class ShplonkVerifier_ { } // [G] += G₀⋅[1] = [G] + (∑ⱼ ρʲ ⋅ vⱼ / ( r − xⱼ ))⋅[1] - G_commitment += vk->srs->get_first_g1() * G_commitment_constant; + G_commitment += vk->get_first_g1() * G_commitment_constant; } // Return opening pair (z, 0) and commitment [G] diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/verification_key.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/verification_key.hpp index 17511fe448f..42fac7a1fab 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/verification_key.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/verification_key.hpp @@ -34,14 +34,14 @@ template <> class VerifierCommitmentKey { using Commitment = typename Curve::AffineElement; public: - std::shared_ptr> srs; - VerifierCommitmentKey() { srs::init_crs_factory("../srs_db/ignition"); srs = srs::get_crs_factory()->get_verifier_crs(); }; + Commitment get_first_g1() { return srs->get_first_g1(); } + /** * @brief verifies a pairing equation over 2 points using the verifier SRS * @@ -58,6 +58,9 @@ template <> class VerifierCommitmentKey { return (result == Curve::TargetField::one()); } + + private: + std::shared_ptr> srs; }; /** @@ -71,8 +74,6 @@ template <> class VerifierCommitmentKey { using Commitment = typename Curve::AffineElement; public: - VerifierCommitmentKey() = delete; - /** * @brief Construct a new IPA Verification Key object from existing SRS * @@ -92,7 +93,13 @@ template <> class VerifierCommitmentKey { srs = srs::get_crs_factory()->get_verifier_crs(num_points); } + Commitment get_first_g1() { return srs->get_first_g1(); } + + Commitment* get_monomial_points() { return srs->get_monomial_points(); } + bb::scalar_multiplication::pippenger_runtime_state pippenger_runtime_state; + + private: std::shared_ptr> srs; }; diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp index 53c52675952..cef5e7ebf43 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp @@ -788,7 +788,7 @@ template class ZeroMorphVerifier_ { auto builder = multivariate_challenge[0].get_context(); first_g1 = Commitment(builder, vk->srs->get_first_g1()); } else { - first_g1 = vk->srs->get_first_g1(); + first_g1 = vk->get_first_g1(); } auto opening_claim = compute_univariate_evaluation_opening_claim(unshifted_commitments, to_be_shifted_commitments, From 038ca2ca8647f920c3d6198a30f1fbbec54a7f00 Mon Sep 17 00:00:00 2001 From: maramihali Date: Wed, 22 May 2024 12:28:05 +0000 Subject: [PATCH 10/40] actuall add the code --- .../eccvm_recursion/CMakeLists.txt | 1 + .../verifier_commitment_key.hpp | 42 +++++++++++++++++++ .../verifier_commitment_key.test.cpp | 42 +++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/CMakeLists.txt create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.hpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.test.cpp diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/eccvm_recursion/CMakeLists.txt new file mode 100644 index 00000000000..fdb95c77ac3 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/CMakeLists.txt @@ -0,0 +1 @@ +barretenberg_module(eccvm_recursion stdlib_circuit_builders commitment_schemes stdlib_primitives) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.hpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.hpp new file mode 100644 index 00000000000..ff981670ef7 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.hpp @@ -0,0 +1,42 @@ +#pragma once +#include "barretenberg/commitment_schemes/verification_key.hpp" +#include "barretenberg/stdlib/primitives/curves/bn254.hpp" +#include "barretenberg/stdlib/primitives/group/cycle_group.hpp" +namespace bb { + +/** + * @brief Representation of the Grumpkin Verifier Commitment Key inside a bn254 circuit + * + * @tparam Builder + */ +template class VerifierCommitmentKey> { + using Commitment = stdlib::cycle_group; + + public: + /** + * @brief Construct a new Verifier Commitment Key object from its native counterpart. instantiated on Grumpkin. This + * will potentially be part of the ECCVMRecursiveFlavor once implemented. + * + * @details The Grumpkin SRS points will be initialised as constants in the circuit but might be subsequently turned + * into constant witnesses to make operations in the circuit more efficient. + */ + VerifierCommitmentKey([[maybe_unused]] Builder* builder, + size_t num_points, + std::shared_ptr>& native_pcs_verification_key) + : first_g1(Commitment(native_pcs_verification_key->get_first_g1())) + { + + auto* native_points = native_pcs_verification_key->get_monomial_points(); + for (size_t i = 0; i < num_points; i++) { + monomial_points.emplace_back(Commitment(native_points[i])); + } + } + + Commitment get_first_g1() { return first_g1; } + std::vector get_monomial_points() { return monomial_points; } + + private: + Commitment first_g1; + std::vector monomial_points; +}; +} // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.test.cpp new file mode 100644 index 00000000000..a6c3bb59130 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.test.cpp @@ -0,0 +1,42 @@ + +#include "barretenberg/eccvm_recursion/verifier_commitment_key.hpp" +#include +namespace bb { +template class RecursiveVeriferCommitmentKeyTest : public testing::Test { + public: + using native_VK = VerifierCommitmentKey; + using VK = VerifierCommitmentKey>; + static void SetUpTestSuite() + { + srs::init_crs_factory("../srs_db/ignition"); + srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); + } + + /** + * @brief Instantiante a recursive verifier commitment key from a Grumpkin native key and check consistency. + * + */ + static void test_equality() + { + size_t num_points = 4096; + Builder builder; + auto native_vk = std::make_shared(num_points); + auto recursive_vk = std::make_shared(&builder, num_points, native_vk); + EXPECT_EQ(native_vk->get_first_g1(), recursive_vk->get_first_g1().get_value()); + auto* native_monomial_points = native_vk->get_monomial_points(); + auto recursive_monomial_ponts = recursive_vk->get_monomial_points(); + for (size_t i = 0; i < num_points; i++) { + EXPECT_EQ(native_monomial_points[i], recursive_monomial_ponts[i].get_value()); + } + } +}; + +using Builders = testing::Types; + +TYPED_TEST_SUITE(RecursiveVeriferCommitmentKeyTest, Builders); + +TYPED_TEST(RecursiveVeriferCommitmentKeyTest, EqualityTest) +{ + TestFixture::test_equality(); +}; +} // namespace bb \ No newline at end of file From 1b127841176ae9b551c0621c19096b9eb34d9452 Mon Sep 17 00:00:00 2001 From: maramihali Date: Wed, 22 May 2024 13:34:09 +0000 Subject: [PATCH 11/40] fix typo --- .../eccvm_recursion/verifier_commitment_key.test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.test.cpp index a6c3bb59130..bca37112b53 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.test.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.test.cpp @@ -24,9 +24,9 @@ template class RecursiveVeriferCommitmentKeyTest : public tes auto recursive_vk = std::make_shared(&builder, num_points, native_vk); EXPECT_EQ(native_vk->get_first_g1(), recursive_vk->get_first_g1().get_value()); auto* native_monomial_points = native_vk->get_monomial_points(); - auto recursive_monomial_ponts = recursive_vk->get_monomial_points(); + auto recursive_monomial_points = recursive_vk->get_monomial_points(); for (size_t i = 0; i < num_points; i++) { - EXPECT_EQ(native_monomial_points[i], recursive_monomial_ponts[i].get_value()); + EXPECT_EQ(native_monomial_points[i], recursive_monomial_points[i].get_value()); } } }; From b685b099de26024b97b900dfa534b653057ec418 Mon Sep 17 00:00:00 2001 From: maramihali Date: Wed, 22 May 2024 14:50:27 +0000 Subject: [PATCH 12/40] another thingy --- .../eccvm_recursion/verifier_commitment_key.hpp | 14 ++++++++------ .../verifier_commitment_key.test.cpp | 12 +++++++----- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.hpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.hpp index ff981670ef7..66331ed20c8 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.hpp @@ -9,20 +9,22 @@ namespace bb { * * @tparam Builder */ -template class VerifierCommitmentKey> { +template class VerifierCommitmentKey { + using Builder = Curve::Builder; using Commitment = stdlib::cycle_group; + using NativeEmbeddedCurve = typename Builder::EmbeddedCurve; public: /** - * @brief Construct a new Verifier Commitment Key object from its native counterpart. instantiated on Grumpkin. This - * will potentially be part of the ECCVMRecursiveFlavor once implemented. + * @brief Construct a new Verifier Commitment Key object from its native counterpart. instantiated on Grumpkin. + * This will potentially be part of the ECCVMRecursiveFlavor once implemented. * - * @details The Grumpkin SRS points will be initialised as constants in the circuit but might be subsequently turned - * into constant witnesses to make operations in the circuit more efficient. + * @details The Grumpkin SRS points will be initialised as constants in the circuit but might be subsequently + * turned into constant witnesses to make operations in the circuit more efficient. */ VerifierCommitmentKey([[maybe_unused]] Builder* builder, size_t num_points, - std::shared_ptr>& native_pcs_verification_key) + std::shared_ptr>& native_pcs_verification_key) : first_g1(Commitment(native_pcs_verification_key->get_first_g1())) { diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.test.cpp index bca37112b53..7066b1f9f1c 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.test.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/verifier_commitment_key.test.cpp @@ -2,10 +2,12 @@ #include "barretenberg/eccvm_recursion/verifier_commitment_key.hpp" #include namespace bb { -template class RecursiveVeriferCommitmentKeyTest : public testing::Test { +template class RecursiveVeriferCommitmentKeyTest : public testing::Test { public: - using native_VK = VerifierCommitmentKey; - using VK = VerifierCommitmentKey>; + using Builder = typename Curve::Builder; + using NativeEmbeddedCurve = Builder::EmbeddedCurve; + using native_VK = VerifierCommitmentKey; + using VK = VerifierCommitmentKey; static void SetUpTestSuite() { srs::init_crs_factory("../srs_db/ignition"); @@ -31,9 +33,9 @@ template class RecursiveVeriferCommitmentKeyTest : public tes } }; -using Builders = testing::Types; +using Curves = testing::Types, stdlib::bn254>; -TYPED_TEST_SUITE(RecursiveVeriferCommitmentKeyTest, Builders); +TYPED_TEST_SUITE(RecursiveVeriferCommitmentKeyTest, Curves); TYPED_TEST(RecursiveVeriferCommitmentKeyTest, EqualityTest) { From f923145e538df6ab54d88b4ff259c03bab17fbc4 Mon Sep 17 00:00:00 2001 From: maramihali Date: Wed, 22 May 2024 15:11:52 +0000 Subject: [PATCH 13/40] aaaa --- .../eccvm_recursive_flavor.hpp | 24 ++++--------------- .../eccvm_recursive_verifier.hpp | 1 - .../stdlib/primitives/curves/bn254.hpp | 2 +- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp index 8c3e3e9c23b..ef360de7ed2 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp @@ -2,6 +2,7 @@ #include "barretenberg/commitment_schemes/ipa/ipa.hpp" #include "barretenberg/common/std_array.hpp" #include "barretenberg/eccvm/eccvm_flavor.hpp" +#include "barretenberg/eccvm_recursion/verifier_commitment_key.hpp" #include "barretenberg/flavor/flavor.hpp" #include "barretenberg/flavor/flavor_macros.hpp" #include "barretenberg/flavor/relation_definitions.hpp" @@ -28,12 +29,9 @@ template class ECCVMRecursiveFlavor_ { // the native stuff becomes nonnative and the nonnative stuff becomes native and everything is in circuit land is // probably what's going on using Curve = stdlib::bn254; - using G1 = typename Curve::Group; using PCS = IPA; - using FF = typename Curve::ScalarField; - using GroupElement = typename Curve::Element; - using Commitment = typename Curve::Element; - using CommitmentKey = bb::CommitmentKey; + using Commitment = typename stdlib::cycle_group; + using FF = typename bb::stdlib::cycle_group::cycle_scalar; using RelationSeparator = FF; using NativeFlavor = ECCVMFlavor; using NativeVerificationKey = NativeFlavor::VerificationKey; @@ -80,21 +78,7 @@ template class ECCVMRecursiveFlavor_ { using Base::Base; }; - class VerifierCommitmentKey : public bb::VerifierCommitmentKey { - public: - Commitment first_g1; - std::vector monomial_points; - - VerifierCommitmentKey(const std::shared_ptr native_pcs_verifiaction_key) - { - first_g1 = Commitment(native_pcs_verifiaction_key->srs->get_monomial_points()); - native_monomial_points = native_pcs_verifiaction_key->srs->get_monomial_points(); - for (auto comm : native_monomial_points) { - monomial_points.emplace_back(Commitment(native_monomial_points)); - } - } - }; - + using VerifierCommitmentKey = VerifierCommitmentKey; /** * @brief The verification key is responsible for storing the the commitments to the precomputed (non-witnessk) * polynomials used by the verifier. diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp index 9d37d32ca7c..18e6f50d1b5 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp @@ -13,7 +13,6 @@ template class ECCVMRecursiveVerifier_ { using Builder = typename Flavor::CircuitBuilder; using PCS = typename Flavor::PCS; using Transcript = bb::BaseTranscript>; - using GroupElement = typename Flavor::GroupElement; using VerifierCommitments = typename Flavor::VerifierCommitments; // dunno if I need thos public: explicit ECCVMRecursiveVerifier_(Builder* builder, diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp index 2c6a82bf1cb..a2f9b5f5932 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp @@ -6,7 +6,7 @@ namespace bb::stdlib { -template struct bn254 { +template struct bn254 { static constexpr bb::CurveType type = bb::CurveType::BN254; // TODO(#673): This flag is temporary. It is needed in the verifier classes (GeminiVerifier, etc.) while these // classes are instantiated with "native" curve types. Eventually, the verifier classes will be instantiated only From c4a5d0a3045f172c510cf0a4f48a004bca582135 Mon Sep 17 00:00:00 2001 From: maramihali Date: Wed, 22 May 2024 19:11:24 +0000 Subject: [PATCH 14/40] more chaos --- .../zeromorph/zeromorph.hpp | 2 +- .../zeromorph/zeromorph.test.cpp | 2 +- .../cpp/src/barretenberg/ecc/fields/field.hpp | 1 + .../scalar_multiplication/runtime_states.cpp | 1 - .../eccvm_recursive_flavor.hpp | 10 +- .../eccvm_recursive_verifier.cpp | 104 +++++++++--------- .../eccvm_recursive_verifier.hpp | 1 + .../stdlib/primitives/curves/bn254.hpp | 43 +++++++- .../primitives/field/field_conversion.hpp | 3 +- .../stdlib/primitives/group/cycle_group.hpp | 2 +- .../ultra_recursive_flavor.hpp | 27 +---- 11 files changed, 107 insertions(+), 89 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp index 04eabdb141d..bdf14ca3a96 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp @@ -777,7 +777,7 @@ template class ZeroMorphVerifier_ { RefSpan unshifted_evaluations, RefSpan shifted_evaluations, std::span multivariate_challenge, - auto& vk, + const std::shared_ptr>& vk, auto& transcript, const std::vector>& concatenation_group_commitments = {}, RefSpan concatenated_evaluations = {}) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.test.cpp index 3fcb56aa3af..ee6a5d58a46 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.test.cpp @@ -106,7 +106,7 @@ template class ZeroMorphTest : public CommitmentTest class ECCVMRecursiveFlavor_ { // I need to somehow make this bn254 hashtag confusion // the native stuff becomes nonnative and the nonnative stuff becomes native and everything is in circuit land is // probably what's going on - using Curve = stdlib::bn254; + using Curve = stdlib::bn254; using PCS = IPA; - using Commitment = typename stdlib::cycle_group; - using FF = typename bb::stdlib::cycle_group::cycle_scalar; + using Commitment = typename Curve::AffineElement; + using FF = typename Curve::ScalarField; + using BF = typename Curve::BaseField; using RelationSeparator = FF; using NativeFlavor = ECCVMFlavor; using NativeVerificationKey = NativeFlavor::VerificationKey; @@ -107,7 +108,8 @@ template class ECCVMRecursiveFlavor_ { VerificationKey(CircuitBuilder* builder, const std::shared_ptr& native_key) { - this->pcs_verification_key = VerifierCommitmentKey(native_key->pcs_verification_key); + this->pcs_verification_key = std::make_shared( + builder, native_key->circuit_size, native_key->pcs_verification_key); this->circuit_size = native_key->circuit_size; this->log_circuit_size = numeric::get_msb(this->circuit_size); this->num_public_inputs = native_key->num_public_inputs; diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp index 332082533ff..5f4d279f2de 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp @@ -18,7 +18,7 @@ ECCVMRecursiveVerifier_::ECCVMRecursiveVerifier_( template bool ECCVMRecursiveVerifier_::verify_proof(const HonkProof& proof) { - using ZeroMorph = ZeroMorphVerifier_; + // using ZeroMorph = ZeroMorphVerifier_; RelationParameters relation_parameters; @@ -28,7 +28,7 @@ template bool ECCVMRecursiveVerifier_::verify_proof(co VerifierCommitments commitments{ key }; CommitmentLabels commitment_labels; - const auto circuit_size = transcript->template receive_from_prover("circuit_size"); + const auto circuit_size = transcript->template receive_from_prover("circuit_size"); for (auto [comm, label] : zip_view(commitments.get_wires(), commitment_labels.get_wires())) { comm = transcript->template receive_from_prover(label); } @@ -54,7 +54,7 @@ template bool ECCVMRecursiveVerifier_::verify_proof(co commitments.z_perm = transcript->template receive_from_prover(commitment_labels.z_perm); // Execute Sumcheck Verifier - const size_t log_circuit_size = numeric::get_msb(circuit_size); + const size_t log_circuit_size = numeric::get_msb(static_cast(circuit_size.get_value())); auto sumcheck = SumcheckVerifier(log_circuit_size, transcript); FF alpha = transcript->template get_challenge("Sumcheck:alpha"); std::vector gate_challenges(static_cast(numeric::get_msb(key->circuit_size))); @@ -70,57 +70,57 @@ template bool ECCVMRecursiveVerifier_::verify_proof(co return false; } - bool multivariate_opening_verified = ZeroMorph::verify(commitments.get_unshifted(), - commitments.get_to_be_shifted(), - claimed_evaluations.get_unshifted(), - claimed_evaluations.get_shifted(), - multivariate_challenge, - key->pcs_verification_key, - transcript); + // bool multivariate_opening_verified = ZeroMorph::verify(commitments.get_unshifted(), + // commitments.get_to_be_shifted(), + // claimed_evaluations.get_unshifted(), + // claimed_evaluations.get_shifted(), + // multivariate_challenge, + // key->pcs_verification_key, + // transcript); // Execute transcript consistency univariate opening round // TODO(#768): Find a better way to do this. See issue for details. - bool univariate_opening_verified = false; - { - auto hack_commitment = transcript->template receive_from_prover("Translation:hack_commitment"); - - FF evaluation_challenge_x = transcript->template get_challenge("Translation:evaluation_challenge_x"); - - // Construct arrays of commitments and evaluations to be batched - const size_t NUM_UNIVARIATES = 6; - std::array transcript_commitments = { - commitments.transcript_op, commitments.transcript_Px, commitments.transcript_Py, - commitments.transcript_z1, commitments.transcript_z2, hack_commitment - }; - std::array transcript_evaluations = { - transcript->template receive_from_prover("Translation:op"), - transcript->template receive_from_prover("Translation:Px"), - transcript->template receive_from_prover("Translation:Py"), - transcript->template receive_from_prover("Translation:z1"), - transcript->template receive_from_prover("Translation:z2"), - transcript->template receive_from_prover("Translation:hack_evaluation") - }; - - // Get another challenge for batching the univariate claims - FF ipa_batching_challenge = transcript->template get_challenge("Translation:ipa_batching_challenge"); - - // Construct batched commitment and batched evaluation - auto batched_commitment = transcript_commitments[0]; - auto batched_transcript_eval = transcript_evaluations[0]; - auto batching_scalar = ipa_batching_challenge; - for (size_t idx = 1; idx < transcript_commitments.size(); ++idx) { - batched_commitment = batched_commitment + transcript_commitments[idx] * batching_scalar; - batched_transcript_eval += batching_scalar * transcript_evaluations[idx]; - batching_scalar *= ipa_batching_challenge; - } - - // Construct and verify batched opening claim - OpeningClaim batched_univariate_claim = { { evaluation_challenge_x, batched_transcript_eval }, - batched_commitment }; - univariate_opening_verified = - PCS::reduce_verify(key->pcs_verification_key, batched_univariate_claim, transcript); - } - - return sumcheck_verified.value() && multivariate_opening_verified && univariate_opening_verified; + // bool univariate_opening_verified = false; + // { + // auto hack_commitment = transcript->template receive_from_prover("Translation:hack_commitment"); + + // FF evaluation_challenge_x = transcript->template get_challenge("Translation:evaluation_challenge_x"); + + // // Construct arrays of commitments and evaluations to be batched + // const size_t NUM_UNIVARIATES = 6; + // std::array transcript_commitments = { + // commitments.transcript_op, commitments.transcript_Px, commitments.transcript_Py, + // commitments.transcript_z1, commitments.transcript_z2, hack_commitment + // }; + // std::array transcript_evaluations = { + // transcript->template receive_from_prover("Translation:op"), + // transcript->template receive_from_prover("Translation:Px"), + // transcript->template receive_from_prover("Translation:Py"), + // transcript->template receive_from_prover("Translation:z1"), + // transcript->template receive_from_prover("Translation:z2"), + // transcript->template receive_from_prover("Translation:hack_evaluation") + // }; + + // // Get another challenge for batching the univariate claims + // FF ipa_batching_challenge = transcript->template get_challenge("Translation:ipa_batching_challenge"); + + // // Construct batched commitment and batched evaluation + // auto batched_commitment = transcript_commitments[0]; + // auto batched_transcript_eval = transcript_evaluations[0]; + // auto batching_scalar = ipa_batching_challenge; + // for (size_t idx = 1; idx < transcript_commitments.size(); ++idx) { + // batched_commitment = batched_commitment + transcript_commitments[idx] * batching_scalar; + // batched_transcript_eval += batching_scalar * transcript_evaluations[idx]; + // batching_scalar *= ipa_batching_challenge; + // } + + // // Construct and verify batched opening claim + // OpeningClaim batched_univariate_claim = { { evaluation_challenge_x, batched_transcript_eval }, + // batched_commitment }; + // univariate_opening_verified = + // PCS::reduce_verify(key->pcs_verification_key, batched_univariate_claim, transcript); + // } + + return sumcheck_verified.value() /* && multivariate_opening_verified && univariate_opening_verified*/; } template class ECCVMRecursiveVerifier_>; diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp index 18e6f50d1b5..ee531de0c59 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp @@ -4,6 +4,7 @@ namespace bb { template class ECCVMRecursiveVerifier_ { using FF = typename Flavor::FF; + using BF = typename Flavor::BF; using Curve = typename Flavor::Curve; using Commitment = typename Flavor::Commitment; using CommitmentLabels = typename Flavor::CommitmentLabels; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp index a2f9b5f5932..5b8843671ec 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp @@ -3,16 +3,27 @@ #include "../biggroup/biggroup.hpp" #include "../field/field.hpp" #include "barretenberg/ecc/curves/types.hpp" +#include "barretenberg/stdlib/primitives/group/cycle_group.hpp" namespace bb::stdlib { -template struct bn254 { +template struct bn254; +template struct bn254 { + static constexpr bb::CurveType type = bb::CurveType::BN254; // TODO(#673): This flag is temporary. It is needed in the verifier classes (GeminiVerifier, etc.) while these // classes are instantiated with "native" curve types. Eventually, the verifier classes will be instantiated only // with stdlib types, and "native" verification will be acheived via a simulated builder. static constexpr bool is_stdlib_type = true; + // Additional types with no analog in the native description of the curve + using Builder = CircuitBuilder; + using witness_ct = witness_t; + using public_witness_ct = public_witness_t; + using byte_array_ct = byte_array; + using bool_ct = bool_t; + using uint32_ct = stdlib::uint32; + // Corresponding native types (used exclusively for testing) using ScalarFieldNative = curve::BN254::ScalarField; using BaseFieldNative = curve::BN254::BaseField; @@ -21,8 +32,8 @@ template struct bn254 { using AffineElementNative = GroupNative::affine_element; // Stdlib types corresponding to those defined in the native description of the curve. - // Note: its useful to have these type names match the native analog exactly so that components that digest a Curve - // (e.g. Gemini) can be agnostic as to whether they're operating on native or stdlib types. + // Note: its useful to have these type names match the native analog exactly so that components that digest a + // Curve (e.g. Gemini) can be agnostic as to whether they're operating on native or stdlib types. using ScalarField = field_t; using BaseField = bigfield; using Group = element; @@ -30,6 +41,17 @@ template struct bn254 { // We dont need the projective form inside a circuit because using Element = Group; + using bigfr_ct = bigfield; + using g1_bigfr_ct = element; +}; + +template struct bn254 { + static constexpr bb::CurveType type = bb::CurveType::BN254; + // TODO(#673): This flag is temporary. It is needed in the verifier classes (GeminiVerifier, etc.) while these + // classes are instantiated with "native" curve types. Eventually, the verifier classes will be instantiated only + // with stdlib types, and "native" verification will be acheived via a simulated builder. + static constexpr bool is_stdlib_type = true; + // Additional types with no analog in the native description of the curve using Builder = CircuitBuilder; using witness_ct = witness_t; @@ -38,8 +60,17 @@ template struct bn254 { using bool_ct = bool_t; using uint32_ct = stdlib::uint32; - using bigfr_ct = bigfield; - using g1_bigfr_ct = element; + // Stdlib types corresponding to those defined in the native description of the curve. + // Note: its useful to have these type names match the native analog exactly so that components that digest a + // Curve (e.g. Gemini) can be agnostic as to whether they're operating on native or stdlib types. + using Group = cycle_group; + using ScalarField = bigfield; + using BaseField = field_t; + using AffineElement = Group; + // We dont need the projective form inside a circuit because + using Element = Group; + + // hopefully we don't need the rest +}; -}; // namespace bn254 } // namespace bb::stdlib diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_conversion.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_conversion.hpp index 0cc9f98ba58..45fd133cb96 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_conversion.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_conversion.hpp @@ -12,8 +12,7 @@ template using fr = field_t; template using fq = bigfield; template using bn254_element = element, fr, curve::BN254::Group>; template using grumpkin_element = cycle_group; - -static constexpr uint64_t NUM_LIMB_BITS = NUM_LIMB_BITS_IN_FIELD_SIMULATION; +template static constexpr uint64_t NUM_LIMB_BITS = NUM_LIMB_BITS_IN_FIELD_SIMULATION; static constexpr uint64_t TOTAL_BITS = 254; template fq convert_to_grumpkin_fr(Builder& builder, const fr& f); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp index 70de06b7372..66abf8d3e1c 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/group/cycle_group.hpp @@ -60,7 +60,7 @@ template class cycle_group { * performs all required conversions if the input scalars are stdlib::field_t elements * * @note We opted to create a new class to represent `cycle_scalar` instead of using `bigfield`, - * as `bigfield` is inefficient in this context. All required range checks for `cycle_scalar` can be obtained for + * as `bigfield` is inefficient in this con text. All required range checks for `cycle_scalar` can be obtained for * free from the `batch_mul` algorithm, making the range checks performed by `bigfield` largely redundant. */ struct cycle_scalar { diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp index e032c206652..aa5dd42eafa 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp @@ -55,9 +55,11 @@ template class UltraRecursiveFlavor_ { using FF = typename Curve::ScalarField; using NativeFlavor = UltraFlavor; using NativeVerificationKey = NativeFlavor::VerificationKey; - using NativeVerifierCommitmentKey = NativeFlavor::VerifierCommitmentKey - static constexpr size_t NUM_WIRES = UltraFlavor::NUM_WIRES; + // Note(luke): Eventually this may not be needed at all + using VerifierCommitmentKey = bb::VerifierCommitmentKey; + + static constexpr size_t NUM_WIRES = UltraFlavor::NUM_WIRES; // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. // Note: this number does not include the individual sorted list polynomials. @@ -259,23 +261,6 @@ template class UltraRecursiveFlavor_ { }; public: - class VerifierCrs : public bb::srs::factories::VerifierCrs { - public: - VerifierCrs(const std::shared_ptr>& native_verifier_crs) - { - num_points = native_verifier_crs->num_points; - native_points = native_verifier_crs->get_monomial_points(); - } - - private: - size_t num_points; - Curve::AffineElement first_g1; - std::shared_ptr monomials_; - } - // WORKTODO: Should this - class VerifierCommitmentKey : public bb::VerifierCommitmentKey { - VerifierCommitmentKey(const std::shared_ptr native_pcs_verification_key) {} - }; /** * @brief The verification key is responsible for storing the the commitments to the precomputed (non-witnessk) * polynomials used by the verifier. @@ -301,7 +286,7 @@ template class UltraRecursiveFlavor_ { */ VerificationKey(CircuitBuilder* builder, const std::shared_ptr& native_key) { - this->pcs_verification_key = VerifierCommitmentKey(native_key->pcs_verification_key); + this->pcs_verification_key = native_key->pcs_verification_key; this->circuit_size = native_key->circuit_size; this->log_circuit_size = numeric::get_msb(this->circuit_size); this->num_public_inputs = native_key->num_public_inputs; @@ -472,4 +457,4 @@ template class UltraRecursiveFlavor_ { using Transcript = bb::BaseTranscript>; }; -} // namespace bb +} // namespace bb \ No newline at end of file From 50bb183399e4d5331ccf9b743abeb55114fcce8e Mon Sep 17 00:00:00 2001 From: maramihali Date: Thu, 23 May 2024 10:34:10 +0000 Subject: [PATCH 15/40] hacking --- .../eccvm/eccvm_composer.test.cpp | 1 - .../eccvm_recursive_verifier.test.cpp | 23 +++++++++++++++++++ .../cpp/src/barretenberg/flavor/flavor.hpp | 3 ++- .../barretenberg/polynomials/barycentric.hpp | 12 +++++----- .../cpp/src/barretenberg/polynomials/pow.hpp | 2 +- .../barretenberg/polynomials/univariate.hpp | 6 ++--- .../stdlib/primitives/bigfield/bigfield.hpp | 3 ++- .../stdlib/primitives/curves/bn254.hpp | 5 ++-- .../stdlib/primitives/field/field.hpp | 12 ++++++++++ .../primitives/field/field_conversion.hpp | 3 ++- .../src/barretenberg/sumcheck/sumcheck.hpp | 4 +++- .../barretenberg/sumcheck/sumcheck_round.hpp | 2 +- 12 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.test.cpp diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_composer.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_composer.test.cpp index 751f056904a..efd78aa09bd 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_composer.test.cpp @@ -19,7 +19,6 @@ using Fr = bb::fr; class ECCVMComposerTests : public ::testing::Test { protected: - // TODO(640): The Standard Honk on Grumpkin test suite fails unless the SRS is initialized for every test. void SetUp() override { srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); }; }; namespace { diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.test.cpp new file mode 100644 index 00000000000..1cae146a3ed --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.test.cpp @@ -0,0 +1,23 @@ +#include +namespace bb { +template class ECCVMRecursiveTests : public ::testing::Test { + public: + using InnerFlavor = typename RecursiveFlavor::NativeFlavor; + using InnerBuilder = typename InnerFlavor::CircuitBuilder; + using InnerProver = ECCVMProver; + using InnerVerifier = ECCVMVerifier; + using InnerG1 = InnerFlavor::Commitment; + using InnerFF = InnerFlavor::FF; + using InnerBF = InnerFlavor::BF; + + using RecursiveVerifier = ECCVMRecursiveVerifier_; + + using OuterBuilder = typename RecursiveFlavor::CircuitBuilder; + using OuterFlavor = std::conditional_t, GoblinUltraFlavor, UltraFlavor>; + using OuterProver = UltraProver_; + using OuterVerifier = UltraVerifier_; + using OuterProverInstance = ProverInstance_; + static void SetUpTestSuite() { srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); } + static void test_recursive_verification() {} +} +} // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp index 293c98efe5f..4a630cda1eb 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp @@ -315,6 +315,7 @@ class TranslatorFlavor; template class UltraRecursiveFlavor_; template class GoblinUltraRecursiveFlavor_; template class TranslatorRecursiveFlavor_; +template class ECCVMRecursiveFlavor_; } // namespace bb // Forward declare plonk flavors @@ -359,7 +360,7 @@ concept IsRecursiveFlavor = IsAnyOf, GoblinUltraRecursiveFlavor_, GoblinUltraRecursiveFlavor_ -,GoblinUltraRecursiveFlavor_, TranslatorRecursiveFlavor_, TranslatorRecursiveFlavor_, TranslatorRecursiveFlavor_>; +,GoblinUltraRecursiveFlavor_, TranslatorRecursiveFlavor_, TranslatorRecursiveFlavor_, TranslatorRecursiveFlavor_, ECCVMRecursiveFlavor_, ECCVMRecursiveFlavor_>; template concept IsGrumpkinFlavor = IsAnyOf; diff --git a/barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp b/barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp index dbad8cb56f0..4725a61cdfe 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp @@ -74,7 +74,7 @@ template result{}; Fr T0; for (size_t i = n - 1; i < n; --i) { @@ -151,7 +151,7 @@ template result; for (size_t i = 0; i != domain_size; ++i) { - result[i] = 1; + result[i] = Fr::one(); for (size_t j = 0; j != domain_size; ++j) { if (j != i) { result[i] *= big_domain[i] - big_domain[j]; @@ -166,7 +166,7 @@ template temporaries{}; std::array skipped{}; - Fr accumulator = 1; + Fr accumulator = Fr::one(); for (size_t i = 0; i < n; ++i) { temporaries[i] = accumulator; if (coeffs[i].get_value() == 0) { @@ -176,7 +176,7 @@ template result{}; Fr T0; for (size_t i = n - 1; i < n; --i) { @@ -213,8 +213,8 @@ template result; for (size_t i = 0; i != num_evals; ++i) { - result[i] = 1; - Fr v_i = i + domain_start; + result[i] = Fr::one(); + Fr v_i = uint256_t(i + domain_start); for (size_t j = 0; j != domain_size; ++j) { result[i] *= v_i - big_domain[j]; } diff --git a/barretenberg/cpp/src/barretenberg/polynomials/pow.hpp b/barretenberg/cpp/src/barretenberg/polynomials/pow.hpp index 583a9d3ddf1..5ec2ca3fc7b 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/pow.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/pow.hpp @@ -39,7 +39,7 @@ template struct PowPolynomial { * (1-u_k) + u_k\cdot \beta_k) \f$. * */ - FF partial_evaluation_result = FF(1); + FF partial_evaluation_result = FF::one(); explicit PowPolynomial(const std::vector& betas) : betas(betas) diff --git a/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp b/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp index e4821a24295..ab3d91b5db5 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp @@ -460,9 +460,9 @@ template ; - Fr full_numerator_value = 1; + Fr full_numerator_value = Fr::one(); for (size_t i = domain_start; i != domain_end; ++i) { - full_numerator_value *= u - i; + full_numerator_value *= u - uint256_t(i); } // build set of domain size-many denominator inverses 1/(d_i*(x_k - x_j)). will multiply against @@ -471,7 +471,7 @@ template class bigfield { const size_t maximum_bitlength = 0); bigfield(Builder* parent_context = nullptr); bigfield(Builder* parent_context, const uint256_t& value); + // (: + bigfield(const uint256_t& value) { bigfield(nullptr, value); } // we assume the limbs have already been normalized! bigfield(const field_t& a, @@ -255,7 +257,6 @@ template class bigfield { void self_reduce() const; bool is_constant() const { return prime_basis_limb.witness_index == IS_CONSTANT; } - /** * Create a public one constant * */ diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp index 5b8843671ec..cc8c1e4409b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp @@ -64,8 +64,9 @@ template struct bn254 { // Note: its useful to have these type names match the native analog exactly so that components that digest a // Curve (e.g. Gemini) can be agnostic as to whether they're operating on native or stdlib types. using Group = cycle_group; - using ScalarField = bigfield; - using BaseField = field_t; + using ScalarField = bigfield; // grumpkin fr which is bn254 fq which in a bn254 circuit + // is bigfield, should be replaced by a cycle_scalar + using BaseField = field_t; // fr using AffineElement = Group; // We dont need the projective form inside a circuit because using Element = Group; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp index d16f51198a5..53cd0127358 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp @@ -85,6 +85,18 @@ template class field_t { static constexpr bool is_composite = false; static constexpr uint256_t modulus = bb::fr::modulus; + static field_t one() + { + field_t result(uint256_t(1)); + return result; + } + + static field_t zero() + { + field_t result(uint256_t(0)); + return result; + } + static field_t from_witness_index(Builder* parent_context, uint32_t witness_index); explicit operator bool_t() const; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_conversion.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_conversion.hpp index 45fd133cb96..0cc9f98ba58 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_conversion.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_conversion.hpp @@ -12,7 +12,8 @@ template using fr = field_t; template using fq = bigfield; template using bn254_element = element, fr, curve::BN254::Group>; template using grumpkin_element = cycle_group; -template static constexpr uint64_t NUM_LIMB_BITS = NUM_LIMB_BITS_IN_FIELD_SIMULATION; + +static constexpr uint64_t NUM_LIMB_BITS = NUM_LIMB_BITS_IN_FIELD_SIMULATION; static constexpr uint64_t TOTAL_BITS = 254; template fq convert_to_grumpkin_fr(Builder& builder, const fr& f); diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp index 5a78502f149..6968cb5c401 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp @@ -417,7 +417,9 @@ template class SumcheckVerifier { bool checked = false; //! [Final Verification Step] if constexpr (IsRecursiveFlavor) { - checked = (full_honk_relation_purported_value == round.target_total_sum).get_value(); + // this is underconstraned but oh well or maybe nooot + full_honk_relation_purported_value.assert_equal(round.target_total_sum); + checked = (full_honk_relation_purported_value.get_value() == round.target_total_sum.get_value()); } else { checked = (full_honk_relation_purported_value == round.target_total_sum); } diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp index 0cbed010cf4..2a8b30a3f5e 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp @@ -377,7 +377,7 @@ template class SumcheckVerifierRound { bool sumcheck_round_failed(false); if constexpr (IsRecursiveFlavor) { target_total_sum.assert_equal(total_sum); - sumcheck_round_failed = (target_total_sum != total_sum).get_value(); + sumcheck_round_failed = (target_total_sum.get_value() != total_sum.get_value()); } else { sumcheck_round_failed = (target_total_sum != total_sum); } From db48cd754838d9063e39007dd7669856484fb6a0 Mon Sep 17 00:00:00 2001 From: maramihali Date: Thu, 23 May 2024 12:03:29 +0000 Subject: [PATCH 16/40] instantiated relations --- .../eccvm/eccvm_composer.test.cpp | 22 +- .../eccvm_recursion/ecc_lookup_relation.cpp | 10 + .../eccvm_recursion/ecc_msm_relation.cpp | 10 + .../ecc_point_table_relation.cpp | 10 + .../eccvm_recursion/ecc_set_relation.cpp | 13 + .../ecc_transcript_relation.cpp | 10 + .../eccvm_recursion/ecc_wnaf_relation.cpp | 10 + .../eccvm_recursive_verifier.test.cpp | 80 +++- .../flavor/relation_definitions.hpp | 4 + .../relations/ecc_vm/ecc_lookup_relation.cpp | 29 +- .../ecc_vm/ecc_lookup_relation_impl.hpp | 30 ++ .../relations/ecc_vm/ecc_msm_relation.cpp | 389 +---------------- .../ecc_vm/ecc_msm_relation_impl.hpp | 391 +++++++++++++++++ .../ecc_vm/ecc_point_table_relation.cpp | 171 +------- .../ecc_vm/ecc_point_table_relation_impl.hpp | 173 ++++++++ .../relations/ecc_vm/ecc_set_relation.cpp | 392 +---------------- .../ecc_vm/ecc_set_relation_impl.hpp | 394 ++++++++++++++++++ .../ecc_vm/ecc_transcript_relation.cpp | 252 +---------- .../ecc_vm/ecc_transcript_relation_impl.hpp | 256 ++++++++++++ .../relations/ecc_vm/ecc_wnaf_relation.cpp | 214 +--------- .../ecc_vm/ecc_wnaf_relation_impl.hpp | 216 ++++++++++ .../goblin_translator_composer.test.cpp | 0 22 files changed, 1625 insertions(+), 1451 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_lookup_relation.cpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_msm_relation.cpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_point_table_relation.cpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_set_relation.cpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_transcript_relation.cpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_wnaf_relation.cpp create mode 100644 barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation_impl.hpp create mode 100644 barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation_impl.hpp create mode 100644 barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation_impl.hpp create mode 100644 barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation_impl.hpp create mode 100644 barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation_impl.hpp create mode 100644 barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation_impl.hpp create mode 100644 barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_composer.test.cpp diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_composer.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_composer.test.cpp index efd78aa09bd..cd2db6b7124 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_composer.test.cpp @@ -14,8 +14,6 @@ #include "barretenberg/sumcheck/sumcheck_round.hpp" using namespace bb; -using G1 = bb::g1; -using Fr = bb::fr; class ECCVMComposerTests : public ::testing::Test { protected: @@ -24,15 +22,23 @@ class ECCVMComposerTests : public ::testing::Test { namespace { auto& engine = numeric::get_debug_randomness(); } + +/** + * @brief Adds operations in BN254 to the op_queue and then constructs and ECCVM circuit from the op_queue. + * + * @param engine + * @return ECCVMCircuitBuilder + */ ECCVMCircuitBuilder generate_circuit(numeric::RNG* engine = nullptr) { - std::shared_ptr op_queue = std::make_shared(); + using Curve = curve::BN254; + using G1 = Curve::Element; + using Fr = Curve::ScalarField; - auto generators = G1::derive_generators("test generators", 3); - - typename G1::element a = generators[0]; - typename G1::element b = generators[1]; - typename G1::element c = generators[2]; + std::shared_ptr op_queue = std::make_shared(); + G1 a = G1::random_element(engine); + G1 b = G1::random_element(engine); + G1 c = G1::random_element(engine); Fr x = Fr::random_element(engine); Fr y = Fr::random_element(engine); diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_lookup_relation.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_lookup_relation.cpp new file mode 100644 index 00000000000..98706d25106 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_lookup_relation.cpp @@ -0,0 +1,10 @@ +#include "barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp" +#include "barretenberg/flavor/relation_definitions.hpp" +#include "barretenberg/relations/ecc_vm/ecc_lookup_relation_impl.hpp" + +namespace bb { +template class ECCVMLookupRelationImpl>; +template class ECCVMLookupRelationImpl>; +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMLookupRelationImpl, ECCVMRecursiveFlavor_); +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMLookupRelationImpl, ECCVMRecursiveFlavor_); +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_msm_relation.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_msm_relation.cpp new file mode 100644 index 00000000000..5315414bb08 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_msm_relation.cpp @@ -0,0 +1,10 @@ +#include "barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp" +#include "barretenberg/flavor/relation_definitions.hpp" +#include "barretenberg/relations/ecc_vm/ecc_msm_relation_impl.hpp" + +namespace bb { +template class ECCVMMSMRelationImpl>; +template class ECCVMMSMRelationImpl>; +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMMSMRelationImpl, ECCVMRecursiveFlavor_); +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMMSMRelationImpl, ECCVMRecursiveFlavor_); +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_point_table_relation.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_point_table_relation.cpp new file mode 100644 index 00000000000..9b1f5bcd7cb --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_point_table_relation.cpp @@ -0,0 +1,10 @@ +#include "barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp" +#include "barretenberg/flavor/relation_definitions.hpp" +#include "barretenberg/relations/ecc_vm/ecc_point_table_relation_impl.hpp" + +namespace bb { +template class ECCVMPointTableRelationImpl>; +template class ECCVMPointTableRelationImpl>; +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMPointTableRelationImpl, ECCVMRecursiveFlavor_); +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMPointTableRelationImpl, ECCVMRecursiveFlavor_); +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_set_relation.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_set_relation.cpp new file mode 100644 index 00000000000..b417ec51f2e --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_set_relation.cpp @@ -0,0 +1,13 @@ +#include "barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp" +#include "barretenberg/flavor/relation_definitions.hpp" +#include "barretenberg/relations/ecc_vm/ecc_set_relation_impl.hpp" + +namespace bb { +template class ECCVMSetRelationImpl>; +template class ECCVMSetRelationImpl>; +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMSetRelationImpl, ECCVMRecursiveFlavor_); +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMSetRelationImpl, ECCVMRecursiveFlavor_); +DEFINE_SUMCHECK_VERIFIER_PERMUTATION_CLASS(ECCVMSetRelationImpl, ECCVMRecursiveFlavor_); +DEFINE_SUMCHECK_VERIFIER_PERMUTATION_CLASS(ECCVMSetRelationImpl, ECCVMRecursiveFlavor_); + +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_transcript_relation.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_transcript_relation.cpp new file mode 100644 index 00000000000..fcc0e4596ea --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_transcript_relation.cpp @@ -0,0 +1,10 @@ +#include "barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp" +#include "barretenberg/flavor/relation_definitions.hpp" +#include "barretenberg/relations/ecc_vm/ecc_transcript_relation_impl.hpp" + +namespace bb { +template class ECCVMTranscriptRelationImpl>; +template class ECCVMTranscriptRelationImpl>; +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMTranscriptRelationImpl, ECCVMRecursiveFlavor_); +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMTranscriptRelationImpl, ECCVMRecursiveFlavor_); +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_wnaf_relation.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_wnaf_relation.cpp new file mode 100644 index 00000000000..03d815f6f3b --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_wnaf_relation.cpp @@ -0,0 +1,10 @@ +#include "barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp" +#include "barretenberg/flavor/relation_definitions.hpp" +#include "barretenberg/relations/ecc_vm/ecc_wnaf_relation_impl.hpp" + +namespace bb { +template class ECCVMWnafRelationImpl>; +template class ECCVMWnafRelationImpl>; +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMWnafRelationImpl, ECCVMRecursiveFlavor_); +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMWnafRelationImpl, ECCVMRecursiveFlavor_); +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.test.cpp index 1cae146a3ed..433dc69f3e2 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.test.cpp @@ -1,4 +1,14 @@ +#include "barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp" +#include "barretenberg/eccvm/eccvm_prover.hpp" +#include "barretenberg/eccvm/eccvm_verifier.hpp" +#include "barretenberg/ultra_honk/ultra_prover.hpp" +#include "barretenberg/ultra_honk/ultra_verifier.hpp" + #include + +namespace { +auto& engine = numeric::get_debug_randomness(); +} namespace bb { template class ECCVMRecursiveTests : public ::testing::Test { public: @@ -18,6 +28,72 @@ template class ECCVMRecursiveTests : public ::testing using OuterVerifier = UltraVerifier_; using OuterProverInstance = ProverInstance_; static void SetUpTestSuite() { srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); } - static void test_recursive_verification() {} -} + + /** + * @brief Adds operations in BN254 to the op_queue and then constructs and ECCVM circuit from the op_queue. + * + * @param engine + * @return ECCVMCircuitBuilder + */ + static InnerBuilder generate_circuit(numeric::RNG* engine = nullptr) + { + using Curve = curve::BN254; + using G1 = Curve::Element; + using Fr = Curve::ScalarField; + + std::shared_ptr op_queue = std::make_shared(); + G1 a = G1::random_element(engine); + G1 b = G1::random_element(engine); + G1 c = G1::random_element(engine); + Fr x = Fr::random_element(engine); + Fr y = Fr::random_element(engine); + + op_queue->add_accumulate(a); + op_queue->mul_accumulate(a, x); + op_queue->mul_accumulate(b, x); + op_queue->mul_accumulate(b, y); + op_queue->add_accumulate(a); + op_queue->mul_accumulate(b, x); + op_queue->eq_and_reset(); + op_queue->add_accumulate(c); + op_queue->mul_accumulate(a, x); + op_queue->mul_accumulate(b, x); + op_queue->eq_and_reset(); + op_queue->mul_accumulate(a, x); + op_queue->mul_accumulate(b, x); + op_queue->mul_accumulate(c, x); + InnerBuilder builder{ op_queue }; + return builder; + } + + static void test_recursive_verification() + { + InnerBuilder builder = generate_circuit(&engine); + InnerProver prover(builder); + auto proof = prover.construct_proof(); + auto verification_key = std::make_shared(prover.key); + OuterBuilder outer_circuit; + RecursiveVerifier verifier{ &outer_circuit, verification_key }; + verifier.verify_proof(proof); + info("Recursive Verifier: num gates = ", outer_circuit.num_gates); + + // Check for a failure flag in the recursive verifier circuit + EXPECT_EQ(outer_circuit.failed(), false) << outer_circuit.err(); + + // InnerVerifier verifier(prover.key); + // bool verified = verifier.verify_proof(proof); + + // ASSERT_TRUE(verified); + } +}; +using FlavorTypes = testing::Types>; +// ECCVMRecursiveFlavor_, +// ECCVMRecursiveFlavor_>; + +TYPED_TEST_SUITE(ECCVMRecursiveTests, FlavorTypes); + +TYPED_TEST(ECCVMRecursiveTests, SingleRecursiveVerification) +{ + TestFixture::test_recursive_verification(); +}; } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/flavor/relation_definitions.hpp b/barretenberg/cpp/src/barretenberg/flavor/relation_definitions.hpp index dbfd02da9fc..b94abec6013 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/relation_definitions.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/relation_definitions.hpp @@ -38,3 +38,7 @@ PERMUTATION_METHOD(compute_grand_product_denominator, RelationImpl, Flavor, UnivariateAccumulator0, ExtendedEdge) \ PERMUTATION_METHOD(compute_grand_product_denominator, RelationImpl, Flavor, ValueAccumulator0, EvaluationEdge) \ PERMUTATION_METHOD(compute_grand_product_denominator, RelationImpl, Flavor, ValueAccumulator0, EntityEdge) + +#define DEFINE_SUMCHECK_VERIFIER_PERMUTATION_CLASS(RelationImpl, Flavor) \ + PERMUTATION_METHOD(compute_grand_product_numerator, RelationImpl, Flavor, ValueAccumulator0, EvaluationEdge) \ + PERMUTATION_METHOD(compute_grand_product_denominator, RelationImpl, Flavor, ValueAccumulator0, EvaluationEdge) diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation.cpp index e42028a487e..4afdd9c0898 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation.cpp @@ -1,35 +1,8 @@ #include "barretenberg/eccvm/eccvm_flavor.hpp" #include "barretenberg/flavor/relation_definitions.hpp" -#include "barretenberg/honk/proof_system/logderivative_library.hpp" -#include "ecc_msm_relation.hpp" +#include "ecc_lookup_relation_impl.hpp" namespace bb { - -/** - * @brief Expression for ECCVM lookup tables. - * @details We use log-derivative lookup tables for the following case: - * Table writes: ECCVMPointTable columns: we define Straus point table: - * { {0, -15[P]}, {1, -13[P]}, ..., {15, 15[P]} } - * write source: { precompute_round, precompute_tx, precompute_ty } - * Table reads: ECCVMMSM columns. Each row adds up to 4 points into MSM accumulator - * read source: { msm_slice1, msm_x1, msm_y1 }, ..., { msm_slice4, msm_x4, msm_y4 } - * @param evals transformed to `evals + C(in(X)...)*scaling_factor` - * @param in an std::array containing the fully extended Accumulator edges. - * @param parameters contains beta, gamma, and public_input_delta, .... - * @param scaling_factor optional term to scale the evaluation before adding to evals. - */ -template -template -void ECCVMLookupRelationImpl::accumulate(ContainerOverSubrelations& accumulator, - const AllEntities& in, - const Parameters& params, - const FF& scaling_factor) -{ - accumulate_logderivative_lookup_subrelation_contributions>( - accumulator, in, params, scaling_factor); -} - template class ECCVMLookupRelationImpl; DEFINE_SUMCHECK_RELATION_CLASS(ECCVMLookupRelationImpl, ECCVMFlavor); - } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation_impl.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation_impl.hpp new file mode 100644 index 00000000000..1a857c54654 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation_impl.hpp @@ -0,0 +1,30 @@ +#pragma once +#include "barretenberg/honk/proof_system/logderivative_library.hpp" +#include "ecc_lookup_relation.hpp" + +namespace bb { + +/** + * @brief Expression for ECCVM lookup tables. + * @details We use log-derivative lookup tables for the following case: + * Table writes: ECCVMPointTable columns: we define Straus point table: + * { {0, -15[P]}, {1, -13[P]}, ..., {15, 15[P]} } + * write source: { precompute_round, precompute_tx, precompute_ty } + * Table reads: ECCVMMSM columns. Each row adds up to 4 points into MSM accumulator + * read source: { msm_slice1, msm_x1, msm_y1 }, ..., { msm_slice4, msm_x4, msm_y4 } + * @param evals transformed to `evals + C(in(X)...)*scaling_factor` + * @param in an std::array containing the fully extended Accumulator edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ +template +template +void ECCVMLookupRelationImpl::accumulate(ContainerOverSubrelations& accumulator, + const AllEntities& in, + const Parameters& params, + const FF& scaling_factor) +{ + accumulate_logderivative_lookup_subrelation_contributions>( + accumulator, in, params, scaling_factor); +} +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation.cpp index b71b5a6e4a0..bb7c3738b6f 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation.cpp @@ -1,396 +1,9 @@ -#include "ecc_msm_relation.hpp" #include "barretenberg/eccvm/eccvm_flavor.hpp" #include "barretenberg/flavor/relation_definitions.hpp" +#include "ecc_msm_relation_impl.hpp" namespace bb { -/** - * @brief MSM relations that evaluate the Strauss multiscalar multiplication algorithm. - * - * @details - * The Strauss algorithm for a size-k MSM takes scalars/points (a_i, [P_i]) for i = 0 to k-1. - * The specific algoritm we use is the following: - * - * PHASE 1: Precomputation (performed in ecc_wnaf_relation.hpp, ecc_point_table_relation.hpp) - * Each scalar a_i is split into 4-bit WNAF slices s_{j, i} for j = 0 to 31, and a skew bool skew_i - * For each point [P_i] a size-16 lookup table of points, T_i, is computed { [-15 P_i], [-13 P_i], ..., [15 P_i] } - * - * PHASE 2: MSM evaluation - * MSM evaluation is split into 32 rounds that operate on an accumulator point [Acc] - * The first 31 rounds are composed of an ADDITION round and a DOUBLE round. - * The final 32nd round is composed of an ADDITION round and a SKEW round. - * - * ADDITION round (round = j): - * [Acc] = [Acc] + T_i[a_{i, j}] for all i in [0, ... k-1] - * - * DOUBLE round: - * [Acc] = 16 * [Acc] (four point doublings) - * - * SKEW round: - * If skew_i == 1, [Acc] = [Acc] - [P_i] for all i in [0, ..., k - 1] - * - * The relations in ECCVMMSMRelationImpl constrain the ADDITION, DOUBLE and SKEW rounds - * @param evals transformed to `evals + C(in(X)...)*scaling_factor` - * @param in an std::array containing the fully extended Accumulator edges. - * @param parameters contains beta, gamma, and public_input_delta, .... - * @param scaling_factor optional term to scale the evaluation before adding to evals. - */ -template -template -void ECCVMMSMRelationImpl::accumulate(ContainerOverSubrelations& accumulator, - const AllEntities& in, - const Parameters& /*unused*/, - const FF& scaling_factor) -{ - using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - - const auto& x1 = View(in.msm_x1); - const auto& y1 = View(in.msm_y1); - const auto& x2 = View(in.msm_x2); - const auto& y2 = View(in.msm_y2); - const auto& x3 = View(in.msm_x3); - const auto& y3 = View(in.msm_y3); - const auto& x4 = View(in.msm_x4); - const auto& y4 = View(in.msm_y4); - const auto& collision_inverse1 = View(in.msm_collision_x1); - const auto& collision_inverse2 = View(in.msm_collision_x2); - const auto& collision_inverse3 = View(in.msm_collision_x3); - const auto& collision_inverse4 = View(in.msm_collision_x4); - const auto& lambda1 = View(in.msm_lambda1); - const auto& lambda2 = View(in.msm_lambda2); - const auto& lambda3 = View(in.msm_lambda3); - const auto& lambda4 = View(in.msm_lambda4); - const auto& lagrange_first = View(in.lagrange_first); - const auto& add1 = View(in.msm_add1); - const auto& add1_shift = View(in.msm_add1_shift); - const auto& add2 = View(in.msm_add2); - const auto& add3 = View(in.msm_add3); - const auto& add4 = View(in.msm_add4); - const auto& acc_x = View(in.msm_accumulator_x); - const auto& acc_y = View(in.msm_accumulator_y); - const auto& acc_x_shift = View(in.msm_accumulator_x_shift); - const auto& acc_y_shift = View(in.msm_accumulator_y_shift); - const auto& slice1 = View(in.msm_slice1); - const auto& slice2 = View(in.msm_slice2); - const auto& slice3 = View(in.msm_slice3); - const auto& slice4 = View(in.msm_slice4); - const auto& msm_transition = View(in.msm_transition); - const auto& msm_transition_shift = View(in.msm_transition_shift); - const auto& round = View(in.msm_round); - const auto& round_shift = View(in.msm_round_shift); - const auto& q_add = View(in.msm_add); - const auto& q_add_shift = View(in.msm_add_shift); - const auto& q_skew = View(in.msm_skew); - const auto& q_skew_shift = View(in.msm_skew_shift); - const auto& q_double = View(in.msm_double); - const auto& q_double_shift = View(in.msm_double_shift); - const auto& msm_size = View(in.msm_size_of_msm); - // const auto& msm_size_shift = View(in.msm_size_of_msm_shift); - const auto& pc = View(in.msm_pc); - const auto& pc_shift = View(in.msm_pc_shift); - const auto& count = View(in.msm_count); - const auto& count_shift = View(in.msm_count_shift); - auto is_not_first_row = (-lagrange_first + 1); - - /** - * @brief Evaluating ADDITION rounds - * - * This comment describes the algorithm we want the Prover to perform. - * The relations we constrain are supposed to make an honest Prover compute witnesses consistent with the following: - * - * For an MSM of size-k... - * - * Algorithm to determine if round at shifted row is an ADDITION round: - * 1. count_shift < msm_size - * 2. round != 32 - * - * Algorithm to process MSM ADDITION round: - * 1. If `round == 0` set `count = 0` - * 2. For j = pc + count, perform the following: - * 2a. If j + 3 < k: [P_{j + 3}] = T_{j+ 3}[slice_{j + 3}] - * 2b. If j + 2 < k: [P_{j + 2}] = T_{j+ 2}[slice_{j + 2}] - * 2c. If j + 1 < k: [P_{j + 1}] = T_{j+ 1}[slice_{j + 1}] - * 2d. [P_{j}] = T_{j}[slice_{j}] - * 2e. If j + 3 < k: [Acc_shift] = [Acc] + [P_j] + [P_{j+1}] + [P_{j+2}] + [P_{j+3}] - * 2f. Else If j + 2 < k: [Acc_shift] = [Acc] + [P_j] + [P_{j+1}] + [P_{j+2}] - * 2g. Else IF j + 1 < k: [Acc_shift] = [Acc] + [P_j] + [P_{j+1}] - * 2h. Else : [Acc_shift] = [Acc] + [P_j] - * 3. `count_shift = count + 1 + (j + 1 < k) + (j + 2 < k) + (j + 3 < k)` - */ - - /** - * @brief Constraining addition rounds - * - * The boolean column q_add describes whether a round is an ADDITION round. - * The values of q_add are Prover-defined. We need to ensure they set q_add correctly. - * We rely on the following statements that we assume are constrained to be true (from other relations): - * 1. The set of reads into (pc, round, wnaf_slice) is constructed when q_add = 1 - * 2. The set of reads into (pc, round, wnaf_slice) must match the set of writes from the point_table columns - * 3. The set of writes into (pc, round, wnaf_slice) from the point table columns is correct - * 4. `round` only updates when `q_add = 1` at current row and `q_add = 0` at next row - * If a Prover sets `q_add = 0` when an honest Prover would set `q_add = 1`, - * this will produce an inequality in the set of reads / writes into the (pc, round, wnaf_slice) table. - * - * The addition algorithm has several IF/ELSE statements based on comparing `count` with `msm_size`. - * Instead of directly constraining these, we define 4 boolean columns `q_add1, q_add2, q_add3, q_add4`. - * Like `q_add`, their values are Prover-defined. We need to ensure they are set correctly. - * We update the above conditions on reads into (pc, round, wnaf_slice) to the following: - * 1. The set of reads into (pc_{count}, round, wnaf_slice_{count}) is constructed when q_add = 1 AND q_add1 = - * 1 - * 2. The set of reads into (pc_{count + 1}, round, wnaf_slice_{count + 1}) is constructed when q_add = 1 AND - * q_add2 = 1 - * 3. The set of reads into (pc_{count + 2}, round, wnaf_slice_{count + 2}) is constructed when q_add = 1 AND - * q_add3 = 1 - * 4. The set of reads into (pc_{count + 3}, round, wnaf_slice_{count + 3}) is constructed when q_add = 1 AND - * q_add4 = 1 - * - * To ensure that all q_addi values are correctly set we apply consistency checks to q_add1/q_add2/q_add3/q_add4: - * 1. If q_add2 = 1, require q_add1 = 1 - * 2. If q_add3 = 1, require q_add2 = 1 - * 3. If q_add4 = 1, require q_add3 = 1 - * 4. If q_add1_shift = 1 AND round does not update between rows, require q_add4 = 1 - * - * We want to use all of the above to reason about the set of reads into (pc, round, wnaf_slice). - * The goal is to conclude that any case where the Prover incorrectly sets q_add/q_add1/q_add2/q_add3/q_add4 will - * produce a set inequality between the reads/writes into (pc, round, wnaf_slice) - */ - - /** - * @brief Addition relation - * - * All addition operations in ECCVMMSMRelationImpl are conditional additions! - * This method returns two Accumulators that represent x/y coord of output. - * Output is either an addition of inputs, or xa/ya dpeending on value of `selector`. - * Additionally, we require `lambda = 0` if `selector = 0`. - * The `collision_relation` accumulator tracks a subrelation that validates xb != xa. - * Repeated calls to this method will increase the max degree of the Accumulator output - * Degree of x_out, y_out = max degree of x_a/x_b + 1 - * 4 Iterations will produce an output degree of 6 - */ - auto add = [&](auto& xb, - auto& yb, - auto& xa, - auto& ya, - auto& lambda, - auto& selector, - auto& relation, - auto& collision_relation) { - // (L * (xb - xa) - yb - ya) * s = 0 - // L * (1 - s) = 0 - // (combine) (L * (xb - xa - 1) - yb - ya) * s + L = 0 - relation += selector * (lambda * (xb - xa - 1) - (yb - ya)) + lambda; - collision_relation += selector * (xb - xa); - // x3 = L.L + (-xb - xa) * q + (1 - q) xa - auto x_out = lambda * lambda + (-xb - xa - xa) * selector + xa; - - // y3 = L . (xa - x3) - ya * q + (1 - q) ya - auto y_out = lambda * (xa - x_out) + (-ya - ya) * selector + ya; - return std::array{ x_out, y_out }; - }; - - // ADD operations (if row represents ADD round, not SKEW or DOUBLE) - Accumulator add_relation(0); - Accumulator x1_collision_relation(0); - Accumulator x2_collision_relation(0); - Accumulator x3_collision_relation(0); - Accumulator x4_collision_relation(0); - // If msm_transition = 1, we have started a new MSM. We need to treat the current value of [Acc] as the point at - // infinity! - auto add_into_accumulator = -msm_transition + 1; - auto [x_t1, y_t1] = add(acc_x, acc_y, x1, y1, lambda1, add_into_accumulator, add_relation, x1_collision_relation); - auto [x_t2, y_t2] = add(x2, y2, x_t1, y_t1, lambda2, add2, add_relation, x2_collision_relation); - auto [x_t3, y_t3] = add(x3, y3, x_t2, y_t2, lambda3, add3, add_relation, x3_collision_relation); - auto [x_t4, y_t4] = add(x4, y4, x_t3, y_t3, lambda4, add4, add_relation, x4_collision_relation); - - // Validate accumulator output matches ADD output if q_add = 1 - // (this is a degree-6 relation) - std::get<0>(accumulator) += q_add * (acc_x_shift - x_t4) * scaling_factor; - std::get<1>(accumulator) += q_add * (acc_y_shift - y_t4) * scaling_factor; - std::get<2>(accumulator) += q_add * add_relation * scaling_factor; - - /** - * @brief doubles a point. - * - * Degree of x_out = 2 - * Degree of y_out = 3 - * Degree of relation = 4 - */ - auto dbl = [&](auto& x, auto& y, auto& lambda, auto& relation) { - auto two_x = x + x; - relation += lambda * (y + y) - (two_x + x) * x; - auto x_out = lambda * lambda - two_x; - auto y_out = lambda * (x - x_out) - y; - return std::array{ x_out, y_out }; - }; - - /** - * @brief - * - * Algorithm to determine if round is a DOUBLE round: - * 1. count_shift >= msm_size - * 2. round != 32 - * - * Algorithm to process MSM DOUBLE round: - * [Acc_shift] = (([Acc].double()).double()).double() - * - * As with additions, the column q_double describes whether row is a double round. It is Prover-defined. - * The value of `msm_round` can only update when `q_double = 1` and we use this to ensure Prover correctly sets - * `q_double`. (see round transition relations further down) - */ - Accumulator double_relation(0); - auto [x_d1, y_d1] = dbl(acc_x, acc_y, lambda1, double_relation); - auto [x_d2, y_d2] = dbl(x_d1, y_d1, lambda2, double_relation); - auto [x_d3, y_d3] = dbl(x_d2, y_d2, lambda3, double_relation); - auto [x_d4, y_d4] = dbl(x_d3, y_d3, lambda4, double_relation); - std::get<10>(accumulator) += q_double * (acc_x_shift - x_d4) * scaling_factor; - std::get<11>(accumulator) += q_double * (acc_y_shift - y_d4) * scaling_factor; - std::get<12>(accumulator) += q_double * double_relation * scaling_factor; - - /** - * @brief SKEW operations - * When computing x * [P], if x is even we must subtract [P] from accumulator - * (this is because our windowed non-adjacent-form can only represent odd numbers) - * Round 32 represents "skew" round. - * If scalar slice == 7, we add into accumulator (point_table[7] maps to -[P]) - * If scalar slice == 0, we do not add into accumulator - * i.e. for the skew round we can use the slice values as our "selector" when doing conditional point adds - */ - Accumulator skew_relation(0); - static constexpr FF inverse_seven = FF(7).invert(); - auto skew1_select = slice1 * inverse_seven; - auto skew2_select = slice2 * inverse_seven; - auto skew3_select = slice3 * inverse_seven; - auto skew4_select = slice4 * inverse_seven; - Accumulator x1_skew_collision_relation(0); - Accumulator x2_skew_collision_relation(0); - Accumulator x3_skew_collision_relation(0); - Accumulator x4_skew_collision_relation(0); - // add skew points iff row is a SKEW row AND slice = 7 (point_table[7] maps to -[P]) - // N.B. while it would be nice to have one `add` relation for both ADD and SKEW rounds, - // this would increase degree of sumcheck identity vs evaluating them separately. - // This is because, for add rounds, the result of adding [P1], [Acc] is [P1 + Acc] or [P1] - // but for skew rounds, the result of adding [P1], [Acc] is [P1 + Acc] or [Acc] - auto [x_s1, y_s1] = add(x1, y1, acc_x, acc_y, lambda1, skew1_select, skew_relation, x1_skew_collision_relation); - auto [x_s2, y_s2] = add(x2, y2, x_s1, y_s1, lambda2, skew2_select, skew_relation, x2_skew_collision_relation); - auto [x_s3, y_s3] = add(x3, y3, x_s2, y_s2, lambda3, skew3_select, skew_relation, x3_skew_collision_relation); - auto [x_s4, y_s4] = add(x4, y4, x_s3, y_s3, lambda4, skew4_select, skew_relation, x4_skew_collision_relation); - - // Validate accumulator output matches SKEW output if q_skew = 1 - // (this is a degree-6 relation) - std::get<3>(accumulator) += q_skew * (acc_x_shift - x_s4) * scaling_factor; - std::get<4>(accumulator) += q_skew * (acc_y_shift - y_s4) * scaling_factor; - std::get<5>(accumulator) += q_skew * skew_relation * scaling_factor; - - // Check x-coordinates do not collide if row is an ADD row or a SKEW row - // if either q_add or q_skew = 1, an inverse should exist for each computed relation - // Step 1: construct boolean selectors that describe whether we added a point at the current row - const auto add_first_point = add_into_accumulator * q_add + q_skew * skew1_select; - const auto add_second_point = add2 * q_add + q_skew * skew2_select; - const auto add_third_point = add3 * q_add + q_skew * skew3_select; - const auto add_fourth_point = add4 * q_add + q_skew * skew4_select; - // Step 2: construct the delta between x-coordinates for each point add (depending on if row is ADD or SKEW) - const auto x1_delta = x1_skew_collision_relation * q_skew + x1_collision_relation * q_add; - const auto x2_delta = x2_skew_collision_relation * q_skew + x2_collision_relation * q_add; - const auto x3_delta = x3_skew_collision_relation * q_skew + x3_collision_relation * q_add; - const auto x4_delta = x4_skew_collision_relation * q_skew + x4_collision_relation * q_add; - // Step 3: x_delta * inverse - 1 = 0 if we performed a point addition (else x_delta * inverse = 0) - std::get<6>(accumulator) += (x1_delta * collision_inverse1 - add_first_point) * scaling_factor; - std::get<7>(accumulator) += (x2_delta * collision_inverse2 - add_second_point) * scaling_factor; - std::get<8>(accumulator) += (x3_delta * collision_inverse3 - add_third_point) * scaling_factor; - std::get<9>(accumulator) += (x4_delta * collision_inverse4 - add_fourth_point) * scaling_factor; - - // Validate that if q_add = 1 or q_skew = 1, add1 also is 1 - // TODO(@zac-williamson) Once we have a stable base to work off of, remove q_add1 and replace with q_msm_add + - // q_msm_skew (issue #2222) - std::get<32>(accumulator) += (add1 - q_add - q_skew) * scaling_factor; - - // If add_i = 0, slice_i = 0 - // When add_i = 0, force slice_i to ALSO be 0 - std::get<13>(accumulator) += (-add1 + 1) * slice1 * scaling_factor; - std::get<14>(accumulator) += (-add2 + 1) * slice2 * scaling_factor; - std::get<15>(accumulator) += (-add3 + 1) * slice3 * scaling_factor; - std::get<16>(accumulator) += (-add4 + 1) * slice4 * scaling_factor; - - // only one of q_skew, q_double, q_add can be nonzero - std::get<17>(accumulator) += (q_add * q_double + q_add * q_skew + q_double * q_skew) * scaling_factor; - - // We look up wnaf slices by mapping round + pc -> slice - // We use an exact set membership check to validate that - // wnafs written in wnaf_relation == wnafs read in msm relation - // We use `add1/add2/add3/add4` to flag whether we are performing a wnaf read op - // We can set these to be Prover-defined as the set membership check implicitly ensures that the correct reads - // have occurred. - // if msm_transition = 0, round_shift - round = 0 or 1 - const auto round_delta = round_shift - round; - - // ROUND TRANSITION LOGIC (when round does not change) - // If msm_transition = 0 (next row) then round_delta = 0 or 1 - const auto round_transition = round_delta * (-msm_transition_shift + 1); - std::get<18>(accumulator) += round_transition * (round_delta - 1) * scaling_factor; - - // ROUND TRANSITION LOGIC (when round DOES change) - // round_transition describes whether we are transitioning between rounds of an MSM - // If round_transition = 1, the next row is either a double (if round != 31) or we are adding skew (if round == - // 31) round_transition * skew * (round - 31) = 0 (if round tx and skew, round == 31) round_transition * (skew + - // double - 1) = 0 (if round tx, skew XOR double = 1) i.e. if round tx and round != 31, double = 1 - std::get<19>(accumulator) += round_transition * q_skew_shift * (round - 31) * scaling_factor; - std::get<20>(accumulator) += round_transition * (q_skew_shift + q_double_shift - 1) * scaling_factor; - - // if no double or no skew, round_delta = 0 - std::get<21>(accumulator) += round_transition * (-q_double_shift + 1) * (-q_skew_shift + 1) * scaling_factor; - - // if double, next double != 1 - std::get<22>(accumulator) += q_double * q_double_shift * scaling_factor; - - // if double, next add = 1 - std::get<23>(accumulator) += q_double * (-q_add_shift + 1) * scaling_factor; - - // updating count - // if msm_transition = 0 and round_transition = 0, count_shift = count + add1 + add2 + add3 + add4 - // todo: we need this? - std::get<24>(accumulator) += (-msm_transition_shift + 1) * (-round_delta + 1) * - (count_shift - count - add1 - add2 - add3 - add4) * scaling_factor; - - std::get<25>(accumulator) += - is_not_first_row * (-msm_transition_shift + 1) * round_delta * count_shift * scaling_factor; - - // if msm_transition = 1, count_shift = 0 - std::get<26>(accumulator) += is_not_first_row * msm_transition_shift * count_shift * scaling_factor; - - // if msm_transition = 1, pc = pc_shift + msm_size - // `ecc_set_relation` ensures `msm_size` maps to `transcript.msm_count` for the current value of `pc` - std::get<27>(accumulator) += is_not_first_row * msm_transition_shift * (msm_size + pc_shift - pc) * scaling_factor; - - // Addition continuity checks - // We want to RULE OUT the following scenarios: - // Case 1: add2 = 1, add1 = 0 - // Case 2: add3 = 1, add2 = 0 - // Case 3: add4 = 1, add3 = 0 - // These checks ensure that the current row does not skip points (for both ADD and SKEW ops) - // This is part of a wider set of checks we use to ensure that all point data is used in the assigned - // multiscalar multiplication operation. - // (and not in a different MSM operation) - std::get<28>(accumulator) += add2 * (-add1 + 1) * scaling_factor; - std::get<29>(accumulator) += add3 * (-add2 + 1) * scaling_factor; - std::get<30>(accumulator) += add4 * (-add3 + 1) * scaling_factor; - - // Final continuity check. - // If an addition spans two rows, we need to make sure that the following scenario is RULED OUT: - // add4 = 0 on the CURRENT row, add1 = 1 on the NEXT row - // We must apply the above for the two cases: - // Case 1: q_add = 1 on the CURRENT row, q_add = 1 on the NEXT row - // Case 2: q_skew = 1 on the CURRENT row, q_skew = 1 on the NEXT row - // (i.e. if q_skew = 1, q_add_shift = 1 this implies an MSM transition so we skip this continuity check) - std::get<31>(accumulator) += - (q_add * q_add_shift + q_skew * q_skew_shift) * (-add4 + 1) * add1_shift * scaling_factor; - - // remaining checks (done in ecc_set_relation.hpp, ecc_lookup_relation.hpp) - // when transition occurs, perform set membership lookup on (accumulator / pc / msm_size) - // perform set membership lookups on add_i * (pc / round / slice_i) - // perform lookups on (pc / slice_i / x / y) -} - template class ECCVMMSMRelationImpl; DEFINE_SUMCHECK_RELATION_CLASS(ECCVMMSMRelationImpl, ECCVMFlavor); diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation_impl.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation_impl.hpp new file mode 100644 index 00000000000..715bf747c00 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation_impl.hpp @@ -0,0 +1,391 @@ +#pragma once +#include "ecc_msm_relation.hpp" +namespace bb { + +/** + * @brief MSM relations that evaluate the Strauss multiscalar multiplication algorithm. + * + * @details + * The Strauss algorithm for a size-k MSM takes scalars/points (a_i, [P_i]) for i = 0 to k-1. + * The specific algoritm we use is the following: + * + * PHASE 1: Precomputation (performed in ecc_wnaf_relation.hpp, ecc_point_table_relation.hpp) + * Each scalar a_i is split into 4-bit WNAF slices s_{j, i} for j = 0 to 31, and a skew bool skew_i + * For each point [P_i] a size-16 lookup table of points, T_i, is computed { [-15 P_i], [-13 P_i], ..., [15 P_i] } + * + * PHASE 2: MSM evaluation + * MSM evaluation is split into 32 rounds that operate on an accumulator point [Acc] + * The first 31 rounds are composed of an ADDITION round and a DOUBLE round. + * The final 32nd round is composed of an ADDITION round and a SKEW round. + * + * ADDITION round (round = j): + * [Acc] = [Acc] + T_i[a_{i, j}] for all i in [0, ... k-1] + * + * DOUBLE round: + * [Acc] = 16 * [Acc] (four point doublings) + * + * SKEW round: + * If skew_i == 1, [Acc] = [Acc] - [P_i] for all i in [0, ..., k - 1] + * + * The relations in ECCVMMSMRelationImpl constrain the ADDITION, DOUBLE and SKEW rounds + * @param evals transformed to `evals + C(in(X)...)*scaling_factor` + * @param in an std::array containing the fully extended Accumulator edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ +template +template +void ECCVMMSMRelationImpl::accumulate(ContainerOverSubrelations& accumulator, + const AllEntities& in, + const Parameters& /*unused*/, + const FF& scaling_factor) +{ + using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; + using View = typename Accumulator::View; + + const auto& x1 = View(in.msm_x1); + const auto& y1 = View(in.msm_y1); + const auto& x2 = View(in.msm_x2); + const auto& y2 = View(in.msm_y2); + const auto& x3 = View(in.msm_x3); + const auto& y3 = View(in.msm_y3); + const auto& x4 = View(in.msm_x4); + const auto& y4 = View(in.msm_y4); + const auto& collision_inverse1 = View(in.msm_collision_x1); + const auto& collision_inverse2 = View(in.msm_collision_x2); + const auto& collision_inverse3 = View(in.msm_collision_x3); + const auto& collision_inverse4 = View(in.msm_collision_x4); + const auto& lambda1 = View(in.msm_lambda1); + const auto& lambda2 = View(in.msm_lambda2); + const auto& lambda3 = View(in.msm_lambda3); + const auto& lambda4 = View(in.msm_lambda4); + const auto& lagrange_first = View(in.lagrange_first); + const auto& add1 = View(in.msm_add1); + const auto& add1_shift = View(in.msm_add1_shift); + const auto& add2 = View(in.msm_add2); + const auto& add3 = View(in.msm_add3); + const auto& add4 = View(in.msm_add4); + const auto& acc_x = View(in.msm_accumulator_x); + const auto& acc_y = View(in.msm_accumulator_y); + const auto& acc_x_shift = View(in.msm_accumulator_x_shift); + const auto& acc_y_shift = View(in.msm_accumulator_y_shift); + const auto& slice1 = View(in.msm_slice1); + const auto& slice2 = View(in.msm_slice2); + const auto& slice3 = View(in.msm_slice3); + const auto& slice4 = View(in.msm_slice4); + const auto& msm_transition = View(in.msm_transition); + const auto& msm_transition_shift = View(in.msm_transition_shift); + const auto& round = View(in.msm_round); + const auto& round_shift = View(in.msm_round_shift); + const auto& q_add = View(in.msm_add); + const auto& q_add_shift = View(in.msm_add_shift); + const auto& q_skew = View(in.msm_skew); + const auto& q_skew_shift = View(in.msm_skew_shift); + const auto& q_double = View(in.msm_double); + const auto& q_double_shift = View(in.msm_double_shift); + const auto& msm_size = View(in.msm_size_of_msm); + // const auto& msm_size_shift = View(in.msm_size_of_msm_shift); + const auto& pc = View(in.msm_pc); + const auto& pc_shift = View(in.msm_pc_shift); + const auto& count = View(in.msm_count); + const auto& count_shift = View(in.msm_count_shift); + auto is_not_first_row = (-lagrange_first + 1); + + /** + * @brief Evaluating ADDITION rounds + * + * This comment describes the algorithm we want the Prover to perform. + * The relations we constrain are supposed to make an honest Prover compute witnesses consistent with the following: + * + * For an MSM of size-k... + * + * Algorithm to determine if round at shifted row is an ADDITION round: + * 1. count_shift < msm_size + * 2. round != 32 + * + * Algorithm to process MSM ADDITION round: + * 1. If `round == 0` set `count = 0` + * 2. For j = pc + count, perform the following: + * 2a. If j + 3 < k: [P_{j + 3}] = T_{j+ 3}[slice_{j + 3}] + * 2b. If j + 2 < k: [P_{j + 2}] = T_{j+ 2}[slice_{j + 2}] + * 2c. If j + 1 < k: [P_{j + 1}] = T_{j+ 1}[slice_{j + 1}] + * 2d. [P_{j}] = T_{j}[slice_{j}] + * 2e. If j + 3 < k: [Acc_shift] = [Acc] + [P_j] + [P_{j+1}] + [P_{j+2}] + [P_{j+3}] + * 2f. Else If j + 2 < k: [Acc_shift] = [Acc] + [P_j] + [P_{j+1}] + [P_{j+2}] + * 2g. Else IF j + 1 < k: [Acc_shift] = [Acc] + [P_j] + [P_{j+1}] + * 2h. Else : [Acc_shift] = [Acc] + [P_j] + * 3. `count_shift = count + 1 + (j + 1 < k) + (j + 2 < k) + (j + 3 < k)` + */ + + /** + * @brief Constraining addition rounds + * + * The boolean column q_add describes whether a round is an ADDITION round. + * The values of q_add are Prover-defined. We need to ensure they set q_add correctly. + * We rely on the following statements that we assume are constrained to be true (from other relations): + * 1. The set of reads into (pc, round, wnaf_slice) is constructed when q_add = 1 + * 2. The set of reads into (pc, round, wnaf_slice) must match the set of writes from the point_table columns + * 3. The set of writes into (pc, round, wnaf_slice) from the point table columns is correct + * 4. `round` only updates when `q_add = 1` at current row and `q_add = 0` at next row + * If a Prover sets `q_add = 0` when an honest Prover would set `q_add = 1`, + * this will produce an inequality in the set of reads / writes into the (pc, round, wnaf_slice) table. + * + * The addition algorithm has several IF/ELSE statements based on comparing `count` with `msm_size`. + * Instead of directly constraining these, we define 4 boolean columns `q_add1, q_add2, q_add3, q_add4`. + * Like `q_add`, their values are Prover-defined. We need to ensure they are set correctly. + * We update the above conditions on reads into (pc, round, wnaf_slice) to the following: + * 1. The set of reads into (pc_{count}, round, wnaf_slice_{count}) is constructed when q_add = 1 AND q_add1 = + * 1 + * 2. The set of reads into (pc_{count + 1}, round, wnaf_slice_{count + 1}) is constructed when q_add = 1 AND + * q_add2 = 1 + * 3. The set of reads into (pc_{count + 2}, round, wnaf_slice_{count + 2}) is constructed when q_add = 1 AND + * q_add3 = 1 + * 4. The set of reads into (pc_{count + 3}, round, wnaf_slice_{count + 3}) is constructed when q_add = 1 AND + * q_add4 = 1 + * + * To ensure that all q_addi values are correctly set we apply consistency checks to q_add1/q_add2/q_add3/q_add4: + * 1. If q_add2 = 1, require q_add1 = 1 + * 2. If q_add3 = 1, require q_add2 = 1 + * 3. If q_add4 = 1, require q_add3 = 1 + * 4. If q_add1_shift = 1 AND round does not update between rows, require q_add4 = 1 + * + * We want to use all of the above to reason about the set of reads into (pc, round, wnaf_slice). + * The goal is to conclude that any case where the Prover incorrectly sets q_add/q_add1/q_add2/q_add3/q_add4 will + * produce a set inequality between the reads/writes into (pc, round, wnaf_slice) + */ + + /** + * @brief Addition relation + * + * All addition operations in ECCVMMSMRelationImpl are conditional additions! + * This method returns two Accumulators that represent x/y coord of output. + * Output is either an addition of inputs, or xa/ya dpeending on value of `selector`. + * Additionally, we require `lambda = 0` if `selector = 0`. + * The `collision_relation` accumulator tracks a subrelation that validates xb != xa. + * Repeated calls to this method will increase the max degree of the Accumulator output + * Degree of x_out, y_out = max degree of x_a/x_b + 1 + * 4 Iterations will produce an output degree of 6 + */ + auto add = [&](auto& xb, + auto& yb, + auto& xa, + auto& ya, + auto& lambda, + auto& selector, + auto& relation, + auto& collision_relation) { + // (L * (xb - xa) - yb - ya) * s = 0 + // L * (1 - s) = 0 + // (combine) (L * (xb - xa - 1) - yb - ya) * s + L = 0 + relation += selector * (lambda * (xb - xa - 1) - (yb - ya)) + lambda; + collision_relation += selector * (xb - xa); + // x3 = L.L + (-xb - xa) * q + (1 - q) xa + auto x_out = lambda * lambda + (-xb - xa - xa) * selector + xa; + + // y3 = L . (xa - x3) - ya * q + (1 - q) ya + auto y_out = lambda * (xa - x_out) + (-ya - ya) * selector + ya; + return std::array{ x_out, y_out }; + }; + + // ADD operations (if row represents ADD round, not SKEW or DOUBLE) + Accumulator add_relation(0); + Accumulator x1_collision_relation(0); + Accumulator x2_collision_relation(0); + Accumulator x3_collision_relation(0); + Accumulator x4_collision_relation(0); + // If msm_transition = 1, we have started a new MSM. We need to treat the current value of [Acc] as the point at + // infinity! + auto add_into_accumulator = -msm_transition + 1; + auto [x_t1, y_t1] = add(acc_x, acc_y, x1, y1, lambda1, add_into_accumulator, add_relation, x1_collision_relation); + auto [x_t2, y_t2] = add(x2, y2, x_t1, y_t1, lambda2, add2, add_relation, x2_collision_relation); + auto [x_t3, y_t3] = add(x3, y3, x_t2, y_t2, lambda3, add3, add_relation, x3_collision_relation); + auto [x_t4, y_t4] = add(x4, y4, x_t3, y_t3, lambda4, add4, add_relation, x4_collision_relation); + + // Validate accumulator output matches ADD output if q_add = 1 + // (this is a degree-6 relation) + std::get<0>(accumulator) += q_add * (acc_x_shift - x_t4) * scaling_factor; + std::get<1>(accumulator) += q_add * (acc_y_shift - y_t4) * scaling_factor; + std::get<2>(accumulator) += q_add * add_relation * scaling_factor; + + /** + * @brief doubles a point. + * + * Degree of x_out = 2 + * Degree of y_out = 3 + * Degree of relation = 4 + */ + auto dbl = [&](auto& x, auto& y, auto& lambda, auto& relation) { + auto two_x = x + x; + relation += lambda * (y + y) - (two_x + x) * x; + auto x_out = lambda * lambda - two_x; + auto y_out = lambda * (x - x_out) - y; + return std::array{ x_out, y_out }; + }; + + /** + * @brief + * + * Algorithm to determine if round is a DOUBLE round: + * 1. count_shift >= msm_size + * 2. round != 32 + * + * Algorithm to process MSM DOUBLE round: + * [Acc_shift] = (([Acc].double()).double()).double() + * + * As with additions, the column q_double describes whether row is a double round. It is Prover-defined. + * The value of `msm_round` can only update when `q_double = 1` and we use this to ensure Prover correctly sets + * `q_double`. (see round transition relations further down) + */ + Accumulator double_relation(0); + auto [x_d1, y_d1] = dbl(acc_x, acc_y, lambda1, double_relation); + auto [x_d2, y_d2] = dbl(x_d1, y_d1, lambda2, double_relation); + auto [x_d3, y_d3] = dbl(x_d2, y_d2, lambda3, double_relation); + auto [x_d4, y_d4] = dbl(x_d3, y_d3, lambda4, double_relation); + std::get<10>(accumulator) += q_double * (acc_x_shift - x_d4) * scaling_factor; + std::get<11>(accumulator) += q_double * (acc_y_shift - y_d4) * scaling_factor; + std::get<12>(accumulator) += q_double * double_relation * scaling_factor; + + /** + * @brief SKEW operations + * When computing x * [P], if x is even we must subtract [P] from accumulator + * (this is because our windowed non-adjacent-form can only represent odd numbers) + * Round 32 represents "skew" round. + * If scalar slice == 7, we add into accumulator (point_table[7] maps to -[P]) + * If scalar slice == 0, we do not add into accumulator + * i.e. for the skew round we can use the slice values as our "selector" when doing conditional point adds + */ + Accumulator skew_relation(0); + static constexpr FF inverse_seven = FF(7).invert(); + auto skew1_select = slice1 * inverse_seven; + auto skew2_select = slice2 * inverse_seven; + auto skew3_select = slice3 * inverse_seven; + auto skew4_select = slice4 * inverse_seven; + Accumulator x1_skew_collision_relation(0); + Accumulator x2_skew_collision_relation(0); + Accumulator x3_skew_collision_relation(0); + Accumulator x4_skew_collision_relation(0); + // add skew points iff row is a SKEW row AND slice = 7 (point_table[7] maps to -[P]) + // N.B. while it would be nice to have one `add` relation for both ADD and SKEW rounds, + // this would increase degree of sumcheck identity vs evaluating them separately. + // This is because, for add rounds, the result of adding [P1], [Acc] is [P1 + Acc] or [P1] + // but for skew rounds, the result of adding [P1], [Acc] is [P1 + Acc] or [Acc] + auto [x_s1, y_s1] = add(x1, y1, acc_x, acc_y, lambda1, skew1_select, skew_relation, x1_skew_collision_relation); + auto [x_s2, y_s2] = add(x2, y2, x_s1, y_s1, lambda2, skew2_select, skew_relation, x2_skew_collision_relation); + auto [x_s3, y_s3] = add(x3, y3, x_s2, y_s2, lambda3, skew3_select, skew_relation, x3_skew_collision_relation); + auto [x_s4, y_s4] = add(x4, y4, x_s3, y_s3, lambda4, skew4_select, skew_relation, x4_skew_collision_relation); + + // Validate accumulator output matches SKEW output if q_skew = 1 + // (this is a degree-6 relation) + std::get<3>(accumulator) += q_skew * (acc_x_shift - x_s4) * scaling_factor; + std::get<4>(accumulator) += q_skew * (acc_y_shift - y_s4) * scaling_factor; + std::get<5>(accumulator) += q_skew * skew_relation * scaling_factor; + + // Check x-coordinates do not collide if row is an ADD row or a SKEW row + // if either q_add or q_skew = 1, an inverse should exist for each computed relation + // Step 1: construct boolean selectors that describe whether we added a point at the current row + const auto add_first_point = add_into_accumulator * q_add + q_skew * skew1_select; + const auto add_second_point = add2 * q_add + q_skew * skew2_select; + const auto add_third_point = add3 * q_add + q_skew * skew3_select; + const auto add_fourth_point = add4 * q_add + q_skew * skew4_select; + // Step 2: construct the delta between x-coordinates for each point add (depending on if row is ADD or SKEW) + const auto x1_delta = x1_skew_collision_relation * q_skew + x1_collision_relation * q_add; + const auto x2_delta = x2_skew_collision_relation * q_skew + x2_collision_relation * q_add; + const auto x3_delta = x3_skew_collision_relation * q_skew + x3_collision_relation * q_add; + const auto x4_delta = x4_skew_collision_relation * q_skew + x4_collision_relation * q_add; + // Step 3: x_delta * inverse - 1 = 0 if we performed a point addition (else x_delta * inverse = 0) + std::get<6>(accumulator) += (x1_delta * collision_inverse1 - add_first_point) * scaling_factor; + std::get<7>(accumulator) += (x2_delta * collision_inverse2 - add_second_point) * scaling_factor; + std::get<8>(accumulator) += (x3_delta * collision_inverse3 - add_third_point) * scaling_factor; + std::get<9>(accumulator) += (x4_delta * collision_inverse4 - add_fourth_point) * scaling_factor; + + // Validate that if q_add = 1 or q_skew = 1, add1 also is 1 + // TODO(@zac-williamson) Once we have a stable base to work off of, remove q_add1 and replace with q_msm_add + + // q_msm_skew (issue #2222) + std::get<32>(accumulator) += (add1 - q_add - q_skew) * scaling_factor; + + // If add_i = 0, slice_i = 0 + // When add_i = 0, force slice_i to ALSO be 0 + std::get<13>(accumulator) += (-add1 + 1) * slice1 * scaling_factor; + std::get<14>(accumulator) += (-add2 + 1) * slice2 * scaling_factor; + std::get<15>(accumulator) += (-add3 + 1) * slice3 * scaling_factor; + std::get<16>(accumulator) += (-add4 + 1) * slice4 * scaling_factor; + + // only one of q_skew, q_double, q_add can be nonzero + std::get<17>(accumulator) += (q_add * q_double + q_add * q_skew + q_double * q_skew) * scaling_factor; + + // We look up wnaf slices by mapping round + pc -> slice + // We use an exact set membership check to validate that + // wnafs written in wnaf_relation == wnafs read in msm relation + // We use `add1/add2/add3/add4` to flag whether we are performing a wnaf read op + // We can set these to be Prover-defined as the set membership check implicitly ensures that the correct reads + // have occurred. + // if msm_transition = 0, round_shift - round = 0 or 1 + const auto round_delta = round_shift - round; + + // ROUND TRANSITION LOGIC (when round does not change) + // If msm_transition = 0 (next row) then round_delta = 0 or 1 + const auto round_transition = round_delta * (-msm_transition_shift + 1); + std::get<18>(accumulator) += round_transition * (round_delta - 1) * scaling_factor; + + // ROUND TRANSITION LOGIC (when round DOES change) + // round_transition describes whether we are transitioning between rounds of an MSM + // If round_transition = 1, the next row is either a double (if round != 31) or we are adding skew (if round == + // 31) round_transition * skew * (round - 31) = 0 (if round tx and skew, round == 31) round_transition * (skew + + // double - 1) = 0 (if round tx, skew XOR double = 1) i.e. if round tx and round != 31, double = 1 + std::get<19>(accumulator) += round_transition * q_skew_shift * (round - 31) * scaling_factor; + std::get<20>(accumulator) += round_transition * (q_skew_shift + q_double_shift - 1) * scaling_factor; + + // if no double or no skew, round_delta = 0 + std::get<21>(accumulator) += round_transition * (-q_double_shift + 1) * (-q_skew_shift + 1) * scaling_factor; + + // if double, next double != 1 + std::get<22>(accumulator) += q_double * q_double_shift * scaling_factor; + + // if double, next add = 1 + std::get<23>(accumulator) += q_double * (-q_add_shift + 1) * scaling_factor; + + // updating count + // if msm_transition = 0 and round_transition = 0, count_shift = count + add1 + add2 + add3 + add4 + // todo: we need this? + std::get<24>(accumulator) += (-msm_transition_shift + 1) * (-round_delta + 1) * + (count_shift - count - add1 - add2 - add3 - add4) * scaling_factor; + + std::get<25>(accumulator) += + is_not_first_row * (-msm_transition_shift + 1) * round_delta * count_shift * scaling_factor; + + // if msm_transition = 1, count_shift = 0 + std::get<26>(accumulator) += is_not_first_row * msm_transition_shift * count_shift * scaling_factor; + + // if msm_transition = 1, pc = pc_shift + msm_size + // `ecc_set_relation` ensures `msm_size` maps to `transcript.msm_count` for the current value of `pc` + std::get<27>(accumulator) += is_not_first_row * msm_transition_shift * (msm_size + pc_shift - pc) * scaling_factor; + + // Addition continuity checks + // We want to RULE OUT the following scenarios: + // Case 1: add2 = 1, add1 = 0 + // Case 2: add3 = 1, add2 = 0 + // Case 3: add4 = 1, add3 = 0 + // These checks ensure that the current row does not skip points (for both ADD and SKEW ops) + // This is part of a wider set of checks we use to ensure that all point data is used in the assigned + // multiscalar multiplication operation. + // (and not in a different MSM operation) + std::get<28>(accumulator) += add2 * (-add1 + 1) * scaling_factor; + std::get<29>(accumulator) += add3 * (-add2 + 1) * scaling_factor; + std::get<30>(accumulator) += add4 * (-add3 + 1) * scaling_factor; + + // Final continuity check. + // If an addition spans two rows, we need to make sure that the following scenario is RULED OUT: + // add4 = 0 on the CURRENT row, add1 = 1 on the NEXT row + // We must apply the above for the two cases: + // Case 1: q_add = 1 on the CURRENT row, q_add = 1 on the NEXT row + // Case 2: q_skew = 1 on the CURRENT row, q_skew = 1 on the NEXT row + // (i.e. if q_skew = 1, q_add_shift = 1 this implies an MSM transition so we skip this continuity check) + std::get<31>(accumulator) += + (q_add * q_add_shift + q_skew * q_skew_shift) * (-add4 + 1) * add1_shift * scaling_factor; + + // remaining checks (done in ecc_set_relation.hpp, ecc_lookup_relation.hpp) + // when transition occurs, perform set membership lookup on (accumulator / pc / msm_size) + // perform set membership lookups on add_i * (pc / round / slice_i) + // perform lookups on (pc / slice_i / x / y) +} +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation.cpp index 0efec02d548..ce12ce062ac 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation.cpp @@ -1,177 +1,8 @@ -#include "ecc_point_table_relation.hpp" #include "barretenberg/eccvm/eccvm_flavor.hpp" #include "barretenberg/flavor/relation_definitions.hpp" +#include "ecc_point_table_relation_impl.hpp" namespace bb { - -/** - * @brief ECCVMPointTableRelationImpl - * @details These relations define the set of point lookup tables we will use in `ecc_msm_relation.hpp`, to evaluate - * multiscalar multiplication. For every point [P] = (Px, Py) involved in an MSM, we need to do define a lookup - * table out of the following points: { -15[P], -13[P], -11[P], -9[P], -7[P], -5[P], -3[P], -[P] } - * ECCVMPointTableRelationImpl defines relations that define the lookup table. - * - * @param evals transformed to `evals + C(in(X)...)*scaling_factor` - * @param in an std::array containing the fully extended Accumulator edges. - * @param parameters contains beta, gamma, and public_input_delta, .... - * @param scaling_factor optional term to scale the evaluation before adding to evals. - */ -template -template -void ECCVMPointTableRelationImpl::accumulate(ContainerOverSubrelations& accumulator, - const AllEntities& in, - const Parameters& /*unused*/, - const FF& scaling_factor) -{ - using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - - const auto& Tx = View(in.precompute_tx); - const auto& Tx_shift = View(in.precompute_tx_shift); - const auto& Ty = View(in.precompute_ty); - const auto& Ty_shift = View(in.precompute_ty_shift); - const auto& Dx = View(in.precompute_dx); - const auto& Dx_shift = View(in.precompute_dx_shift); - const auto& Dy = View(in.precompute_dy); - const auto& Dy_shift = View(in.precompute_dy_shift); - const auto& precompute_point_transition = View(in.precompute_point_transition); - const auto& lagrange_first = View(in.lagrange_first); - - /** - * @brief Row structure - * - * Consider the set of (128-bit scalar multiplier, point, pc) tuples in the transcript columns. - * The point table columns process one tuple every 8 rows. The tuple with the largest pc value is first. - * When transitioning between tuple elements, pc decrements by 1. - * - * The following table gives an example for two points. - * In the table, the point associated with `pc = 1` is labelled P. - * the point associated with `pc = 0` is labelled Q. - * - * | precompute_pc | precompute_point_transition | precompute_round | Tx | Ty | Dx | Dy | - * | -------- | ----------------------- | ----------- | ----- | ----- | ---- | ---- | - * | 1 | 0 | 0 |15P.x | 15P.y | 2P.x | 2P.y | - * | 1 | 0 | 1 |13P.x | 13P.y | 2P.x | 2P.y | - * | 1 | 0 | 2 |11P.x | 11P.y | 2P.x | 2P.y | - * | 1 | 0 | 3 | 9P.x | 9P.y | 2P.x | 2P.y | - * | 1 | 0 | 4 | 7P.x | 7P.y | 2P.x | 2P.y | - * | 1 | 0 | 5 | 5P.x | 5P.y | 2P.x | 2P.y | - * | 1 | 0 | 6 | 3P.x | 3P.y | 2P.x | 2P.y | - * | 1 | 1 | 7 | P.x | P.y | 2P.x | 2P.y | - * | 0 | 0 | 0 |15Q.x | 15Q.y | 2Q.x | 2Q.y | - * | 0 | 0 | 1 |13Q.x | 13Q.y | 2Q.x | 2Q.y | - * | 0 | 0 | 2 |11Q.x | 11Q.y | 2Q.x | 2Q.y | - * | 0 | 0 | 3 | 9Q.x | 9Q.y | 2Q.x | 2Q.y | - * | 0 | 0 | 4 | 7Q.x | 7Q.y | 2Q.x | 2Q.y | - * | 0 | 0 | 5 | 5Q.x | 5Q.y | 2Q.x | 2Q.y | - * | 0 | 0 | 6 | 3Q.x | 3Q.y | 2Q.x | 2Q.y | - * | 0 | 1 | 7 | Q.x | Q.y | 2Q.x | 2Q.y | - * - * We apply the following relations to constrain the above table: - * - * 1. If precompute_point_transition = 0, (Dx, Dy) = (Dx_shift, Dy_shift) - * 2. If precompute_point_transition = 1, (Dx, Dy) = 2 (Px, Py) - * 3. If precompute_point_transition = 0, (Tx, Ty) = (Tx_shift, Ty_shift) + (Dx, Dy) - * - * The relations that constrain `precompute_point_transition` and `precompute_pc` are in `ecc_wnaf_relation.hpp` - * - * When precompute_point_transition = 1, we use a strict lookup protocol in `ecc_set_relation.hpp` to validate (pc, - * Tx, Ty) belong to the set of points present in our transcript columns. - * ("strict" lookup protocol = every item in the table must be read from once, and only once) - * - * For every row, we use a lookup protocol in `ecc_lookup_relation.hpp` to write the following tuples into a lookup - * table: - * 1. (pc, 15 - precompute_round, Tx, Ty) - * 2. (pc, precompute_round, Tx, -Ty) - * - * The value `15 - precompute_round` describes the multiplier applied to P at the current row. - * (this can be expanded into a wnaf value by taking `2x - 15` where `x = 15 - precompute_round`) . - * The value `precompute_round` describes the *negative multiplier* applied to P at the current row. - * This is also expanded into a wnaf value by taking `2x - 15` where `x = precompute_round`. - * - * The following table describes how taking (15 - precompute_round) for positive values and (precompute_round) for - * negative values produces the WNAF slice values that correspond to the multipliers for (Tx, Ty) and (Tx, -Ty): - * - * | Tx | Ty | x = 15 - precompute_round | 2x - 15 | y = precompute_round | 2y - 15 | - * | ----- | ----- | -------------------- | ------- | --------------- | ------- | - * | 15P.x | 15P.y | 15 | 15 | 0 | -15 | - * | 13P.x | 13P.y | 14 | 13 | 1 | -13 | - * | 11P.x | 11P.y | 13 | 11 | 2 | -11 | - * | 9P.x | 9P.y | 12 | 9 | 3 | -9 | - * | 7P.x | 7P.y | 11 | 7 | 4 | -7 | - * | 5P.x | 5P.y | 10 | 5 | 5 | -5 | - * | 3P.x | 3P.y | 9 | 3 | 6 | -3 | - * | P.x | P.y | 8 | 1 | 7 | -1 | - */ - - /** - * @brief Validate Dx, Dy correctness relation - * - * When computing a point table for point [P] = (Px, Py), we require [D] (Dx, Dy) = 2.[P] - * If all other relations are satisfied, we know that (Tx, Ty) = (Px, Py) - * i.e. (Dx, Dy) = 2(Px, Py) when precompute_round_transition = 1. - * - * Double formula: - * x_3 = 9x^4 / 4y^2 - 2x - * y_3 = (3x^2 / 2y) * (x - x_3) - y - * - * Expanding into relations: - * (x_3 + 2x) * 4y^2 - 9x^4 = 0 - * (y3 + y) * 2y - 3x^2 * (x - x_3) = 0 - */ - auto two_x = Tx + Tx; - auto three_x = two_x + Tx; - auto three_xx = Tx * three_x; - auto nine_xxxx = three_xx * three_xx; - auto two_y = Ty + Ty; - auto four_yy = two_y * two_y; - auto x_double_check = (Dx + two_x) * four_yy - nine_xxxx; - auto y_double_check = (Ty + Dy) * two_y + three_xx * (Dx - Tx); - std::get<0>(accumulator) += precompute_point_transition * x_double_check * scaling_factor; - std::get<1>(accumulator) += precompute_point_transition * y_double_check * scaling_factor; - - /** - * @brief If precompute_round_transition = 0, (Dx_shift, Dy_shift) = (Dx, Dy) - * - * 1st row is empty => don't apply if lagrange_first == 1 - */ - std::get<2>(accumulator) += - (-lagrange_first + 1) * (-precompute_point_transition + 1) * (Dx - Dx_shift) * scaling_factor; - std::get<3>(accumulator) += - (-lagrange_first + 1) * (-precompute_point_transition + 1) * (Dy - Dy_shift) * scaling_factor; - - /** - * @brief Valdiate (Tx, Ty) is correctly computed from (Tx_shift, Ty_shift), (Dx, Dy). - * If precompute_round_transition = 0, [T] = [T_shift] + [D]. - * - * Add formula: - * x_3 = (y_2 - y_1)^2 / (x_2 - x_1)^2 - x_2 - x_1 - * y_3 = ((y_2 - y_1) / (x_2 - x_1)) * (x_1 - x_3) - y_1 - * - * Expanding into relations: - * (x_3 + x_2 + x_1) * (x_2 - x_1)^2 - (y_2 - y_1)^2 = 0 - * (y_3 + y_1) * (x_2 - x_1) + (x_3 - x_1) * (y_2 - y_1) = 0 - * - * We don't need to check for incomplete point addition edge case (x_1 == x_2) - * TODO explain why (computing simple point multiples cannot trigger the edge cases, but need to serve a proof of - * this...) - */ - const auto& x1 = Tx_shift; - const auto& y1 = Ty_shift; - const auto& x2 = Dx; - const auto& y2 = Dy; - const auto& x3 = Tx; - const auto& y3 = Ty; - const auto lambda_numerator = y2 - y1; - const auto lambda_denominator = x2 - x1; - auto x_add_check = (x3 + x2 + x1) * lambda_denominator * lambda_denominator - lambda_numerator * lambda_numerator; - auto y_add_check = (y3 + y1) * lambda_denominator + (x3 - x1) * lambda_numerator; - std::get<4>(accumulator) += - (-lagrange_first + 1) * (-precompute_point_transition + 1) * x_add_check * scaling_factor; - std::get<5>(accumulator) += - (-lagrange_first + 1) * (-precompute_point_transition + 1) * y_add_check * scaling_factor; -} - template class ECCVMPointTableRelationImpl; DEFINE_SUMCHECK_RELATION_CLASS(ECCVMPointTableRelationImpl, ECCVMFlavor); diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation_impl.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation_impl.hpp new file mode 100644 index 00000000000..efc80367f28 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation_impl.hpp @@ -0,0 +1,173 @@ +#pragma once +#include "ecc_point_table_relation.hpp" + +namespace bb { + +/** + * @brief ECCVMPointTableRelationImpl + * @details These relations define the set of point lookup tables we will use in `ecc_msm_relation.hpp`, to evaluate + * multiscalar multiplication. For every point [P] = (Px, Py) involved in an MSM, we need to do define a lookup + * table out of the following points: { -15[P], -13[P], -11[P], -9[P], -7[P], -5[P], -3[P], -[P] } + * ECCVMPointTableRelationImpl defines relations that define the lookup table. + * + * @param evals transformed to `evals + C(in(X)...)*scaling_factor` + * @param in an std::array containing the fully extended Accumulator edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ +template +template +void ECCVMPointTableRelationImpl::accumulate(ContainerOverSubrelations& accumulator, + const AllEntities& in, + const Parameters& /*unused*/, + const FF& scaling_factor) +{ + using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; + using View = typename Accumulator::View; + + const auto& Tx = View(in.precompute_tx); + const auto& Tx_shift = View(in.precompute_tx_shift); + const auto& Ty = View(in.precompute_ty); + const auto& Ty_shift = View(in.precompute_ty_shift); + const auto& Dx = View(in.precompute_dx); + const auto& Dx_shift = View(in.precompute_dx_shift); + const auto& Dy = View(in.precompute_dy); + const auto& Dy_shift = View(in.precompute_dy_shift); + const auto& precompute_point_transition = View(in.precompute_point_transition); + const auto& lagrange_first = View(in.lagrange_first); + + /** + * @brief Row structure + * + * Consider the set of (128-bit scalar multiplier, point, pc) tuples in the transcript columns. + * The point table columns process one tuple every 8 rows. The tuple with the largest pc value is first. + * When transitioning between tuple elements, pc decrements by 1. + * + * The following table gives an example for two points. + * In the table, the point associated with `pc = 1` is labelled P. + * the point associated with `pc = 0` is labelled Q. + * + * | precompute_pc | precompute_point_transition | precompute_round | Tx | Ty | Dx | Dy | + * | -------- | ----------------------- | ----------- | ----- | ----- | ---- | ---- | + * | 1 | 0 | 0 |15P.x | 15P.y | 2P.x | 2P.y | + * | 1 | 0 | 1 |13P.x | 13P.y | 2P.x | 2P.y | + * | 1 | 0 | 2 |11P.x | 11P.y | 2P.x | 2P.y | + * | 1 | 0 | 3 | 9P.x | 9P.y | 2P.x | 2P.y | + * | 1 | 0 | 4 | 7P.x | 7P.y | 2P.x | 2P.y | + * | 1 | 0 | 5 | 5P.x | 5P.y | 2P.x | 2P.y | + * | 1 | 0 | 6 | 3P.x | 3P.y | 2P.x | 2P.y | + * | 1 | 1 | 7 | P.x | P.y | 2P.x | 2P.y | + * | 0 | 0 | 0 |15Q.x | 15Q.y | 2Q.x | 2Q.y | + * | 0 | 0 | 1 |13Q.x | 13Q.y | 2Q.x | 2Q.y | + * | 0 | 0 | 2 |11Q.x | 11Q.y | 2Q.x | 2Q.y | + * | 0 | 0 | 3 | 9Q.x | 9Q.y | 2Q.x | 2Q.y | + * | 0 | 0 | 4 | 7Q.x | 7Q.y | 2Q.x | 2Q.y | + * | 0 | 0 | 5 | 5Q.x | 5Q.y | 2Q.x | 2Q.y | + * | 0 | 0 | 6 | 3Q.x | 3Q.y | 2Q.x | 2Q.y | + * | 0 | 1 | 7 | Q.x | Q.y | 2Q.x | 2Q.y | + * + * We apply the following relations to constrain the above table: + * + * 1. If precompute_point_transition = 0, (Dx, Dy) = (Dx_shift, Dy_shift) + * 2. If precompute_point_transition = 1, (Dx, Dy) = 2 (Px, Py) + * 3. If precompute_point_transition = 0, (Tx, Ty) = (Tx_shift, Ty_shift) + (Dx, Dy) + * + * The relations that constrain `precompute_point_transition` and `precompute_pc` are in `ecc_wnaf_relation.hpp` + * + * When precompute_point_transition = 1, we use a strict lookup protocol in `ecc_set_relation.hpp` to validate (pc, + * Tx, Ty) belong to the set of points present in our transcript columns. + * ("strict" lookup protocol = every item in the table must be read from once, and only once) + * + * For every row, we use a lookup protocol in `ecc_lookup_relation.hpp` to write the following tuples into a lookup + * table: + * 1. (pc, 15 - precompute_round, Tx, Ty) + * 2. (pc, precompute_round, Tx, -Ty) + * + * The value `15 - precompute_round` describes the multiplier applied to P at the current row. + * (this can be expanded into a wnaf value by taking `2x - 15` where `x = 15 - precompute_round`) . + * The value `precompute_round` describes the *negative multiplier* applied to P at the current row. + * This is also expanded into a wnaf value by taking `2x - 15` where `x = precompute_round`. + * + * The following table describes how taking (15 - precompute_round) for positive values and (precompute_round) for + * negative values produces the WNAF slice values that correspond to the multipliers for (Tx, Ty) and (Tx, -Ty): + * + * | Tx | Ty | x = 15 - precompute_round | 2x - 15 | y = precompute_round | 2y - 15 | + * | ----- | ----- | -------------------- | ------- | --------------- | ------- | + * | 15P.x | 15P.y | 15 | 15 | 0 | -15 | + * | 13P.x | 13P.y | 14 | 13 | 1 | -13 | + * | 11P.x | 11P.y | 13 | 11 | 2 | -11 | + * | 9P.x | 9P.y | 12 | 9 | 3 | -9 | + * | 7P.x | 7P.y | 11 | 7 | 4 | -7 | + * | 5P.x | 5P.y | 10 | 5 | 5 | -5 | + * | 3P.x | 3P.y | 9 | 3 | 6 | -3 | + * | P.x | P.y | 8 | 1 | 7 | -1 | + */ + + /** + * @brief Validate Dx, Dy correctness relation + * + * When computing a point table for point [P] = (Px, Py), we require [D] (Dx, Dy) = 2.[P] + * If all other relations are satisfied, we know that (Tx, Ty) = (Px, Py) + * i.e. (Dx, Dy) = 2(Px, Py) when precompute_round_transition = 1. + * + * Double formula: + * x_3 = 9x^4 / 4y^2 - 2x + * y_3 = (3x^2 / 2y) * (x - x_3) - y + * + * Expanding into relations: + * (x_3 + 2x) * 4y^2 - 9x^4 = 0 + * (y3 + y) * 2y - 3x^2 * (x - x_3) = 0 + */ + auto two_x = Tx + Tx; + auto three_x = two_x + Tx; + auto three_xx = Tx * three_x; + auto nine_xxxx = three_xx * three_xx; + auto two_y = Ty + Ty; + auto four_yy = two_y * two_y; + auto x_double_check = (Dx + two_x) * four_yy - nine_xxxx; + auto y_double_check = (Ty + Dy) * two_y + three_xx * (Dx - Tx); + std::get<0>(accumulator) += precompute_point_transition * x_double_check * scaling_factor; + std::get<1>(accumulator) += precompute_point_transition * y_double_check * scaling_factor; + + /** + * @brief If precompute_round_transition = 0, (Dx_shift, Dy_shift) = (Dx, Dy) + * + * 1st row is empty => don't apply if lagrange_first == 1 + */ + std::get<2>(accumulator) += + (-lagrange_first + 1) * (-precompute_point_transition + 1) * (Dx - Dx_shift) * scaling_factor; + std::get<3>(accumulator) += + (-lagrange_first + 1) * (-precompute_point_transition + 1) * (Dy - Dy_shift) * scaling_factor; + + /** + * @brief Valdiate (Tx, Ty) is correctly computed from (Tx_shift, Ty_shift), (Dx, Dy). + * If precompute_round_transition = 0, [T] = [T_shift] + [D]. + * + * Add formula: + * x_3 = (y_2 - y_1)^2 / (x_2 - x_1)^2 - x_2 - x_1 + * y_3 = ((y_2 - y_1) / (x_2 - x_1)) * (x_1 - x_3) - y_1 + * + * Expanding into relations: + * (x_3 + x_2 + x_1) * (x_2 - x_1)^2 - (y_2 - y_1)^2 = 0 + * (y_3 + y_1) * (x_2 - x_1) + (x_3 - x_1) * (y_2 - y_1) = 0 + * + * We don't need to check for incomplete point addition edge case (x_1 == x_2) + * TODO explain why (computing simple point multiples cannot trigger the edge cases, but need to serve a proof of + * this...) + */ + const auto& x1 = Tx_shift; + const auto& y1 = Ty_shift; + const auto& x2 = Dx; + const auto& y2 = Dy; + const auto& x3 = Tx; + const auto& y3 = Ty; + const auto lambda_numerator = y2 - y1; + const auto lambda_denominator = x2 - x1; + auto x_add_check = (x3 + x2 + x1) * lambda_denominator * lambda_denominator - lambda_numerator * lambda_numerator; + auto y_add_check = (y3 + y1) * lambda_denominator + (x3 - x1) * lambda_numerator; + std::get<4>(accumulator) += + (-lagrange_first + 1) * (-precompute_point_transition + 1) * x_add_check * scaling_factor; + std::get<5>(accumulator) += + (-lagrange_first + 1) * (-precompute_point_transition + 1) * y_add_check * scaling_factor; +} +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation.cpp index 3fa0e512058..03e8565fcb4 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation.cpp @@ -1,398 +1,8 @@ #include "barretenberg/eccvm/eccvm_flavor.hpp" #include "barretenberg/flavor/relation_definitions.hpp" -#include "ecc_msm_relation.hpp" +#include "ecc_set_relation_impl.hpp" namespace bb { - -/** - * @brief Performs list-equivalence checks for the ECCVM - * - * @details ECCVMSetRelationImpl validates the correctness of the inputs/outputs of the three main algorithms evaluated - * by the ECCVM. - * - * First term: tuple of (pc, round, wnaf_slice), computed when slicing scalar multipliers into slices, - * as part of ECCVMWnafRelation - * Input source: ECCVMWnafRelation - * Output source: ECCVMMSMRelation - * - * - * Second term: tuple of (point-counter, P.x, P.y, scalar-multiplier), used in ECCVMWnafRelation and - * ECCVMPointTableRelation - * Input source: ECCVMPointTableRelation - * Output source: ECCVMMSMRelation - * - * Third term: tuple of (point-counter, P.x, P.y, msm-size) from ECCVMMSMRelation - * Input source: ECCVMMSMRelation - * Output source: ECCVMTranscriptRelation - * - * @tparam FF - * @tparam AccumulatorTypes - * @param in - * @param relation_params - * @param index - * @return ECCVMSetRelationImpl::template Accumulator - */ -template -template -Accumulator ECCVMSetRelationImpl::compute_grand_product_numerator(const AllEntities& in, const Parameters& params) -{ - using View = typename Accumulator::View; - - const auto& precompute_round = View(in.precompute_round); - const auto precompute_round2 = precompute_round + precompute_round; - const auto precompute_round4 = precompute_round2 + precompute_round2; - - const auto& gamma = params.gamma; - const auto& beta = params.beta; - const auto& beta_sqr = params.beta_sqr; - const auto& beta_cube = params.beta_cube; - const auto& precompute_pc = View(in.precompute_pc); - const auto& precompute_select = View(in.precompute_select); - - /** - * @brief First term: tuple of (pc, round, wnaf_slice), computed when slicing scalar multipliers into slices, - * as part of ECCVMWnafRelation. - * If precompute_select = 1, tuple entry = (wnaf-slice + point-counter * beta + msm-round * beta_sqr). - * There are 4 tuple entries per row. - */ - Accumulator numerator(1); // degree-0 - { - const auto& s0 = View(in.precompute_s1hi); - const auto& s1 = View(in.precompute_s1lo); - - auto wnaf_slice = s0 + s0; - wnaf_slice += wnaf_slice; - wnaf_slice += s1; - - // TODO(@zac-williamson #2226) optimize - const auto wnaf_slice_input0 = wnaf_slice + gamma + precompute_pc * beta + precompute_round4 * beta_sqr; - numerator *= wnaf_slice_input0; // degree-1 - } - { - const auto& s0 = View(in.precompute_s2hi); - const auto& s1 = View(in.precompute_s2lo); - - auto wnaf_slice = s0 + s0; - wnaf_slice += wnaf_slice; - wnaf_slice += s1; - - // TODO(@zac-williamson #2226) optimize - const auto wnaf_slice_input1 = wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 1) * beta_sqr; - numerator *= wnaf_slice_input1; // degree-2 - } - { - const auto& s0 = View(in.precompute_s3hi); - const auto& s1 = View(in.precompute_s3lo); - - auto wnaf_slice = s0 + s0; - wnaf_slice += wnaf_slice; - wnaf_slice += s1; - - // TODO(@zac-williamson #2226) optimize - const auto wnaf_slice_input2 = wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 2) * beta_sqr; - numerator *= wnaf_slice_input2; // degree-3 - } - { - const auto& s0 = View(in.precompute_s4hi); - const auto& s1 = View(in.precompute_s4lo); - - auto wnaf_slice = s0 + s0; - wnaf_slice += wnaf_slice; - wnaf_slice += s1; - // TODO(@zac-williamson #2226) optimize - const auto wnaf_slice_input3 = wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 3) * beta_sqr; - numerator *= wnaf_slice_input3; // degree-4 - } - { - // skew product if relevant - const auto& skew = View(in.precompute_skew); - const auto& precompute_point_transition = View(in.precompute_point_transition); - const auto skew_input = - precompute_point_transition * (skew + gamma + precompute_pc * beta + (precompute_round4 + 4) * beta_sqr) + - (-precompute_point_transition + 1); - numerator *= skew_input; // degree-5 - } - { - const auto& eccvm_set_permutation_delta = params.eccvm_set_permutation_delta; - numerator *= precompute_select * (-eccvm_set_permutation_delta + 1) + eccvm_set_permutation_delta; // degree-7 - } - - /** - * @brief Second term: tuple of (point-counter, P.x, P.y, scalar-multiplier), used in ECCVMWnafRelation and - * ECCVMPointTableRelation. ECCVMWnafRelation validates the sum of the wnaf slices associated with point-counter - * equals scalar-multiplier. ECCVMPointTableRelation computes a table of muliples of [P]: { -15[P], -13[P], ..., - * 15[P] }. We need to validate that scalar-multiplier and [P] = (P.x, P.y) come from MUL opcodes in the transcript - * columns. - */ - { - const auto& table_x = View(in.precompute_tx); - const auto& table_y = View(in.precompute_ty); - - const auto& precompute_skew = View(in.precompute_skew); - static constexpr FF negative_inverse_seven = FF(-7).invert(); - auto adjusted_skew = precompute_skew * negative_inverse_seven; - - const auto& wnaf_scalar_sum = View(in.precompute_scalar_sum); - const auto w0 = convert_to_wnaf(View(in.precompute_s1hi), View(in.precompute_s1lo)); - const auto w1 = convert_to_wnaf(View(in.precompute_s2hi), View(in.precompute_s2lo)); - const auto w2 = convert_to_wnaf(View(in.precompute_s3hi), View(in.precompute_s3lo)); - const auto w3 = convert_to_wnaf(View(in.precompute_s4hi), View(in.precompute_s4lo)); - - auto row_slice = w0; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += w1; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += w2; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += w3; - - auto scalar_sum_full = wnaf_scalar_sum + wnaf_scalar_sum; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += row_slice + adjusted_skew; - - auto precompute_point_transition = View(in.precompute_point_transition); - - auto point_table_init_read = - (precompute_pc + table_x * beta + table_y * beta_sqr + scalar_sum_full * beta_cube); - point_table_init_read = - precompute_point_transition * (point_table_init_read + gamma) + (-precompute_point_transition + 1); - - numerator *= point_table_init_read; // degree-9 - } - /** - * @brief Third term: tuple of (point-counter, P.x, P.y, msm-size) from ECCVMMSMRelation. - * (P.x, P.y) is the output of a multi-scalar-multiplication evaluated in ECCVMMSMRelation. - * We need to validate that the same values (P.x, P.y) are present in the Transcript columns and describe a - * multi-scalar multiplication of size `msm-size`, starting at `point-counter`. - * - * If msm_transition_shift = 1, this indicates the current row is the last row of a multiscalar - * multiplication evaluation. The output of the MSM will be present on `(msm_accumulator_x_shift, - * msm_accumulator_y_shift)`. The values of `msm_accumulator_x_shift, msm_accumulator_y_shift, msm_pc, - * msm_size_of_msm` must match up with equivalent values `transcript_msm_output_x, transcript_msm_output_y, - * transcript_pc, transcript_msm_count` present in the Transcript columns - */ - { - const auto& lagrange_first = View(in.lagrange_first); - const auto& partial_msm_transition_shift = View(in.msm_transition_shift); - const auto msm_transition_shift = (-lagrange_first + 1) * partial_msm_transition_shift; - const auto& msm_pc_shift = View(in.msm_pc_shift); - - const auto& msm_x_shift = View(in.msm_accumulator_x_shift); - const auto& msm_y_shift = View(in.msm_accumulator_y_shift); - const auto& msm_size = View(in.msm_size_of_msm); - - // msm_transition = 1 when a row BEGINS a new msm - // - // row msm tx acc.x acc.y pc msm_size - // i 0 no no no yes - // i+1 1 yes yes yes no - // - // at row i we are at the final row of the current msm - // at row i the value of `msm_size` = size of current msm - // at row i + 1 we have the final accumulated value of the msm computation - // at row i + 1 we have updated `pc` to be `(pc at start of msm) + msm_count` - // at row i + 1 q_msm_transtiion = 1 - - auto msm_result_write = msm_pc_shift + msm_x_shift * beta + msm_y_shift * beta_sqr + msm_size * beta_cube; - - // msm_result_write = degree 2 - msm_result_write = msm_transition_shift * (msm_result_write + gamma) + (-msm_transition_shift + 1); - numerator *= msm_result_write; // degree-11 - } - return numerator; -} - -template -template -Accumulator ECCVMSetRelationImpl::compute_grand_product_denominator(const AllEntities& in, const Parameters& params) -{ - using View = typename Accumulator::View; - - // TODO(@zac-williamson). The degree of this contribution is 17! makes overall relation degree 19. - // Can optimise by refining the algebra, once we have a stable base to iterate off of. - const auto& gamma = params.gamma; - const auto& beta = params.beta; - const auto& beta_sqr = params.beta_sqr; - const auto& beta_cube = params.beta_cube; - const auto& msm_pc = View(in.msm_pc); - const auto& msm_count = View(in.msm_count); - const auto& msm_round = View(in.msm_round); - - /** - * @brief First term: tuple of (pc, round, wnaf_slice), used to determine which points we extract from lookup tables - * when evaluaing MSMs in ECCVMMsmRelation. - * These values must be equivalent to the values computed in the 1st term of `compute_grand_product_numerator` - */ - Accumulator denominator(1); // degree-0 - { - const auto& add1 = View(in.msm_add1); - const auto& msm_slice1 = View(in.msm_slice1); - - auto wnaf_slice_output1 = - add1 * (msm_slice1 + gamma + (msm_pc - msm_count) * beta + msm_round * beta_sqr) + (-add1 + 1); - denominator *= wnaf_slice_output1; // degree-2 - } - { - const auto& add2 = View(in.msm_add2); - const auto& msm_slice2 = View(in.msm_slice2); - - auto wnaf_slice_output2 = - add2 * (msm_slice2 + gamma + (msm_pc - msm_count - 1) * beta + msm_round * beta_sqr) + (-add2 + 1); - denominator *= wnaf_slice_output2; // degree-4 - } - { - const auto& add3 = View(in.msm_add3); - const auto& msm_slice3 = View(in.msm_slice3); - - auto wnaf_slice_output3 = - add3 * (msm_slice3 + gamma + (msm_pc - msm_count - 2) * beta + msm_round * beta_sqr) + (-add3 + 1); - denominator *= wnaf_slice_output3; // degree-6 - } - { - const auto& add4 = View(in.msm_add4); - const auto& msm_slice4 = View(in.msm_slice4); - auto wnaf_slice_output4 = - add4 * (msm_slice4 + gamma + (msm_pc - msm_count - 3) * beta + msm_round * beta_sqr) + (-add4 + 1); - denominator *= wnaf_slice_output4; // degree-8 - } - - /** - * @brief Second term: tuple of (transcript_pc, transcript_Px, transcript_Py, z1) OR (transcript_pc, \lambda * - * transcript_Px, -transcript_Py, z2) for each scalar multiplication in ECCVMTranscriptRelation columns. (the latter - * term uses the curve endomorphism: \lambda = cube root of unity). These values must be equivalent to the second - * term values in `compute_grand_product_numerator` - */ - { - const auto& transcript_pc = View(in.transcript_pc); - - auto transcript_Px = View(in.transcript_Px); - auto transcript_Py = View(in.transcript_Py); - auto z1 = View(in.transcript_z1); - auto z2 = View(in.transcript_z2); - auto z1_zero = View(in.transcript_z1zero); - auto z2_zero = View(in.transcript_z2zero); - auto transcript_mul = View(in.transcript_mul); - - auto lookup_first = (-z1_zero + 1); - auto lookup_second = (-z2_zero + 1); - FF endomorphism_base_field_shift = FF::cube_root_of_unity(); - - auto transcript_input1 = transcript_pc + transcript_Px * beta + transcript_Py * beta_sqr + z1 * beta_cube; - auto transcript_input2 = (transcript_pc - 1) + transcript_Px * endomorphism_base_field_shift * beta - - transcript_Py * beta_sqr + z2 * beta_cube; - - // | q_mul | z2_zero | z1_zero | lookup | - // | ----- | ------- | ------- | ---------------------- | - // | 0 | - | - | 1 | - // | 1 | 0 | 1 | X + gamma | - // | 1 | 1 | 0 | Y + gamma | - // | 1 | 1 | 1 | (X + gamma)(Y + gamma) | - transcript_input1 = (transcript_input1 + gamma) * lookup_first + (-lookup_first + 1); - transcript_input2 = (transcript_input2 + gamma) * lookup_second + (-lookup_second + 1); - // point_table_init_write = degree 2 - - auto point_table_init_write = transcript_mul * transcript_input1 * transcript_input2 + (-transcript_mul + 1); - denominator *= point_table_init_write; // degree-13 - - // auto point_table_init_write_1 = transcript_mul * transcript_input1 + (-transcript_mul + 1); - // denominator *= point_table_init_write_1; // degree-11 - - // auto point_table_init_write_2 = transcript_mul * transcript_input2 + (-transcript_mul + 1); - // denominator *= point_table_init_write_2; // degree-14 - } - /** - * @brief Third term: tuple of (point-counter, P.x, P.y, msm-size) from ECCVMTranscriptRelation. - * (P.x, P.y) is the *claimed* output of a multi-scalar-multiplication evaluated in ECCVMMSMRelation. - * We need to validate that the msm output produced in ECCVMMSMRelation is equivalent to the output present - * in `transcript_msm_output_x, transcript_msm_output_y`, for a given multi-scalar multiplication starting at - * `transcript_pc` and has size `transcript_msm_count` - */ - { - auto transcript_pc_shift = View(in.transcript_pc_shift); - auto transcript_msm_x = View(in.transcript_msm_x); - auto transcript_msm_y = View(in.transcript_msm_y); - auto transcript_msm_transition = View(in.transcript_msm_transition); - auto transcript_msm_count = View(in.transcript_msm_count); - auto z1_zero = View(in.transcript_z1zero); - auto z2_zero = View(in.transcript_z2zero); - auto transcript_mul = View(in.transcript_mul); - - auto full_msm_count = transcript_msm_count + transcript_mul * ((-z1_zero + 1) + (-z2_zero + 1)); - // auto count_test = transcript_msm_count - // msm_result_read = degree 2 - auto msm_result_read = - transcript_pc_shift + transcript_msm_x * beta + transcript_msm_y * beta_sqr + full_msm_count * beta_cube; - - msm_result_read = transcript_msm_transition * (msm_result_read + gamma) + (-transcript_msm_transition + 1); - denominator *= msm_result_read; // degree-17 - } - return denominator; -} - -/** - * @brief Expression for the StandardArithmetic gate. - * @dbetails The relation is defined as C(in(X)...) = - * (q_m * w_r * w_l) + (q_l * w_l) + (q_r * w_r) + (q_o * w_o) + q_c - * - * @param evals transformed to `evals + C(in(X)...)*scaling_factor` - * @param in an std::array containing the fully extended Accumulator edges. - * @param parameters contains beta, gamma, and public_input_delta, .... - * @param scaling_factor optional term to scale the evaluation before adding to evals. - */ -template -template -void ECCVMSetRelationImpl::accumulate(ContainerOverSubrelations& accumulator, - const AllEntities& in, - const Parameters& params, - const FF& scaling_factor) -{ - using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - - // degree-11 - Accumulator numerator_evaluation = compute_grand_product_numerator(in, params); - - // degree-17 - Accumulator denominator_evaluation = compute_grand_product_denominator(in, params); - - const auto& lagrange_first = View(in.lagrange_first); - const auto& lagrange_last = View(in.lagrange_last); - - const auto& z_perm = View(in.z_perm); - const auto& z_perm_shift = View(in.z_perm_shift); - - // degree-18 - std::get<0>(accumulator) += - ((z_perm + lagrange_first) * numerator_evaluation - (z_perm_shift + lagrange_last) * denominator_evaluation) * - scaling_factor; - - // Contribution (2) - std::get<1>(accumulator) += (lagrange_last * z_perm_shift) * scaling_factor; -} - template class ECCVMSetRelationImpl; DEFINE_SUMCHECK_RELATION_CLASS(ECCVMSetRelationImpl, ECCVMFlavor); DEFINE_SUMCHECK_PERMUTATION_CLASS(ECCVMSetRelationImpl, ECCVMFlavor); diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation_impl.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation_impl.hpp new file mode 100644 index 00000000000..67ca79375e7 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation_impl.hpp @@ -0,0 +1,394 @@ +#pragma once +#include "ecc_set_relation.hpp" + +namespace bb { + +/** + * @brief Performs list-equivalence checks for the ECCVM + * + * @details ECCVMSetRelationImpl validates the correctness of the inputs/outputs of the three main algorithms evaluated + * by the ECCVM. + * + * First term: tuple of (pc, round, wnaf_slice), computed when slicing scalar multipliers into slices, + * as part of ECCVMWnafRelation + * Input source: ECCVMWnafRelation + * Output source: ECCVMMSMRelation + * + * + * Second term: tuple of (point-counter, P.x, P.y, scalar-multiplier), used in ECCVMWnafRelation and + * ECCVMPointTableRelation + * Input source: ECCVMPointTableRelation + * Output source: ECCVMMSMRelation + * + * Third term: tuple of (point-counter, P.x, P.y, msm-size) from ECCVMMSMRelation + * Input source: ECCVMMSMRelation + * Output source: ECCVMTranscriptRelation + * + * @tparam FF + * @tparam AccumulatorTypes + * @param in + * @param relation_params + * @param index + * @return ECCVMSetRelationImpl::template Accumulator + */ +template +template +Accumulator ECCVMSetRelationImpl::compute_grand_product_numerator(const AllEntities& in, const Parameters& params) +{ + using View = typename Accumulator::View; + + const auto& precompute_round = View(in.precompute_round); + const auto precompute_round2 = precompute_round + precompute_round; + const auto precompute_round4 = precompute_round2 + precompute_round2; + + const auto& gamma = params.gamma; + const auto& beta = params.beta; + const auto& beta_sqr = params.beta_sqr; + const auto& beta_cube = params.beta_cube; + const auto& precompute_pc = View(in.precompute_pc); + const auto& precompute_select = View(in.precompute_select); + + /** + * @brief First term: tuple of (pc, round, wnaf_slice), computed when slicing scalar multipliers into slices, + * as part of ECCVMWnafRelation. + * If precompute_select = 1, tuple entry = (wnaf-slice + point-counter * beta + msm-round * beta_sqr). + * There are 4 tuple entries per row. + */ + Accumulator numerator(1); // degree-0 + { + const auto& s0 = View(in.precompute_s1hi); + const auto& s1 = View(in.precompute_s1lo); + + auto wnaf_slice = s0 + s0; + wnaf_slice += wnaf_slice; + wnaf_slice += s1; + + // TODO(@zac-williamson #2226) optimize + const auto wnaf_slice_input0 = wnaf_slice + gamma + precompute_pc * beta + precompute_round4 * beta_sqr; + numerator *= wnaf_slice_input0; // degree-1 + } + { + const auto& s0 = View(in.precompute_s2hi); + const auto& s1 = View(in.precompute_s2lo); + + auto wnaf_slice = s0 + s0; + wnaf_slice += wnaf_slice; + wnaf_slice += s1; + + // TODO(@zac-williamson #2226) optimize + const auto wnaf_slice_input1 = wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 1) * beta_sqr; + numerator *= wnaf_slice_input1; // degree-2 + } + { + const auto& s0 = View(in.precompute_s3hi); + const auto& s1 = View(in.precompute_s3lo); + + auto wnaf_slice = s0 + s0; + wnaf_slice += wnaf_slice; + wnaf_slice += s1; + + // TODO(@zac-williamson #2226) optimize + const auto wnaf_slice_input2 = wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 2) * beta_sqr; + numerator *= wnaf_slice_input2; // degree-3 + } + { + const auto& s0 = View(in.precompute_s4hi); + const auto& s1 = View(in.precompute_s4lo); + + auto wnaf_slice = s0 + s0; + wnaf_slice += wnaf_slice; + wnaf_slice += s1; + // TODO(@zac-williamson #2226) optimize + const auto wnaf_slice_input3 = wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 3) * beta_sqr; + numerator *= wnaf_slice_input3; // degree-4 + } + { + // skew product if relevant + const auto& skew = View(in.precompute_skew); + const auto& precompute_point_transition = View(in.precompute_point_transition); + const auto skew_input = + precompute_point_transition * (skew + gamma + precompute_pc * beta + (precompute_round4 + 4) * beta_sqr) + + (-precompute_point_transition + 1); + numerator *= skew_input; // degree-5 + } + { + const auto& eccvm_set_permutation_delta = params.eccvm_set_permutation_delta; + numerator *= precompute_select * (-eccvm_set_permutation_delta + 1) + eccvm_set_permutation_delta; // degree-7 + } + + /** + * @brief Second term: tuple of (point-counter, P.x, P.y, scalar-multiplier), used in ECCVMWnafRelation and + * ECCVMPointTableRelation. ECCVMWnafRelation validates the sum of the wnaf slices associated with point-counter + * equals scalar-multiplier. ECCVMPointTableRelation computes a table of muliples of [P]: { -15[P], -13[P], ..., + * 15[P] }. We need to validate that scalar-multiplier and [P] = (P.x, P.y) come from MUL opcodes in the transcript + * columns. + */ + { + const auto& table_x = View(in.precompute_tx); + const auto& table_y = View(in.precompute_ty); + + const auto& precompute_skew = View(in.precompute_skew); + static constexpr FF negative_inverse_seven = FF(-7).invert(); + auto adjusted_skew = precompute_skew * negative_inverse_seven; + + const auto& wnaf_scalar_sum = View(in.precompute_scalar_sum); + const auto w0 = convert_to_wnaf(View(in.precompute_s1hi), View(in.precompute_s1lo)); + const auto w1 = convert_to_wnaf(View(in.precompute_s2hi), View(in.precompute_s2lo)); + const auto w2 = convert_to_wnaf(View(in.precompute_s3hi), View(in.precompute_s3lo)); + const auto w3 = convert_to_wnaf(View(in.precompute_s4hi), View(in.precompute_s4lo)); + + auto row_slice = w0; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += w1; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += w2; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += w3; + + auto scalar_sum_full = wnaf_scalar_sum + wnaf_scalar_sum; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += row_slice + adjusted_skew; + + auto precompute_point_transition = View(in.precompute_point_transition); + + auto point_table_init_read = + (precompute_pc + table_x * beta + table_y * beta_sqr + scalar_sum_full * beta_cube); + point_table_init_read = + precompute_point_transition * (point_table_init_read + gamma) + (-precompute_point_transition + 1); + + numerator *= point_table_init_read; // degree-9 + } + /** + * @brief Third term: tuple of (point-counter, P.x, P.y, msm-size) from ECCVMMSMRelation. + * (P.x, P.y) is the output of a multi-scalar-multiplication evaluated in ECCVMMSMRelation. + * We need to validate that the same values (P.x, P.y) are present in the Transcript columns and describe a + * multi-scalar multiplication of size `msm-size`, starting at `point-counter`. + * + * If msm_transition_shift = 1, this indicates the current row is the last row of a multiscalar + * multiplication evaluation. The output of the MSM will be present on `(msm_accumulator_x_shift, + * msm_accumulator_y_shift)`. The values of `msm_accumulator_x_shift, msm_accumulator_y_shift, msm_pc, + * msm_size_of_msm` must match up with equivalent values `transcript_msm_output_x, transcript_msm_output_y, + * transcript_pc, transcript_msm_count` present in the Transcript columns + */ + { + const auto& lagrange_first = View(in.lagrange_first); + const auto& partial_msm_transition_shift = View(in.msm_transition_shift); + const auto msm_transition_shift = (-lagrange_first + 1) * partial_msm_transition_shift; + const auto& msm_pc_shift = View(in.msm_pc_shift); + + const auto& msm_x_shift = View(in.msm_accumulator_x_shift); + const auto& msm_y_shift = View(in.msm_accumulator_y_shift); + const auto& msm_size = View(in.msm_size_of_msm); + + // msm_transition = 1 when a row BEGINS a new msm + // + // row msm tx acc.x acc.y pc msm_size + // i 0 no no no yes + // i+1 1 yes yes yes no + // + // at row i we are at the final row of the current msm + // at row i the value of `msm_size` = size of current msm + // at row i + 1 we have the final accumulated value of the msm computation + // at row i + 1 we have updated `pc` to be `(pc at start of msm) + msm_count` + // at row i + 1 q_msm_transtiion = 1 + + auto msm_result_write = msm_pc_shift + msm_x_shift * beta + msm_y_shift * beta_sqr + msm_size * beta_cube; + + // msm_result_write = degree 2 + msm_result_write = msm_transition_shift * (msm_result_write + gamma) + (-msm_transition_shift + 1); + numerator *= msm_result_write; // degree-11 + } + return numerator; +} + +template +template +Accumulator ECCVMSetRelationImpl::compute_grand_product_denominator(const AllEntities& in, const Parameters& params) +{ + using View = typename Accumulator::View; + + // TODO(@zac-williamson). The degree of this contribution is 17! makes overall relation degree 19. + // Can optimise by refining the algebra, once we have a stable base to iterate off of. + const auto& gamma = params.gamma; + const auto& beta = params.beta; + const auto& beta_sqr = params.beta_sqr; + const auto& beta_cube = params.beta_cube; + const auto& msm_pc = View(in.msm_pc); + const auto& msm_count = View(in.msm_count); + const auto& msm_round = View(in.msm_round); + + /** + * @brief First term: tuple of (pc, round, wnaf_slice), used to determine which points we extract from lookup tables + * when evaluaing MSMs in ECCVMMsmRelation. + * These values must be equivalent to the values computed in the 1st term of `compute_grand_product_numerator` + */ + Accumulator denominator(1); // degree-0 + { + const auto& add1 = View(in.msm_add1); + const auto& msm_slice1 = View(in.msm_slice1); + + auto wnaf_slice_output1 = + add1 * (msm_slice1 + gamma + (msm_pc - msm_count) * beta + msm_round * beta_sqr) + (-add1 + 1); + denominator *= wnaf_slice_output1; // degree-2 + } + { + const auto& add2 = View(in.msm_add2); + const auto& msm_slice2 = View(in.msm_slice2); + + auto wnaf_slice_output2 = + add2 * (msm_slice2 + gamma + (msm_pc - msm_count - 1) * beta + msm_round * beta_sqr) + (-add2 + 1); + denominator *= wnaf_slice_output2; // degree-4 + } + { + const auto& add3 = View(in.msm_add3); + const auto& msm_slice3 = View(in.msm_slice3); + + auto wnaf_slice_output3 = + add3 * (msm_slice3 + gamma + (msm_pc - msm_count - 2) * beta + msm_round * beta_sqr) + (-add3 + 1); + denominator *= wnaf_slice_output3; // degree-6 + } + { + const auto& add4 = View(in.msm_add4); + const auto& msm_slice4 = View(in.msm_slice4); + auto wnaf_slice_output4 = + add4 * (msm_slice4 + gamma + (msm_pc - msm_count - 3) * beta + msm_round * beta_sqr) + (-add4 + 1); + denominator *= wnaf_slice_output4; // degree-8 + } + + /** + * @brief Second term: tuple of (transcript_pc, transcript_Px, transcript_Py, z1) OR (transcript_pc, \lambda * + * transcript_Px, -transcript_Py, z2) for each scalar multiplication in ECCVMTranscriptRelation columns. (the latter + * term uses the curve endomorphism: \lambda = cube root of unity). These values must be equivalent to the second + * term values in `compute_grand_product_numerator` + */ + { + const auto& transcript_pc = View(in.transcript_pc); + + auto transcript_Px = View(in.transcript_Px); + auto transcript_Py = View(in.transcript_Py); + auto z1 = View(in.transcript_z1); + auto z2 = View(in.transcript_z2); + auto z1_zero = View(in.transcript_z1zero); + auto z2_zero = View(in.transcript_z2zero); + auto transcript_mul = View(in.transcript_mul); + + auto lookup_first = (-z1_zero + 1); + auto lookup_second = (-z2_zero + 1); + FF endomorphism_base_field_shift = FF::cube_root_of_unity(); + + auto transcript_input1 = transcript_pc + transcript_Px * beta + transcript_Py * beta_sqr + z1 * beta_cube; + auto transcript_input2 = (transcript_pc - 1) + transcript_Px * endomorphism_base_field_shift * beta - + transcript_Py * beta_sqr + z2 * beta_cube; + + // | q_mul | z2_zero | z1_zero | lookup | + // | ----- | ------- | ------- | ---------------------- | + // | 0 | - | - | 1 | + // | 1 | 0 | 1 | X + gamma | + // | 1 | 1 | 0 | Y + gamma | + // | 1 | 1 | 1 | (X + gamma)(Y + gamma) | + transcript_input1 = (transcript_input1 + gamma) * lookup_first + (-lookup_first + 1); + transcript_input2 = (transcript_input2 + gamma) * lookup_second + (-lookup_second + 1); + // point_table_init_write = degree 2 + + auto point_table_init_write = transcript_mul * transcript_input1 * transcript_input2 + (-transcript_mul + 1); + denominator *= point_table_init_write; // degree-13 + + // auto point_table_init_write_1 = transcript_mul * transcript_input1 + (-transcript_mul + 1); + // denominator *= point_table_init_write_1; // degree-11 + + // auto point_table_init_write_2 = transcript_mul * transcript_input2 + (-transcript_mul + 1); + // denominator *= point_table_init_write_2; // degree-14 + } + /** + * @brief Third term: tuple of (point-counter, P.x, P.y, msm-size) from ECCVMTranscriptRelation. + * (P.x, P.y) is the *claimed* output of a multi-scalar-multiplication evaluated in ECCVMMSMRelation. + * We need to validate that the msm output produced in ECCVMMSMRelation is equivalent to the output present + * in `transcript_msm_output_x, transcript_msm_output_y`, for a given multi-scalar multiplication starting at + * `transcript_pc` and has size `transcript_msm_count` + */ + { + auto transcript_pc_shift = View(in.transcript_pc_shift); + auto transcript_msm_x = View(in.transcript_msm_x); + auto transcript_msm_y = View(in.transcript_msm_y); + auto transcript_msm_transition = View(in.transcript_msm_transition); + auto transcript_msm_count = View(in.transcript_msm_count); + auto z1_zero = View(in.transcript_z1zero); + auto z2_zero = View(in.transcript_z2zero); + auto transcript_mul = View(in.transcript_mul); + + auto full_msm_count = transcript_msm_count + transcript_mul * ((-z1_zero + 1) + (-z2_zero + 1)); + // auto count_test = transcript_msm_count + // msm_result_read = degree 2 + auto msm_result_read = + transcript_pc_shift + transcript_msm_x * beta + transcript_msm_y * beta_sqr + full_msm_count * beta_cube; + + msm_result_read = transcript_msm_transition * (msm_result_read + gamma) + (-transcript_msm_transition + 1); + denominator *= msm_result_read; // degree-17 + } + return denominator; +} + +/** + * @brief Expression for the StandardArithmetic gate. + * @dbetails The relation is defined as C(in(X)...) = + * (q_m * w_r * w_l) + (q_l * w_l) + (q_r * w_r) + (q_o * w_o) + q_c + * + * @param evals transformed to `evals + C(in(X)...)*scaling_factor` + * @param in an std::array containing the fully extended Accumulator edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ +template +template +void ECCVMSetRelationImpl::accumulate(ContainerOverSubrelations& accumulator, + const AllEntities& in, + const Parameters& params, + const FF& scaling_factor) +{ + using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; + using View = typename Accumulator::View; + + // degree-11 + Accumulator numerator_evaluation = compute_grand_product_numerator(in, params); + + // degree-17 + Accumulator denominator_evaluation = compute_grand_product_denominator(in, params); + + const auto& lagrange_first = View(in.lagrange_first); + const auto& lagrange_last = View(in.lagrange_last); + + const auto& z_perm = View(in.z_perm); + const auto& z_perm_shift = View(in.z_perm_shift); + + // degree-18 + std::get<0>(accumulator) += + ((z_perm + lagrange_first) * numerator_evaluation - (z_perm_shift + lagrange_last) * denominator_evaluation) * + scaling_factor; + + // Contribution (2) + std::get<1>(accumulator) += (lagrange_last * z_perm_shift) * scaling_factor; +} +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation.cpp index 4e7a4cdbdb6..79834907a99 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation.cpp @@ -1,261 +1,11 @@ #include #include -#include "./ecc_transcript_relation.hpp" +#include "./ecc_transcript_relation_impl.hpp" #include "barretenberg/eccvm/eccvm_flavor.hpp" #include "barretenberg/flavor/relation_definitions.hpp" namespace bb { - -/** - * @brief ECCVMTranscriptRelationImpl evaluates the correctness of the ECCVM transcript columns - * - * @details The transcript relations directly evaluate the correctness of `add, eq, reset` operations. - * `mul` operations are lazily evaluated. The output of multiscalar multiplications is present in - * `transcript_msm_x, transcript_msm_y` columns. A set equality check is used to validate these - * have been correctly read from a table produced by the relations in `ecc_msm_relation.hpp`. - * - * Sequential `mul` opcodes are interpreted as a multiscalar multiplication. - * The column `transcript_msm_count` tracks the number of muls in a given multiscalar multiplication. - * - * The column `transcript_pc` tracks a "point counter" value, that describes the number of multiplications - * that must be evaluated. - * - * One mul opcode can generate up to TWO multiplications. Each 128-bit scalar `z1, z2` is treated as an independent mul. - * The purpose of this is to reduce the length of the MSM algorithm evalauted in `ecc_msm_relation.hpp` to 128 bits - * (from 256 bits). Many scalar muls required to recursively verify a proof are only 128-bits in length; this prevents - * us doing redundant computation. - * @tparam FF - * @tparam AccumulatorTypes - * @tparam PolynomialTypes - */ -template -template -void ECCVMTranscriptRelationImpl::accumulate(ContainerOverSubrelations& accumulator, - const AllEntities& in, - const Parameters& /*unused*/, - const FF& scaling_factor) -{ - using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - - auto z1 = View(in.transcript_z1); - auto z2 = View(in.transcript_z2); - auto z1_zero = View(in.transcript_z1zero); - auto z2_zero = View(in.transcript_z2zero); - auto op = View(in.transcript_op); - auto q_add = View(in.transcript_add); - auto q_mul = View(in.transcript_mul); - auto q_mul_shift = View(in.transcript_mul_shift); - auto q_eq = View(in.transcript_eq); - auto msm_transition = View(in.transcript_msm_transition); - auto msm_count = View(in.transcript_msm_count); - auto msm_count_shift = View(in.transcript_msm_count_shift); - auto pc = View(in.transcript_pc); - auto pc_shift = View(in.transcript_pc_shift); - auto transcript_accumulator_x_shift = View(in.transcript_accumulator_x_shift); - auto transcript_accumulator_y_shift = View(in.transcript_accumulator_y_shift); - auto transcript_accumulator_x = View(in.transcript_accumulator_x); - auto transcript_accumulator_y = View(in.transcript_accumulator_y); - auto transcript_msm_x = View(in.transcript_msm_x); - auto transcript_msm_y = View(in.transcript_msm_y); - auto transcript_Px = View(in.transcript_Px); - auto transcript_Py = View(in.transcript_Py); - auto is_accumulator_empty = View(in.transcript_accumulator_empty); - auto lagrange_first = View(in.lagrange_first); - auto lagrange_last = View(in.lagrange_last); - auto is_accumulator_empty_shift = View(in.transcript_accumulator_empty_shift); - auto q_reset_accumulator = View(in.transcript_reset_accumulator); - auto lagrange_second = View(in.lagrange_second); - auto transcript_collision_check = View(in.transcript_collision_check); - - auto is_not_first_row = (-lagrange_first + 1); - auto is_not_first_or_last_row = (-lagrange_first + -lagrange_last + 1); - /** - * @brief Validate correctness of z1_zero, z2_zero. - * If z1_zero = 0 and operation is a MUL, we will write a scalar mul instruction into our multiplication table. - * If z1_zero = 1 and operation is a MUL, we will NOT write a scalar mul instruction. - * (same with z2_zero). - * z1_zero / z2_zero is user-defined. - * We constraint z1_zero such that if z1_zero == 1, we require z1 == 0. (same for z2_zero). - * We do *NOT* constrain z1 != 0 if z1_zero = 0. If the user sets z1_zero = 0 and z1 = 0, - * this will add a scalar mul instruction into the multiplication table, where the scalar multiplier is 0. - * This is inefficient but will still produce the correct output. - */ - std::get<0>(accumulator) += (z1 * z1_zero) * scaling_factor; // if z1_zero = 1, z1 must be 0 - std::get<1>(accumulator) += (z2 * z2_zero) * scaling_factor; - - /** - * @brief Validate `op` opcode is well formed. - * `op` is defined to be q_reset_accumulator + 2 * q_eq + 4 * q_mul + 8 * q_add, - * where q_reset_accumulator, q_eq, q_mul, q_add are all boolean - * (TODO: bool constrain these efficiently #2223) - */ - auto tmp = q_add + q_add; - tmp += q_mul; - tmp += tmp; - tmp += q_eq; - tmp += tmp; - tmp += q_reset_accumulator; - std::get<2>(accumulator) += (tmp - op) * scaling_factor; - - /** - * @brief Validate `pc` is updated correctly. - * pc stands for Point Counter. It decrements by 1 for every 128-bit multiplication operation. - * If q_mul = 1, pc decrements by !z1_zero + !z2_zero, else pc decrements by 0 - * @note pc starts out at its max value and decrements down to 0. This keeps the degree of the pc polynomial smol - */ - Accumulator pc_delta = pc - pc_shift; - std::get<3>(accumulator) += - is_not_first_row * (pc_delta - q_mul * ((-z1_zero + 1) + (-z2_zero + 1))) * scaling_factor; - - /** - * @brief Validate `msm_transition` is well-formed. - * - * If the current row is the last mul instruction in a multiscalar multiplication, msm_transition = 1. - * i.e. if q_mul == 1 and q_mul_shift == 0, msm_transition = 1, else is 0 - */ - auto msm_transition_check = q_mul * (-q_mul_shift + 1); - std::get<4>(accumulator) += (msm_transition - msm_transition_check) * scaling_factor; - - /** - * @brief Validate `msm_count` resets when we end a multiscalar multiplication. - * msm_count tracks the number of scalar muls in the current active multiscalar multiplication. - * (if no msm active, msm_count == 0) - * If current row ends an MSM, `msm_count_shift = 0` (msm_count value at next row) - */ - std::get<5>(accumulator) += (msm_transition * msm_count_shift) * scaling_factor; - - /** - * @brief Validate `msm_count` updates correctly for mul operations. - * msm_count updates by (!z1_zero + !z2_zero) if current op is a mul instruction (and msm is not terminating at next - * row). - */ - auto msm_count_delta = msm_count_shift - msm_count; // degree 4 - std::get<6>(accumulator) += is_not_first_row * (-msm_transition + 1) * - (msm_count_delta - q_mul * ((-z1_zero + 1) + (-z2_zero + 1))) * scaling_factor; - - /** - * @brief Add multiscalar multiplication result into transcript accumulator. - * If `msm_transition == 1`, we expect msm output to be present on (transcript_msm_x, transcript_msm_y). - * (this is enforced via a lookup protocol). - * If `is_accumulator_empty == 0`, we ADD msm output into transcript_accumulator. - * If `is_accumulator_empty = =1`, we ASSIGN msm output to transcript_accumulator. - * @note the output of an msm cannot be point at infinity (will create unsatisfiable constraints in - * ecc_msm_relation). We assume this does not affect statistical completeness for honest provers. We should validate - * this! - */ - auto add_msm_into_accumulator = msm_transition * (-is_accumulator_empty + 1); - auto x3 = transcript_accumulator_x_shift; - auto y3 = transcript_accumulator_y_shift; - auto x1 = transcript_accumulator_x; - auto y1 = transcript_accumulator_y; - auto x2 = transcript_msm_x; - auto y2 = transcript_msm_y; - auto tmpx = (x3 + x2 + x1) * (x2 - x1) * (x2 - x1) - (y2 - y1) * (y2 - y1); - auto tmpy = (y3 + y1) * (x2 - x1) - (y2 - y1) * (x1 - x3); - std::get<7>(accumulator) += tmpx * add_msm_into_accumulator * scaling_factor; // degree 5 - std::get<8>(accumulator) += tmpy * add_msm_into_accumulator * scaling_factor; // degree 4 - - /** - * @brief If is_accumulator_empty == 1, assign transcript_accumulator output into accumulator - * - * @note The accumulator point for all operations at row `i` is the accumulator point at row `i + 1`! - */ - auto assign_msm_into_accumulator = msm_transition * is_accumulator_empty; - std::get<9>(accumulator) += assign_msm_into_accumulator * (x3 - x2) * scaling_factor; // degree 3 - std::get<10>(accumulator) += assign_msm_into_accumulator * (y3 - y2) * scaling_factor; - - /** - * @brief Constrain `add` opcode. - * - * add will add the input point in (transcript_Px, transcript_Py) into the accumulator. - * Correctly handles case where accumulator is point at infinity. - * TODO: need to add constraints to rule out point doubling case (x2 != x1) - * TODO: need to assert input point is on the curve! - */ - x2 = transcript_Px; - y2 = transcript_Py; - auto add_into_accumulator = q_add * (-is_accumulator_empty + 1); - tmpx = (x3 + x2 + x1) * (x2 - x1) * (x2 - x1) - (y2 - y1) * (y2 - y1); - tmpy = (y3 + y1) * (x2 - x1) - (y2 - y1) * (x1 - x3); - std::get<11>(accumulator) += tmpx * add_into_accumulator * scaling_factor; // degree 5 - std::get<12>(accumulator) += tmpy * add_into_accumulator * scaling_factor; // degree 4 - auto assign_into_accumulator = q_add * is_accumulator_empty; - std::get<13>(accumulator) += (x3 - x2) * assign_into_accumulator * scaling_factor; // degree 3 - std::get<14>(accumulator) += (y3 - y2) * assign_into_accumulator * scaling_factor; - - /** - * @brief Opcode exclusion tests. We have the following assertions: - * 1. If q_mul = 1, (q_add, eq, reset) are zero - * 2. If q_reset = 1, is_accumulator_empty at next row = 1 - * 3. If q_add = 1 OR msm_transition = 1, is_accumulator_empty at next row = 0 - * 4. If q_add = 0 AND msm_transition = 0 AND q_reset_accumulator = 0, is_accumulator at next row = current row - * value - * @note point 3: both q_add = 1, msm_transition = 1 cannot occur because of point 1 (msm_transition only 1 when - * q_mul 1) we can use a slightly more efficient relation than a pure binary OR - */ - std::get<15>(accumulator) += q_mul * (q_add + q_eq + q_reset_accumulator) * scaling_factor; - std::get<16>(accumulator) += q_add * (q_mul + q_eq + q_reset_accumulator) * scaling_factor; - std::get<17>(accumulator) += q_reset_accumulator * (-is_accumulator_empty_shift + 1) * scaling_factor; - std::get<18>(accumulator) += (q_add + msm_transition) * is_accumulator_empty_shift * scaling_factor; - auto accumulator_state_not_modified = -(q_add + msm_transition + q_reset_accumulator) + 1; - std::get<19>(accumulator) += accumulator_state_not_modified * is_not_first_or_last_row * - (is_accumulator_empty_shift - is_accumulator_empty) * scaling_factor; - - /** - * @brief `eq` opcode. - * If eq = 1, assert transcript_Px/y = transcript_accumulator_x/y. - * If eq = 1, assert is_accumulator_empty = 0 (input point cannot be point at infinity) - */ - std::get<20>(accumulator) += q_eq * (transcript_accumulator_x - transcript_Px) * scaling_factor; - std::get<21>(accumulator) += - q_eq * (-is_accumulator_empty + 1) * (transcript_accumulator_y - transcript_Py) * scaling_factor; - std::get<22>(accumulator) += q_eq * is_accumulator_empty * scaling_factor; - - // validate selectors are boolean (put somewhere else? these are low degree) - std::get<23>(accumulator) += q_eq * (q_eq - 1) * scaling_factor; - std::get<24>(accumulator) += q_add * (q_add - 1) * scaling_factor; - std::get<25>(accumulator) += q_mul * (q_mul - 1) * scaling_factor; - std::get<26>(accumulator) += q_reset_accumulator * (q_reset_accumulator - 1) * scaling_factor; - std::get<27>(accumulator) += msm_transition * (msm_transition - 1) * scaling_factor; - std::get<28>(accumulator) += is_accumulator_empty * (is_accumulator_empty - 1) * scaling_factor; - std::get<29>(accumulator) += z1_zero * (z1_zero - 1) * scaling_factor; - std::get<30>(accumulator) += z2_zero * (z2_zero - 1) * scaling_factor; - - /** - * @brief Initial condition check on 1st row. - * We require the following values are 0 on 1st row: - * is_accumulator_empty = 1 - * msm_count = 0 - * note...actually second row? bleurgh - * NOTE: we want pc = 0 at lagrange_last :o - */ - std::get<31>(accumulator) += lagrange_second * (-is_accumulator_empty + 1) * scaling_factor; - std::get<32>(accumulator) += lagrange_second * msm_count * scaling_factor; - - /** - * @brief On-curve validation checks. - * If q_mul = 1 OR q_add = 1 OR q_eq = 1, require (transcript_Px, transcript_Py) is valid ecc point - * q_mul/q_add/q_eq mutually exclusive, can represent as sum of 3 - */ - const auto validate_on_curve = q_mul; // q_add + q_mul + q_eq; - const auto on_curve_check = - transcript_Py * transcript_Py - transcript_Px * transcript_Px * transcript_Px - get_curve_b(); - std::get<33>(accumulator) += validate_on_curve * on_curve_check * scaling_factor; - - /** - * @brief If performing an add, validate x-coordintes of inputs do not collide. - * If adding msm output into accumulator, validate x-coordinates of inputs do not collide - */ - auto x_coordinate_collision_check = - add_msm_into_accumulator * ((transcript_msm_x - transcript_accumulator_x) * transcript_collision_check - FF(1)); - x_coordinate_collision_check += - add_into_accumulator * ((transcript_Px - transcript_accumulator_x) * transcript_collision_check - FF(1)); - std::get<34>(accumulator) += x_coordinate_collision_check * scaling_factor; -} - template class ECCVMTranscriptRelationImpl; DEFINE_SUMCHECK_RELATION_CLASS(ECCVMTranscriptRelationImpl, ECCVMFlavor); - } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation_impl.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation_impl.hpp new file mode 100644 index 00000000000..037f79a6390 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation_impl.hpp @@ -0,0 +1,256 @@ +#pragma once +#include +#include + +#include "./ecc_transcript_relation.hpp" + +namespace bb { + +/** + * @brief ECCVMTranscriptRelationImpl evaluates the correctness of the ECCVM transcript columns + * + * @details The transcript relations directly evaluate the correctness of `add, eq, reset` operations. + * `mul` operations are lazily evaluated. The output of multiscalar multiplications is present in + * `transcript_msm_x, transcript_msm_y` columns. A set equality check is used to validate these + * have been correctly read from a table produced by the relations in `ecc_msm_relation.hpp`. + * + * Sequential `mul` opcodes are interpreted as a multiscalar multiplication. + * The column `transcript_msm_count` tracks the number of muls in a given multiscalar multiplication. + * + * The column `transcript_pc` tracks a "point counter" value, that describes the number of multiplications + * that must be evaluated. + * + * One mul opcode can generate up to TWO multiplications. Each 128-bit scalar `z1, z2` is treated as an independent mul. + * The purpose of this is to reduce the length of the MSM algorithm evalauted in `ecc_msm_relation.hpp` to 128 bits + * (from 256 bits). Many scalar muls required to recursively verify a proof are only 128-bits in length; this prevents + * us doing redundant computation. + * @tparam FF + * @tparam AccumulatorTypes + * @tparam PolynomialTypes + */ +template +template +void ECCVMTranscriptRelationImpl::accumulate(ContainerOverSubrelations& accumulator, + const AllEntities& in, + const Parameters& /*unused*/, + const FF& scaling_factor) +{ + using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; + using View = typename Accumulator::View; + + auto z1 = View(in.transcript_z1); + auto z2 = View(in.transcript_z2); + auto z1_zero = View(in.transcript_z1zero); + auto z2_zero = View(in.transcript_z2zero); + auto op = View(in.transcript_op); + auto q_add = View(in.transcript_add); + auto q_mul = View(in.transcript_mul); + auto q_mul_shift = View(in.transcript_mul_shift); + auto q_eq = View(in.transcript_eq); + auto msm_transition = View(in.transcript_msm_transition); + auto msm_count = View(in.transcript_msm_count); + auto msm_count_shift = View(in.transcript_msm_count_shift); + auto pc = View(in.transcript_pc); + auto pc_shift = View(in.transcript_pc_shift); + auto transcript_accumulator_x_shift = View(in.transcript_accumulator_x_shift); + auto transcript_accumulator_y_shift = View(in.transcript_accumulator_y_shift); + auto transcript_accumulator_x = View(in.transcript_accumulator_x); + auto transcript_accumulator_y = View(in.transcript_accumulator_y); + auto transcript_msm_x = View(in.transcript_msm_x); + auto transcript_msm_y = View(in.transcript_msm_y); + auto transcript_Px = View(in.transcript_Px); + auto transcript_Py = View(in.transcript_Py); + auto is_accumulator_empty = View(in.transcript_accumulator_empty); + auto lagrange_first = View(in.lagrange_first); + auto lagrange_last = View(in.lagrange_last); + auto is_accumulator_empty_shift = View(in.transcript_accumulator_empty_shift); + auto q_reset_accumulator = View(in.transcript_reset_accumulator); + auto lagrange_second = View(in.lagrange_second); + auto transcript_collision_check = View(in.transcript_collision_check); + + auto is_not_first_row = (-lagrange_first + 1); + auto is_not_first_or_last_row = (-lagrange_first + -lagrange_last + 1); + /** + * @brief Validate correctness of z1_zero, z2_zero. + * If z1_zero = 0 and operation is a MUL, we will write a scalar mul instruction into our multiplication table. + * If z1_zero = 1 and operation is a MUL, we will NOT write a scalar mul instruction. + * (same with z2_zero). + * z1_zero / z2_zero is user-defined. + * We constraint z1_zero such that if z1_zero == 1, we require z1 == 0. (same for z2_zero). + * We do *NOT* constrain z1 != 0 if z1_zero = 0. If the user sets z1_zero = 0 and z1 = 0, + * this will add a scalar mul instruction into the multiplication table, where the scalar multiplier is 0. + * This is inefficient but will still produce the correct output. + */ + std::get<0>(accumulator) += (z1 * z1_zero) * scaling_factor; // if z1_zero = 1, z1 must be 0 + std::get<1>(accumulator) += (z2 * z2_zero) * scaling_factor; + + /** + * @brief Validate `op` opcode is well formed. + * `op` is defined to be q_reset_accumulator + 2 * q_eq + 4 * q_mul + 8 * q_add, + * where q_reset_accumulator, q_eq, q_mul, q_add are all boolean + * (TODO: bool constrain these efficiently #2223) + */ + auto tmp = q_add + q_add; + tmp += q_mul; + tmp += tmp; + tmp += q_eq; + tmp += tmp; + tmp += q_reset_accumulator; + std::get<2>(accumulator) += (tmp - op) * scaling_factor; + + /** + * @brief Validate `pc` is updated correctly. + * pc stands for Point Counter. It decrements by 1 for every 128-bit multiplication operation. + * If q_mul = 1, pc decrements by !z1_zero + !z2_zero, else pc decrements by 0 + * @note pc starts out at its max value and decrements down to 0. This keeps the degree of the pc polynomial smol + */ + Accumulator pc_delta = pc - pc_shift; + std::get<3>(accumulator) += + is_not_first_row * (pc_delta - q_mul * ((-z1_zero + 1) + (-z2_zero + 1))) * scaling_factor; + + /** + * @brief Validate `msm_transition` is well-formed. + * + * If the current row is the last mul instruction in a multiscalar multiplication, msm_transition = 1. + * i.e. if q_mul == 1 and q_mul_shift == 0, msm_transition = 1, else is 0 + */ + auto msm_transition_check = q_mul * (-q_mul_shift + 1); + std::get<4>(accumulator) += (msm_transition - msm_transition_check) * scaling_factor; + + /** + * @brief Validate `msm_count` resets when we end a multiscalar multiplication. + * msm_count tracks the number of scalar muls in the current active multiscalar multiplication. + * (if no msm active, msm_count == 0) + * If current row ends an MSM, `msm_count_shift = 0` (msm_count value at next row) + */ + std::get<5>(accumulator) += (msm_transition * msm_count_shift) * scaling_factor; + + /** + * @brief Validate `msm_count` updates correctly for mul operations. + * msm_count updates by (!z1_zero + !z2_zero) if current op is a mul instruction (and msm is not terminating at next + * row). + */ + auto msm_count_delta = msm_count_shift - msm_count; // degree 4 + std::get<6>(accumulator) += is_not_first_row * (-msm_transition + 1) * + (msm_count_delta - q_mul * ((-z1_zero + 1) + (-z2_zero + 1))) * scaling_factor; + + /** + * @brief Add multiscalar multiplication result into transcript accumulator. + * If `msm_transition == 1`, we expect msm output to be present on (transcript_msm_x, transcript_msm_y). + * (this is enforced via a lookup protocol). + * If `is_accumulator_empty == 0`, we ADD msm output into transcript_accumulator. + * If `is_accumulator_empty = =1`, we ASSIGN msm output to transcript_accumulator. + * @note the output of an msm cannot be point at infinity (will create unsatisfiable constraints in + * ecc_msm_relation). We assume this does not affect statistical completeness for honest provers. We should validate + * this! + */ + auto add_msm_into_accumulator = msm_transition * (-is_accumulator_empty + 1); + auto x3 = transcript_accumulator_x_shift; + auto y3 = transcript_accumulator_y_shift; + auto x1 = transcript_accumulator_x; + auto y1 = transcript_accumulator_y; + auto x2 = transcript_msm_x; + auto y2 = transcript_msm_y; + auto tmpx = (x3 + x2 + x1) * (x2 - x1) * (x2 - x1) - (y2 - y1) * (y2 - y1); + auto tmpy = (y3 + y1) * (x2 - x1) - (y2 - y1) * (x1 - x3); + std::get<7>(accumulator) += tmpx * add_msm_into_accumulator * scaling_factor; // degree 5 + std::get<8>(accumulator) += tmpy * add_msm_into_accumulator * scaling_factor; // degree 4 + + /** + * @brief If is_accumulator_empty == 1, assign transcript_accumulator output into accumulator + * + * @note The accumulator point for all operations at row `i` is the accumulator point at row `i + 1`! + */ + auto assign_msm_into_accumulator = msm_transition * is_accumulator_empty; + std::get<9>(accumulator) += assign_msm_into_accumulator * (x3 - x2) * scaling_factor; // degree 3 + std::get<10>(accumulator) += assign_msm_into_accumulator * (y3 - y2) * scaling_factor; + + /** + * @brief Constrain `add` opcode. + * + * add will add the input point in (transcript_Px, transcript_Py) into the accumulator. + * Correctly handles case where accumulator is point at infinity. + * TODO: need to add constraints to rule out point doubling case (x2 != x1) + * TODO: need to assert input point is on the curve! + */ + x2 = transcript_Px; + y2 = transcript_Py; + auto add_into_accumulator = q_add * (-is_accumulator_empty + 1); + tmpx = (x3 + x2 + x1) * (x2 - x1) * (x2 - x1) - (y2 - y1) * (y2 - y1); + tmpy = (y3 + y1) * (x2 - x1) - (y2 - y1) * (x1 - x3); + std::get<11>(accumulator) += tmpx * add_into_accumulator * scaling_factor; // degree 5 + std::get<12>(accumulator) += tmpy * add_into_accumulator * scaling_factor; // degree 4 + auto assign_into_accumulator = q_add * is_accumulator_empty; + std::get<13>(accumulator) += (x3 - x2) * assign_into_accumulator * scaling_factor; // degree 3 + std::get<14>(accumulator) += (y3 - y2) * assign_into_accumulator * scaling_factor; + + /** + * @brief Opcode exclusion tests. We have the following assertions: + * 1. If q_mul = 1, (q_add, eq, reset) are zero + * 2. If q_reset = 1, is_accumulator_empty at next row = 1 + * 3. If q_add = 1 OR msm_transition = 1, is_accumulator_empty at next row = 0 + * 4. If q_add = 0 AND msm_transition = 0 AND q_reset_accumulator = 0, is_accumulator at next row = current row + * value + * @note point 3: both q_add = 1, msm_transition = 1 cannot occur because of point 1 (msm_transition only 1 when + * q_mul 1) we can use a slightly more efficient relation than a pure binary OR + */ + std::get<15>(accumulator) += q_mul * (q_add + q_eq + q_reset_accumulator) * scaling_factor; + std::get<16>(accumulator) += q_add * (q_mul + q_eq + q_reset_accumulator) * scaling_factor; + std::get<17>(accumulator) += q_reset_accumulator * (-is_accumulator_empty_shift + 1) * scaling_factor; + std::get<18>(accumulator) += (q_add + msm_transition) * is_accumulator_empty_shift * scaling_factor; + auto accumulator_state_not_modified = -(q_add + msm_transition + q_reset_accumulator) + 1; + std::get<19>(accumulator) += accumulator_state_not_modified * is_not_first_or_last_row * + (is_accumulator_empty_shift - is_accumulator_empty) * scaling_factor; + + /** + * @brief `eq` opcode. + * If eq = 1, assert transcript_Px/y = transcript_accumulator_x/y. + * If eq = 1, assert is_accumulator_empty = 0 (input point cannot be point at infinity) + */ + std::get<20>(accumulator) += q_eq * (transcript_accumulator_x - transcript_Px) * scaling_factor; + std::get<21>(accumulator) += + q_eq * (-is_accumulator_empty + 1) * (transcript_accumulator_y - transcript_Py) * scaling_factor; + std::get<22>(accumulator) += q_eq * is_accumulator_empty * scaling_factor; + + // validate selectors are boolean (put somewhere else? these are low degree) + std::get<23>(accumulator) += q_eq * (q_eq - 1) * scaling_factor; + std::get<24>(accumulator) += q_add * (q_add - 1) * scaling_factor; + std::get<25>(accumulator) += q_mul * (q_mul - 1) * scaling_factor; + std::get<26>(accumulator) += q_reset_accumulator * (q_reset_accumulator - 1) * scaling_factor; + std::get<27>(accumulator) += msm_transition * (msm_transition - 1) * scaling_factor; + std::get<28>(accumulator) += is_accumulator_empty * (is_accumulator_empty - 1) * scaling_factor; + std::get<29>(accumulator) += z1_zero * (z1_zero - 1) * scaling_factor; + std::get<30>(accumulator) += z2_zero * (z2_zero - 1) * scaling_factor; + + /** + * @brief Initial condition check on 1st row. + * We require the following values are 0 on 1st row: + * is_accumulator_empty = 1 + * msm_count = 0 + * note...actually second row? bleurgh + * NOTE: we want pc = 0 at lagrange_last :o + */ + std::get<31>(accumulator) += lagrange_second * (-is_accumulator_empty + 1) * scaling_factor; + std::get<32>(accumulator) += lagrange_second * msm_count * scaling_factor; + + /** + * @brief On-curve validation checks. + * If q_mul = 1 OR q_add = 1 OR q_eq = 1, require (transcript_Px, transcript_Py) is valid ecc point + * q_mul/q_add/q_eq mutually exclusive, can represent as sum of 3 + */ + const auto validate_on_curve = q_mul; // q_add + q_mul + q_eq; + const auto on_curve_check = + transcript_Py * transcript_Py - transcript_Px * transcript_Px * transcript_Px - get_curve_b(); + std::get<33>(accumulator) += validate_on_curve * on_curve_check * scaling_factor; + + /** + * @brief If performing an add, validate x-coordintes of inputs do not collide. + * If adding msm output into accumulator, validate x-coordinates of inputs do not collide + */ + auto x_coordinate_collision_check = + add_msm_into_accumulator * ((transcript_msm_x - transcript_accumulator_x) * transcript_collision_check - FF(1)); + x_coordinate_collision_check += + add_into_accumulator * ((transcript_Px - transcript_accumulator_x) * transcript_collision_check - FF(1)); + std::get<34>(accumulator) += x_coordinate_collision_check * scaling_factor; +} +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation.cpp index 3c7e7ca8433..2ab7a00a381 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation.cpp @@ -1,221 +1,9 @@ -#include "ecc_wnaf_relation.hpp" #include "barretenberg/eccvm/eccvm_flavor.hpp" #include "barretenberg/flavor/relation_definitions.hpp" +#include "ecc_wnaf_relation_impl.hpp" namespace bb { -/** - * @brief ECCVMWnafRelationImpl evaluates relations that convert scalar multipliers into 4-bit WNAF slices - * @details Each WNAF slice is a 4-bit slice representing one of 16 integers { -15, -13, ..., 15 } - * Each WNAF slice is represented via two 2-bit columns (precompute_s1hi, ..., precompute_s4lo) - * One 128-bit scalar multiplier is processed across 8 rows, indexed by a round variable. - * The following table describes the structure for one scalar. - * - * | point_transition | round | slices | skew | scalar_sum | - * | ---------------- | ----- | --------------- | ------ | ------------------------------- | - * | 0 | 0 | s0,s1,s2,s3 | 0 | 0 | - * | 0 | 1 | s4,s5,s6,s7 | 0 | \sum_{i=0}^4 16^i * s_{31 - i} | - * | 0 | 2 | s8,s9,s10,s11 | 0 | \sum_{i=0}^8 16^i * s_{31 - i} | - * | 0 | 3 | s12,s13,s14,s14 | 0 | \sum_{i=0}^12 16^i * s_{31 - i} | - * | 0 | 4 | s16,s17,s18,s19 | 0 | \sum_{i=0}^16 16^i * s_{31 - i} | - * | 0 | 5 | s20,s21,s22,s23 | 0 | \sum_{i=0}^20 16^i * s_{31 - i} | - * | 0 | 6 | s24,s25,s26,s27 | 0 | \sum_{i=0}^24 16^i * s_{31 - i} | - * | 1 | 7 | s28,s29,s30,s31 | s_skew | \sum_{i=0}^28 16^i * s_{31 - i} | - * - * The value of the input scalar is equal to the following: - * - * scalar = 2^16 * scalar_sum + 2^12 * s31 + 2^8 * s30 + 2^4 * s29 + s28 - s_skew - * We use a set equality check in `ecc_set_relation.hpp` to validate the above value maps to the correct input - * scalar for a given value of `pc`. - * - * The column `point_transition` is committed to by the Prover, we must constrain it is correctly computed (see - * `ecc_point_table_relation.cpp` for details) - * - * @tparam FF - * @tparam AccumulatorTypes - */ -template -template -void ECCVMWnafRelationImpl::accumulate(ContainerOverSubrelations& accumulator, - const AllEntities& in, - const Parameters& /*unused*/, - const FF& scaling_factor) -{ - using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - - auto scalar_sum = View(in.precompute_scalar_sum); - auto scalar_sum_new = View(in.precompute_scalar_sum_shift); - auto q_transition = View(in.precompute_point_transition); - auto round = View(in.precompute_round); - auto round_shift = View(in.precompute_round_shift); - auto pc = View(in.precompute_pc); - auto pc_shift = View(in.precompute_pc_shift); - // precompute_select is a boolean column. We only evaluate the ecc_wnaf_relation and the ecc_point_table_relation if - // `precompute_select=1` - auto precompute_select = View(in.precompute_select); - auto precompute_select_shift = View(in.precompute_select_shift); - - const auto& precompute_skew = View(in.precompute_skew); - - const std::array slices{ - View(in.precompute_s1hi), View(in.precompute_s1lo), View(in.precompute_s2hi), View(in.precompute_s2lo), - View(in.precompute_s3hi), View(in.precompute_s3lo), View(in.precompute_s4hi), View(in.precompute_s4lo), - }; - - const auto range_constraint_slice_to_2_bits = [&scaling_factor](const View& s, auto& acc) { - acc += s * (s - 1) * (s - 2) * (s - 3) * scaling_factor; - }; - - const auto convert_to_wnaf = [](const View& s0, const View& s1) { - auto t = s0 + s0; - t += t; - t += s1; - auto naf = t + t - 15; - return naf; - }; - - const auto scaled_transition = q_transition * scaling_factor; - const auto scaled_transition_is_zero = -scaled_transition + scaling_factor; - /** - * @brief Constrain each of our scalar slice chunks (s1, ..., s8) to be 2 bits. - * Doing range checks this way vs permutation-based range check removes need to create sorted list + grand product - * polynomial. Probably cheaper even if we have to split each 4-bit WNAF slice into 2-bit chunks. - */ - range_constraint_slice_to_2_bits(slices[0], std::get<0>(accumulator)); - range_constraint_slice_to_2_bits(slices[1], std::get<1>(accumulator)); - range_constraint_slice_to_2_bits(slices[2], std::get<2>(accumulator)); - range_constraint_slice_to_2_bits(slices[3], std::get<3>(accumulator)); - range_constraint_slice_to_2_bits(slices[4], std::get<4>(accumulator)); - range_constraint_slice_to_2_bits(slices[5], std::get<5>(accumulator)); - range_constraint_slice_to_2_bits(slices[6], std::get<6>(accumulator)); - range_constraint_slice_to_2_bits(slices[7], std::get<7>(accumulator)); - - /** - * @brief If we are processing a new scalar (q_transition = 1), validate that the first slice is positive. - * This requires us to validate slice1 is in the range [8, ... 15]. - * (when converted into wnaf form this maps to the range [1, 3, ..., 15]). - * We do this to ensure the final scalar sum is positive. - * We already know slice1 is in the range [0, ..., 15] - * To check the range [8, ..., 15] we validate the most significant 2 bits (s1) are >=2 - */ - const auto s1_shift = View(in.precompute_s1hi_shift); - const auto s1_shift_msb_set = (s1_shift - 2) * (s1_shift - 3); - std::get<20>(accumulator) += scaled_transition * precompute_select_shift * s1_shift_msb_set; - - /** - * @brief Convert each pair of 2-bit scalar slices into a 4-bit windowed-non-adjacent-form slice. - * Conversion from binary -> wnaf = 2 * binary - 15. - * Converts a value in [0, ..., 15] into [-15, -13, -11, -9, -7, -5, -3, -1, 1, 3, 5, 7, 9, 11 , 13, 15]. - * We use WNAF representation to avoid case where we are conditionally adding a point in our MSM algo. - */ - const auto w0 = convert_to_wnaf(slices[0], slices[1]); - const auto w1 = convert_to_wnaf(slices[2], slices[3]); - const auto w2 = convert_to_wnaf(slices[4], slices[5]); - const auto w3 = convert_to_wnaf(slices[6], slices[7]); - - /** - * @brief Slice consistency check. - * We require that `scalar_sum` on the next row correctly accumulates the 4 WNAF slices present on the current row - * (i.e. 16 WNAF bits). - * i.e. next_scalar_sum - 2^{16} * current_scalar_sum - 2^12 * w_0 - 2^8 * w_1 - 2^4 * w_2 - w_3 = 0 - * @note We only perform slice_consistency check when next row is processing the same scalar as the current row! - * i.e. when q_transition = 0 - * TODO(@zac-williamson) Optimize WNAF use (#2224) - */ - auto row_slice = w0; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += w1; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += w2; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += w3; - auto sum_delta = scalar_sum * FF(1ULL << 16) + row_slice; - const auto check_sum = scalar_sum_new - sum_delta; - std::get<8>(accumulator) += precompute_select * check_sum * scaled_transition_is_zero; - - /** - * @brief Round transition logic. - * Goal: `round` is an integer in [0, ... 7] that tracks how many slices we have processed for a given scalar. - * i.e. number of 4-bit WNAF slices processed = round * 4. - * We apply the following constraints: - * If q_transition = 0, round increments by 1 between rows. - * If q_transition = 1, round value at current row = 7 - * If q_transition = 1, round value at next row = 0 - * Question: is this sufficient? We don't actually range constrain `round` (expensive if we don't need to!). - * Let us analyze... - * 1. When `q_transition = 1`, we use a set membership check to map the tuple of (pc, scalar_sum) into a set. - * We compare this set with an equivalent set generated from the transcript columns. The sets must match. - * 2. Only case where, at row `i`, a Prover can set `round` to value > 7 is if `q_transition = 0` for all j > i. - * `precompute_pc` decrements by 1 when `q_transition` = 1 - * We can infer from 1, 2, that if `round > 7`, the resulting wnafs will map into a set at a value of `pc` that is - * greater than all valid msm pc values (assuming the set equivalence check on the scalar sums is satisfied). - * The resulting msm output of such a computation cannot be mapped into the set of msm outputs in - * the transcript columns (see relations in ecc_msm_relation.cpp). - * Conclusion: not applying a strict range-check on `round` does not affect soundness (TODO(@zac-williamson) - * validate this! #2225) - */ - // We combine checks 0, 1 into a single relation - // q_transition * (round - 7) + (-q_transition + 1) * (round_shift - round - 1) - // => q_transition * (round - 7 - round_shift + round + 1) + (round_shift - round - 1) - // => q_transition * (2 * round - round_shift - 6) + (round_shift - round - 1) - const auto round_check = round_shift - round - 1; - std::get<9>(accumulator) += precompute_select * scaled_transition * ((round - round_check - 7) + round_check); - std::get<10>(accumulator) += precompute_select * scaled_transition * round_shift; - - /** - * @brief Scalar transition checks. - * 1: if q_transition = 1, scalar_sum_new = 0 - * 2: if q_transition = 0, pc at next row = pc at current row - * 3: if q_transition = 1, pc at next row = pc at current row - 1 (decrements by 1) - * (we combine 2 and 3 into a single relation) - */ - std::get<11>(accumulator) += precompute_select * scalar_sum_new * scaled_transition; - // (2, 3 combined): q_transition * (pc - pc_shift - 1) + (-q_transition + 1) * (pc_shift - pc) - // => q_transition * (-2 * (pc_shift - pc) - 1) + (pc_shift - pc) - const auto pc_delta = pc_shift - pc; - std::get<12>(accumulator) += - precompute_select * (scaled_transition * ((-pc_delta - pc_delta - 1)) + pc_delta * scaling_factor); - - /** - * @brief Validate skew is 0 or 7 - * 7 is the wnaf representation of -1. - * We have one skew variable per scalar multiplier. We can only represent odd integers in WNAF form. - * If input scalar is even, we must subtract 1 from WNAF scalar sum to get actual value (i.e. where skew = 7) - * We use skew in two places. - * 1: when validating sum of wnaf slices matches input scalar (we add skew to scalar_sum in ecc_set_relation) - * 2: in ecc_msm_relation. Final MSM round uses skew to conditionally subtract a point from the accumulator - */ - std::get<13>(accumulator) += precompute_select * (precompute_skew * (precompute_skew - 7)) * scaling_factor; - - const auto precompute_select_zero = (-precompute_select + 1) * scaling_factor; - std::get<14>(accumulator) += precompute_select_zero * (w0 + 15); - std::get<15>(accumulator) += precompute_select_zero * (w1 + 15); - std::get<16>(accumulator) += precompute_select_zero * (w2 + 15); - std::get<17>(accumulator) += precompute_select_zero * (w3 + 15); - - std::get<18>(accumulator) += precompute_select_zero * round; - std::get<19>(accumulator) += precompute_select_zero * pc; - - // TODO(@zac-williamson #2226) - // if precompute_select = 0, validate pc, round, slice values are all zero - // If we do this we can reduce the degree of the set equivalence relations - // (currently when checking pc/round/wnaf tuples from WNAF columns match those from MSM columns, - // we conditionally include tuples depending on if precompute_select = 1 (for WNAF columns) or if q_add1/2/3/4 = 1 - // (for MSM columns). - // If we KNOW that the wnaf tuple values are 0 when precompute_select = 0, we can remove the conditional checks in - // the set equivalence relation -} - template class ECCVMWnafRelationImpl; DEFINE_SUMCHECK_RELATION_CLASS(ECCVMWnafRelationImpl, ECCVMFlavor); diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation_impl.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation_impl.hpp new file mode 100644 index 00000000000..3af5063c1a5 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation_impl.hpp @@ -0,0 +1,216 @@ +#include "ecc_wnaf_relation.hpp" + +namespace bb { + +/** + * @brief ECCVMWnafRelationImpl evaluates relations that convert scalar multipliers into 4-bit WNAF slices + * @details Each WNAF slice is a 4-bit slice representing one of 16 integers { -15, -13, ..., 15 } + * Each WNAF slice is represented via two 2-bit columns (precompute_s1hi, ..., precompute_s4lo) + * One 128-bit scalar multiplier is processed across 8 rows, indexed by a round variable. + * The following table describes the structure for one scalar. + * + * | point_transition | round | slices | skew | scalar_sum | + * | ---------------- | ----- | --------------- | ------ | ------------------------------- | + * | 0 | 0 | s0,s1,s2,s3 | 0 | 0 | + * | 0 | 1 | s4,s5,s6,s7 | 0 | \sum_{i=0}^4 16^i * s_{31 - i} | + * | 0 | 2 | s8,s9,s10,s11 | 0 | \sum_{i=0}^8 16^i * s_{31 - i} | + * | 0 | 3 | s12,s13,s14,s14 | 0 | \sum_{i=0}^12 16^i * s_{31 - i} | + * | 0 | 4 | s16,s17,s18,s19 | 0 | \sum_{i=0}^16 16^i * s_{31 - i} | + * | 0 | 5 | s20,s21,s22,s23 | 0 | \sum_{i=0}^20 16^i * s_{31 - i} | + * | 0 | 6 | s24,s25,s26,s27 | 0 | \sum_{i=0}^24 16^i * s_{31 - i} | + * | 1 | 7 | s28,s29,s30,s31 | s_skew | \sum_{i=0}^28 16^i * s_{31 - i} | + * + * The value of the input scalar is equal to the following: + * + * scalar = 2^16 * scalar_sum + 2^12 * s31 + 2^8 * s30 + 2^4 * s29 + s28 - s_skew + * We use a set equality check in `ecc_set_relation.hpp` to validate the above value maps to the correct input + * scalar for a given value of `pc`. + * + * The column `point_transition` is committed to by the Prover, we must constrain it is correctly computed (see + * `ecc_point_table_relation.cpp` for details) + * + * @tparam FF + * @tparam AccumulatorTypes + */ +template +template +void ECCVMWnafRelationImpl::accumulate(ContainerOverSubrelations& accumulator, + const AllEntities& in, + const Parameters& /*unused*/, + const FF& scaling_factor) +{ + using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; + using View = typename Accumulator::View; + + auto scalar_sum = View(in.precompute_scalar_sum); + auto scalar_sum_new = View(in.precompute_scalar_sum_shift); + auto q_transition = View(in.precompute_point_transition); + auto round = View(in.precompute_round); + auto round_shift = View(in.precompute_round_shift); + auto pc = View(in.precompute_pc); + auto pc_shift = View(in.precompute_pc_shift); + // precompute_select is a boolean column. We only evaluate the ecc_wnaf_relation and the ecc_point_table_relation if + // `precompute_select=1` + auto precompute_select = View(in.precompute_select); + auto precompute_select_shift = View(in.precompute_select_shift); + + const auto& precompute_skew = View(in.precompute_skew); + + const std::array slices{ + View(in.precompute_s1hi), View(in.precompute_s1lo), View(in.precompute_s2hi), View(in.precompute_s2lo), + View(in.precompute_s3hi), View(in.precompute_s3lo), View(in.precompute_s4hi), View(in.precompute_s4lo), + }; + + const auto range_constraint_slice_to_2_bits = [&scaling_factor](const View& s, auto& acc) { + acc += s * (s - 1) * (s - 2) * (s - 3) * scaling_factor; + }; + + const auto convert_to_wnaf = [](const View& s0, const View& s1) { + auto t = s0 + s0; + t += t; + t += s1; + auto naf = t + t - 15; + return naf; + }; + + const auto scaled_transition = q_transition * scaling_factor; + const auto scaled_transition_is_zero = -scaled_transition + scaling_factor; + /** + * @brief Constrain each of our scalar slice chunks (s1, ..., s8) to be 2 bits. + * Doing range checks this way vs permutation-based range check removes need to create sorted list + grand product + * polynomial. Probably cheaper even if we have to split each 4-bit WNAF slice into 2-bit chunks. + */ + range_constraint_slice_to_2_bits(slices[0], std::get<0>(accumulator)); + range_constraint_slice_to_2_bits(slices[1], std::get<1>(accumulator)); + range_constraint_slice_to_2_bits(slices[2], std::get<2>(accumulator)); + range_constraint_slice_to_2_bits(slices[3], std::get<3>(accumulator)); + range_constraint_slice_to_2_bits(slices[4], std::get<4>(accumulator)); + range_constraint_slice_to_2_bits(slices[5], std::get<5>(accumulator)); + range_constraint_slice_to_2_bits(slices[6], std::get<6>(accumulator)); + range_constraint_slice_to_2_bits(slices[7], std::get<7>(accumulator)); + + /** + * @brief If we are processing a new scalar (q_transition = 1), validate that the first slice is positive. + * This requires us to validate slice1 is in the range [8, ... 15]. + * (when converted into wnaf form this maps to the range [1, 3, ..., 15]). + * We do this to ensure the final scalar sum is positive. + * We already know slice1 is in the range [0, ..., 15] + * To check the range [8, ..., 15] we validate the most significant 2 bits (s1) are >=2 + */ + const auto s1_shift = View(in.precompute_s1hi_shift); + const auto s1_shift_msb_set = (s1_shift - 2) * (s1_shift - 3); + std::get<20>(accumulator) += scaled_transition * precompute_select_shift * s1_shift_msb_set; + + /** + * @brief Convert each pair of 2-bit scalar slices into a 4-bit windowed-non-adjacent-form slice. + * Conversion from binary -> wnaf = 2 * binary - 15. + * Converts a value in [0, ..., 15] into [-15, -13, -11, -9, -7, -5, -3, -1, 1, 3, 5, 7, 9, 11 , 13, 15]. + * We use WNAF representation to avoid case where we are conditionally adding a point in our MSM algo. + */ + const auto w0 = convert_to_wnaf(slices[0], slices[1]); + const auto w1 = convert_to_wnaf(slices[2], slices[3]); + const auto w2 = convert_to_wnaf(slices[4], slices[5]); + const auto w3 = convert_to_wnaf(slices[6], slices[7]); + + /** + * @brief Slice consistency check. + * We require that `scalar_sum` on the next row correctly accumulates the 4 WNAF slices present on the current row + * (i.e. 16 WNAF bits). + * i.e. next_scalar_sum - 2^{16} * current_scalar_sum - 2^12 * w_0 - 2^8 * w_1 - 2^4 * w_2 - w_3 = 0 + * @note We only perform slice_consistency check when next row is processing the same scalar as the current row! + * i.e. when q_transition = 0 + * TODO(@zac-williamson) Optimize WNAF use (#2224) + */ + auto row_slice = w0; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += w1; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += w2; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += w3; + auto sum_delta = scalar_sum * FF(1ULL << 16) + row_slice; + const auto check_sum = scalar_sum_new - sum_delta; + std::get<8>(accumulator) += precompute_select * check_sum * scaled_transition_is_zero; + + /** + * @brief Round transition logic. + * Goal: `round` is an integer in [0, ... 7] that tracks how many slices we have processed for a given scalar. + * i.e. number of 4-bit WNAF slices processed = round * 4. + * We apply the following constraints: + * If q_transition = 0, round increments by 1 between rows. + * If q_transition = 1, round value at current row = 7 + * If q_transition = 1, round value at next row = 0 + * Question: is this sufficient? We don't actually range constrain `round` (expensive if we don't need to!). + * Let us analyze... + * 1. When `q_transition = 1`, we use a set membership check to map the tuple of (pc, scalar_sum) into a set. + * We compare this set with an equivalent set generated from the transcript columns. The sets must match. + * 2. Only case where, at row `i`, a Prover can set `round` to value > 7 is if `q_transition = 0` for all j > i. + * `precompute_pc` decrements by 1 when `q_transition` = 1 + * We can infer from 1, 2, that if `round > 7`, the resulting wnafs will map into a set at a value of `pc` that is + * greater than all valid msm pc values (assuming the set equivalence check on the scalar sums is satisfied). + * The resulting msm output of such a computation cannot be mapped into the set of msm outputs in + * the transcript columns (see relations in ecc_msm_relation.cpp). + * Conclusion: not applying a strict range-check on `round` does not affect soundness (TODO(@zac-williamson) + * validate this! #2225) + */ + // We combine checks 0, 1 into a single relation + // q_transition * (round - 7) + (-q_transition + 1) * (round_shift - round - 1) + // => q_transition * (round - 7 - round_shift + round + 1) + (round_shift - round - 1) + // => q_transition * (2 * round - round_shift - 6) + (round_shift - round - 1) + const auto round_check = round_shift - round - 1; + std::get<9>(accumulator) += precompute_select * scaled_transition * ((round - round_check - 7) + round_check); + std::get<10>(accumulator) += precompute_select * scaled_transition * round_shift; + + /** + * @brief Scalar transition checks. + * 1: if q_transition = 1, scalar_sum_new = 0 + * 2: if q_transition = 0, pc at next row = pc at current row + * 3: if q_transition = 1, pc at next row = pc at current row - 1 (decrements by 1) + * (we combine 2 and 3 into a single relation) + */ + std::get<11>(accumulator) += precompute_select * scalar_sum_new * scaled_transition; + // (2, 3 combined): q_transition * (pc - pc_shift - 1) + (-q_transition + 1) * (pc_shift - pc) + // => q_transition * (-2 * (pc_shift - pc) - 1) + (pc_shift - pc) + const auto pc_delta = pc_shift - pc; + std::get<12>(accumulator) += + precompute_select * (scaled_transition * ((-pc_delta - pc_delta - 1)) + pc_delta * scaling_factor); + + /** + * @brief Validate skew is 0 or 7 + * 7 is the wnaf representation of -1. + * We have one skew variable per scalar multiplier. We can only represent odd integers in WNAF form. + * If input scalar is even, we must subtract 1 from WNAF scalar sum to get actual value (i.e. where skew = 7) + * We use skew in two places. + * 1: when validating sum of wnaf slices matches input scalar (we add skew to scalar_sum in ecc_set_relation) + * 2: in ecc_msm_relation. Final MSM round uses skew to conditionally subtract a point from the accumulator + */ + std::get<13>(accumulator) += precompute_select * (precompute_skew * (precompute_skew - 7)) * scaling_factor; + + const auto precompute_select_zero = (-precompute_select + 1) * scaling_factor; + std::get<14>(accumulator) += precompute_select_zero * (w0 + 15); + std::get<15>(accumulator) += precompute_select_zero * (w1 + 15); + std::get<16>(accumulator) += precompute_select_zero * (w2 + 15); + std::get<17>(accumulator) += precompute_select_zero * (w3 + 15); + + std::get<18>(accumulator) += precompute_select_zero * round; + std::get<19>(accumulator) += precompute_select_zero * pc; + + // TODO(@zac-williamson #2226) + // if precompute_select = 0, validate pc, round, slice values are all zero + // If we do this we can reduce the degree of the set equivalence relations + // (currently when checking pc/round/wnaf tuples from WNAF columns match those from MSM columns, + // we conditionally include tuples depending on if precompute_select = 1 (for WNAF columns) or if q_add1/2/3/4 = 1 + // (for MSM columns). + // If we KNOW that the wnaf tuple values are 0 when precompute_select = 0, we can remove the conditional checks in + // the set equivalence relation +} +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_composer.test.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_composer.test.cpp new file mode 100644 index 00000000000..e69de29bb2d From 5b82114684779c1f695bad47d41ae67f3c4c3ba4 Mon Sep 17 00:00:00 2001 From: maramihali Date: Thu, 23 May 2024 18:10:06 +0000 Subject: [PATCH 17/40] yay --- barretenberg/cpp/src/CMakeLists.txt | 1 + .../src/barretenberg/eccvm/eccvm_flavor.hpp | 43 +- .../eccvm_recursion/CMakeLists.txt | 2 +- .../eccvm_recursion/ecc_lookup_relation.cpp | 11 + .../eccvm_recursion/ecc_msm_relation.cpp | 11 + .../ecc_point_table_relation.cpp | 11 + .../ecc_relation_consistency.test.cpp | 55 +++ .../eccvm_recursion/ecc_set_relation.cpp | 14 + .../ecc_transcript_relation.cpp | 11 + .../eccvm_recursion/ecc_wnaf_relation.cpp | 11 + .../eccvm_recursive_flavor.hpp | 75 ++++ .../flavor/relation_definitions.hpp | 4 + .../relations/ecc_vm/ecc_lookup_relation.cpp | 29 +- .../ecc_vm/ecc_lookup_relation_impl.hpp | 32 ++ .../relations/ecc_vm/ecc_msm_relation.cpp | 389 +---------------- .../ecc_vm/ecc_msm_relation_impl.hpp | 391 +++++++++++++++++ .../ecc_vm/ecc_point_table_relation.cpp | 171 +------- .../ecc_vm/ecc_point_table_relation_impl.hpp | 173 ++++++++ .../relations/ecc_vm/ecc_set_relation.cpp | 392 +---------------- .../ecc_vm/ecc_set_relation_impl.hpp | 394 ++++++++++++++++++ .../ecc_vm/ecc_transcript_relation.cpp | 252 +---------- .../ecc_vm/ecc_transcript_relation_impl.hpp | 256 ++++++++++++ .../relations/ecc_vm/ecc_wnaf_relation.cpp | 214 +--------- .../ecc_vm/ecc_wnaf_relation_impl.hpp | 216 ++++++++++ .../stdlib/primitives/bigfield/bigfield.hpp | 15 +- 25 files changed, 1703 insertions(+), 1470 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_lookup_relation.cpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_msm_relation.cpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_point_table_relation.cpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_relation_consistency.test.cpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_set_relation.cpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_transcript_relation.cpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_wnaf_relation.cpp create mode 100644 barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp create mode 100644 barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation_impl.hpp create mode 100644 barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation_impl.hpp create mode 100644 barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation_impl.hpp create mode 100644 barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation_impl.hpp create mode 100644 barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation_impl.hpp create mode 100644 barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation_impl.hpp diff --git a/barretenberg/cpp/src/CMakeLists.txt b/barretenberg/cpp/src/CMakeLists.txt index 62c5eac73a2..dc6c99569f1 100644 --- a/barretenberg/cpp/src/CMakeLists.txt +++ b/barretenberg/cpp/src/CMakeLists.txt @@ -119,6 +119,7 @@ set(BARRETENBERG_TARGET_OBJECTS $ $ $ + $ $ $ $ diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp index 5f53b7e1784..148487809e0 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp @@ -16,7 +16,7 @@ #include "barretenberg/relations/ecc_vm/ecc_wnaf_relation.hpp" #include "barretenberg/relations/relation_parameters.hpp" -// NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members) +// NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members) ? namespace bb { @@ -51,13 +51,14 @@ class ECCVMFlavor { using GrandProductRelations = std::tuple>; // define the tuple of Relations that comprise the Sumcheck relation - using Relations = std::tuple, - ECCVMPointTableRelation, - ECCVMWnafRelation, - ECCVMMSMRelation, - ECCVMSetRelation, - ECCVMLookupRelation>; - + template + using Relations_ = std::tuple, + ECCVMPointTableRelation, + ECCVMWnafRelation, + ECCVMMSMRelation, + ECCVMSetRelation, + ECCVMLookupRelation>; + using Relations = Relations_; using LookupRelation = ECCVMLookupRelation; static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); @@ -73,7 +74,9 @@ class ECCVMFlavor { using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); - private: + // WORKTODO make the specifiers good + + public: /** * @brief A base class labelling precomputed entities and (ordered) subsets of interest. * @details Used to build the proving key and verification key. @@ -299,12 +302,6 @@ class ECCVMFlavor { }; public: - /** - * @brief A container for polynomials produced after the first round of sumcheck. - * @todo TODO(#394) Use polynomial classes for guaranteed memory alignment. - */ - using FoldedPolynomials = AllEntities>; - /** * @brief A field element for each entity of the flavor. These entities represent the prover polynomials * evaluated at one point. @@ -315,12 +312,6 @@ class ECCVMFlavor { using Base::Base; }; - /** - * @brief A container for polynomials produced after the first round of sumcheck. - * @todo TODO(#394) Use polynomial classes for guaranteed memory alignment. - */ - using RowPolynomials = AllEntities; - /** * @brief A container for storing the partially evaluated multivariates produced by sumcheck. */ @@ -630,14 +621,11 @@ class ECCVMFlavor { */ class VerificationKey : public VerificationKey_, VerifierCommitmentKey> { public: - std::vector public_inputs; - VerificationKey(const size_t circuit_size, const size_t num_public_inputs) : VerificationKey_(circuit_size, num_public_inputs) {} VerificationKey(const std::shared_ptr& proving_key) - : public_inputs(proving_key->public_inputs) { this->pcs_verification_key = std::make_shared(proving_key->circuit_size); this->circuit_size = proving_key->circuit_size; @@ -749,9 +737,10 @@ class ECCVMFlavor { }; }; - class VerifierCommitments : public AllEntities { + template + class VerifierCommitments_ : public AllEntities { public: - VerifierCommitments(const std::shared_ptr& verification_key) + VerifierCommitments_(const std::shared_ptr& verification_key) { this->lagrange_first = verification_key->lagrange_first; this->lagrange_second = verification_key->lagrange_second; @@ -759,6 +748,8 @@ class ECCVMFlavor { } }; + using VerifierCommitments = VerifierCommitments_; + /** * @brief Derived class that defines proof structure for ECCVM proofs, as well as supporting functions. * diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/eccvm_recursion/CMakeLists.txt index fdb95c77ac3..5d8da511431 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/CMakeLists.txt @@ -1 +1 @@ -barretenberg_module(eccvm_recursion stdlib_circuit_builders commitment_schemes stdlib_primitives) \ No newline at end of file +barretenberg_module(eccvm_recursion eccvm stdlib_circuit_builders commitment_schemes stdlib_primitives) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_lookup_relation.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_lookup_relation.cpp new file mode 100644 index 00000000000..dc0d5553ed2 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_lookup_relation.cpp @@ -0,0 +1,11 @@ +#include "barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp" +#include "barretenberg/flavor/relation_definitions.hpp" +#include "barretenberg/relations/ecc_vm/ecc_lookup_relation_impl.hpp" +#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp" + +namespace bb { +template class ECCVMLookupRelationImpl>; +template class ECCVMLookupRelationImpl>; +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMLookupRelationImpl, ECCVMRecursiveFlavor_); +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMLookupRelationImpl, ECCVMRecursiveFlavor_); +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_msm_relation.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_msm_relation.cpp new file mode 100644 index 00000000000..aa3eb2c2647 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_msm_relation.cpp @@ -0,0 +1,11 @@ +#include "barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp" +#include "barretenberg/flavor/relation_definitions.hpp" +#include "barretenberg/relations/ecc_vm/ecc_msm_relation_impl.hpp" +#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp" + +namespace bb { +template class ECCVMMSMRelationImpl>; +template class ECCVMMSMRelationImpl>; +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMMSMRelationImpl, ECCVMRecursiveFlavor_); +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMMSMRelationImpl, ECCVMRecursiveFlavor_); +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_point_table_relation.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_point_table_relation.cpp new file mode 100644 index 00000000000..c1e2faeb8cb --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_point_table_relation.cpp @@ -0,0 +1,11 @@ +#include "barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp" +#include "barretenberg/flavor/relation_definitions.hpp" +#include "barretenberg/relations/ecc_vm/ecc_point_table_relation_impl.hpp" +#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp" + +namespace bb { +template class ECCVMPointTableRelationImpl>; +template class ECCVMPointTableRelationImpl>; +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMPointTableRelationImpl, ECCVMRecursiveFlavor_); +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMPointTableRelationImpl, ECCVMRecursiveFlavor_); +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_relation_consistency.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_relation_consistency.test.cpp new file mode 100644 index 00000000000..1da429e99d4 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_relation_consistency.test.cpp @@ -0,0 +1,55 @@ +#include "barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp" +#include "barretenberg/relations/ecc_vm/ecc_lookup_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_msm_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_point_table_relation.hpp" +#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp" +#include +namespace bb { + +class EccRelationsConsistency : public testing::Test { + public: + using Flavor = ECCVMRecursiveFlavor_; + using FF = typename Flavor::FF; + using NativeFF = bb::fq; + using InputElements = typename Flavor::AllValues; + + InputElements get_random_input() + { + InputElements result; + for (FF& element : result.get_all()) { + element = FF(NativeFF::random_element()); + } + return result; + } + template + static void validate_relation_execution(const InputElements& input_elements, const auto& parameters) + { + typename Relation::SumcheckArrayOfValuesOverSubrelations accumulator; + std::fill(accumulator.begin(), accumulator.end(), FF(0)); + Relation::accumulate(accumulator, input_elements, parameters, 1); + }; +}; + +TEST_F(EccRelationsConsistency, ECCVMLookupRelation) +{ + using ECCVMLookupRelation = ECCVMLookupRelation; + const RelationParameters parameters; + const InputElements input_elements = get_random_input(); + validate_relation_execution(input_elements, parameters); + + using ECCVMMSMRelation = ECCVMMSMRelation; + validate_relation_execution(input_elements, parameters); + + using ECCVMPointTableRelation = ECCVMPointTableRelation; + validate_relation_execution(input_elements, parameters); + + using ECCVMSetRelation = ECCVMSetRelation; + validate_relation_execution(input_elements, parameters); + + using ECCVMTranscriptRelation = ECCVMTranscriptRelation; + validate_relation_execution(input_elements, parameters); + + using ECCVMWnafRelation = ECCVMWnafRelation; + validate_relation_execution(input_elements, parameters); +} +} // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_set_relation.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_set_relation.cpp new file mode 100644 index 00000000000..5e735d3557a --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_set_relation.cpp @@ -0,0 +1,14 @@ +#include "barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp" +#include "barretenberg/flavor/relation_definitions.hpp" +#include "barretenberg/relations/ecc_vm/ecc_set_relation_impl.hpp" +#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp" + +namespace bb { +template class ECCVMSetRelationImpl>; +template class ECCVMSetRelationImpl>; +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMSetRelationImpl, ECCVMRecursiveFlavor_); +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMSetRelationImpl, ECCVMRecursiveFlavor_); +DEFINE_SUMCHECK_VERIFIER_PERMUTATION_CLASS(ECCVMSetRelationImpl, ECCVMRecursiveFlavor_); +DEFINE_SUMCHECK_VERIFIER_PERMUTATION_CLASS(ECCVMSetRelationImpl, ECCVMRecursiveFlavor_); + +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_transcript_relation.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_transcript_relation.cpp new file mode 100644 index 00000000000..76ca3006c97 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_transcript_relation.cpp @@ -0,0 +1,11 @@ +#include "barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp" +#include "barretenberg/flavor/relation_definitions.hpp" +#include "barretenberg/relations/ecc_vm/ecc_transcript_relation_impl.hpp" +#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp" + +namespace bb { +template class ECCVMTranscriptRelationImpl>; +template class ECCVMTranscriptRelationImpl>; +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMTranscriptRelationImpl, ECCVMRecursiveFlavor_); +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMTranscriptRelationImpl, ECCVMRecursiveFlavor_); +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_wnaf_relation.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_wnaf_relation.cpp new file mode 100644 index 00000000000..ed75d195714 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_wnaf_relation.cpp @@ -0,0 +1,11 @@ +#include "barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp" +#include "barretenberg/flavor/relation_definitions.hpp" +#include "barretenberg/relations/ecc_vm/ecc_wnaf_relation_impl.hpp" +#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp" + +namespace bb { +template class ECCVMWnafRelationImpl>; +template class ECCVMWnafRelationImpl>; +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMWnafRelationImpl, ECCVMRecursiveFlavor_); +DEFINE_SUMCHECK_VERIFIER_RELATION_CLASS(ECCVMWnafRelationImpl, ECCVMRecursiveFlavor_); +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp new file mode 100644 index 00000000000..a75e8053765 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_flavor.hpp @@ -0,0 +1,75 @@ +#pragma once +#include "barretenberg/common/std_array.hpp" +#include "barretenberg/eccvm/eccvm_flavor.hpp" +#include "barretenberg/eccvm_recursion/verifier_commitment_key.hpp" +#include "barretenberg/flavor/flavor.hpp" +#include "barretenberg/flavor/flavor_macros.hpp" +#include "barretenberg/flavor/relation_definitions.hpp" +#include "barretenberg/polynomials/univariate.hpp" +#include "barretenberg/relations/ecc_vm/ecc_lookup_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_msm_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_point_table_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_set_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_transcript_relation.hpp" +#include "barretenberg/relations/ecc_vm/ecc_wnaf_relation.hpp" +#include "barretenberg/relations/relation_parameters.hpp" +#include "barretenberg/stdlib/honk_recursion/transcript/transcript.hpp" + +// NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members) ? + +namespace bb { + +template class ECCVMRecursiveFlavor_ { + public: + using CircuitBuilder = BuilderType; // determines the arithmetisation of recursive verifier + using FF = stdlib::bigfield; + using BF = stdlib::field_t; + using RelationSeparator = FF; + using NativeFlavor = ECCVMFlavor; + using NativeVerificationKey = NativeFlavor::VerificationKey; + + static constexpr size_t NUM_WIRES = ECCVMFlavor::NUM_WIRES; + // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often + // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. + // Note: this number does not include the individual sorted list polynomials. + static constexpr size_t NUM_ALL_ENTITIES = ECCVMFlavor::NUM_ALL_ENTITIES; + // The number of polynomials precomputed to describe a circuit and to aid a prover in constructing a satisfying + // assignment of witnesses. We again choose a neutral name. + static constexpr size_t NUM_PRECOMPUTED_ENTITIES = ECCVMFlavor::NUM_PRECOMPUTED_ENTITIES; + // The total number of witness entities not including shifts. + static constexpr size_t NUM_WITNESS_ENTITIES = ECCVMFlavor::NUM_WITNESS_ENTITIES; + + // define the tuple of Relations that comprise the Sumcheck relation + // Reuse the Relations from ECCVM + using Relations = ECCVMFlavor::Relations_; + + // think these two are not needed for recursive verifier land + // using GrandProductRelations = std::tuple>; + // using LookupRelation = ECCVMLookupRelation; + static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); + + // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` + // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation + // length = 3 + static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 1; + static constexpr size_t NUM_RELATIONS = std::tuple_size::value; + + // Instantiate the BarycentricData needed to extend each Relation Univariate + + // define the containers for storing the contributions from each relation in Sumcheck + using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); + + public: + /** + * @brief A field element for each entity of the flavor. These entities represent the prover polynomials + * evaluated at one point. + */ + class AllValues : public ECCVMFlavor::AllEntities { + public: + using Base = ECCVMFlavor::AllEntities; + using Base::Base; + }; + +}; // NOLINTEND(cppcoreguidelines-avoid-const-or-ref-data-members) + +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/flavor/relation_definitions.hpp b/barretenberg/cpp/src/barretenberg/flavor/relation_definitions.hpp index dbfd02da9fc..b94abec6013 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/relation_definitions.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/relation_definitions.hpp @@ -38,3 +38,7 @@ PERMUTATION_METHOD(compute_grand_product_denominator, RelationImpl, Flavor, UnivariateAccumulator0, ExtendedEdge) \ PERMUTATION_METHOD(compute_grand_product_denominator, RelationImpl, Flavor, ValueAccumulator0, EvaluationEdge) \ PERMUTATION_METHOD(compute_grand_product_denominator, RelationImpl, Flavor, ValueAccumulator0, EntityEdge) + +#define DEFINE_SUMCHECK_VERIFIER_PERMUTATION_CLASS(RelationImpl, Flavor) \ + PERMUTATION_METHOD(compute_grand_product_numerator, RelationImpl, Flavor, ValueAccumulator0, EvaluationEdge) \ + PERMUTATION_METHOD(compute_grand_product_denominator, RelationImpl, Flavor, ValueAccumulator0, EvaluationEdge) diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation.cpp index e42028a487e..4afdd9c0898 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation.cpp @@ -1,35 +1,8 @@ #include "barretenberg/eccvm/eccvm_flavor.hpp" #include "barretenberg/flavor/relation_definitions.hpp" -#include "barretenberg/honk/proof_system/logderivative_library.hpp" -#include "ecc_msm_relation.hpp" +#include "ecc_lookup_relation_impl.hpp" namespace bb { - -/** - * @brief Expression for ECCVM lookup tables. - * @details We use log-derivative lookup tables for the following case: - * Table writes: ECCVMPointTable columns: we define Straus point table: - * { {0, -15[P]}, {1, -13[P]}, ..., {15, 15[P]} } - * write source: { precompute_round, precompute_tx, precompute_ty } - * Table reads: ECCVMMSM columns. Each row adds up to 4 points into MSM accumulator - * read source: { msm_slice1, msm_x1, msm_y1 }, ..., { msm_slice4, msm_x4, msm_y4 } - * @param evals transformed to `evals + C(in(X)...)*scaling_factor` - * @param in an std::array containing the fully extended Accumulator edges. - * @param parameters contains beta, gamma, and public_input_delta, .... - * @param scaling_factor optional term to scale the evaluation before adding to evals. - */ -template -template -void ECCVMLookupRelationImpl::accumulate(ContainerOverSubrelations& accumulator, - const AllEntities& in, - const Parameters& params, - const FF& scaling_factor) -{ - accumulate_logderivative_lookup_subrelation_contributions>( - accumulator, in, params, scaling_factor); -} - template class ECCVMLookupRelationImpl; DEFINE_SUMCHECK_RELATION_CLASS(ECCVMLookupRelationImpl, ECCVMFlavor); - } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation_impl.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation_impl.hpp new file mode 100644 index 00000000000..9fe77c7bf34 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_lookup_relation_impl.hpp @@ -0,0 +1,32 @@ +#pragma once +#include "barretenberg/common/constexpr_utils.hpp" + +#include "barretenberg/honk/proof_system/logderivative_library.hpp" +#include "ecc_lookup_relation.hpp" + +namespace bb { + +/** + * @brief Expression for ECCVM lookup tables. + * @details We use log-derivative lookup tables for the following case: + * Table writes: ECCVMPointTable columns: we define Straus point table: + * { {0, -15[P]}, {1, -13[P]}, ..., {15, 15[P]} } + * write source: { precompute_round, precompute_tx, precompute_ty } + * Table reads: ECCVMMSM columns. Each row adds up to 4 points into MSM accumulator + * read source: { msm_slice1, msm_x1, msm_y1 }, ..., { msm_slice4, msm_x4, msm_y4 } + * @param evals transformed to `evals + C(in(X)...)*scaling_factor` + * @param in an std::array containing the fully extended Accumulator edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ +template +template +void ECCVMLookupRelationImpl::accumulate(ContainerOverSubrelations& accumulator, + const AllEntities& in, + const Parameters& params, + const FF& scaling_factor) +{ + accumulate_logderivative_lookup_subrelation_contributions>( + accumulator, in, params, scaling_factor); +} +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation.cpp index b71b5a6e4a0..bb7c3738b6f 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation.cpp @@ -1,396 +1,9 @@ -#include "ecc_msm_relation.hpp" #include "barretenberg/eccvm/eccvm_flavor.hpp" #include "barretenberg/flavor/relation_definitions.hpp" +#include "ecc_msm_relation_impl.hpp" namespace bb { -/** - * @brief MSM relations that evaluate the Strauss multiscalar multiplication algorithm. - * - * @details - * The Strauss algorithm for a size-k MSM takes scalars/points (a_i, [P_i]) for i = 0 to k-1. - * The specific algoritm we use is the following: - * - * PHASE 1: Precomputation (performed in ecc_wnaf_relation.hpp, ecc_point_table_relation.hpp) - * Each scalar a_i is split into 4-bit WNAF slices s_{j, i} for j = 0 to 31, and a skew bool skew_i - * For each point [P_i] a size-16 lookup table of points, T_i, is computed { [-15 P_i], [-13 P_i], ..., [15 P_i] } - * - * PHASE 2: MSM evaluation - * MSM evaluation is split into 32 rounds that operate on an accumulator point [Acc] - * The first 31 rounds are composed of an ADDITION round and a DOUBLE round. - * The final 32nd round is composed of an ADDITION round and a SKEW round. - * - * ADDITION round (round = j): - * [Acc] = [Acc] + T_i[a_{i, j}] for all i in [0, ... k-1] - * - * DOUBLE round: - * [Acc] = 16 * [Acc] (four point doublings) - * - * SKEW round: - * If skew_i == 1, [Acc] = [Acc] - [P_i] for all i in [0, ..., k - 1] - * - * The relations in ECCVMMSMRelationImpl constrain the ADDITION, DOUBLE and SKEW rounds - * @param evals transformed to `evals + C(in(X)...)*scaling_factor` - * @param in an std::array containing the fully extended Accumulator edges. - * @param parameters contains beta, gamma, and public_input_delta, .... - * @param scaling_factor optional term to scale the evaluation before adding to evals. - */ -template -template -void ECCVMMSMRelationImpl::accumulate(ContainerOverSubrelations& accumulator, - const AllEntities& in, - const Parameters& /*unused*/, - const FF& scaling_factor) -{ - using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - - const auto& x1 = View(in.msm_x1); - const auto& y1 = View(in.msm_y1); - const auto& x2 = View(in.msm_x2); - const auto& y2 = View(in.msm_y2); - const auto& x3 = View(in.msm_x3); - const auto& y3 = View(in.msm_y3); - const auto& x4 = View(in.msm_x4); - const auto& y4 = View(in.msm_y4); - const auto& collision_inverse1 = View(in.msm_collision_x1); - const auto& collision_inverse2 = View(in.msm_collision_x2); - const auto& collision_inverse3 = View(in.msm_collision_x3); - const auto& collision_inverse4 = View(in.msm_collision_x4); - const auto& lambda1 = View(in.msm_lambda1); - const auto& lambda2 = View(in.msm_lambda2); - const auto& lambda3 = View(in.msm_lambda3); - const auto& lambda4 = View(in.msm_lambda4); - const auto& lagrange_first = View(in.lagrange_first); - const auto& add1 = View(in.msm_add1); - const auto& add1_shift = View(in.msm_add1_shift); - const auto& add2 = View(in.msm_add2); - const auto& add3 = View(in.msm_add3); - const auto& add4 = View(in.msm_add4); - const auto& acc_x = View(in.msm_accumulator_x); - const auto& acc_y = View(in.msm_accumulator_y); - const auto& acc_x_shift = View(in.msm_accumulator_x_shift); - const auto& acc_y_shift = View(in.msm_accumulator_y_shift); - const auto& slice1 = View(in.msm_slice1); - const auto& slice2 = View(in.msm_slice2); - const auto& slice3 = View(in.msm_slice3); - const auto& slice4 = View(in.msm_slice4); - const auto& msm_transition = View(in.msm_transition); - const auto& msm_transition_shift = View(in.msm_transition_shift); - const auto& round = View(in.msm_round); - const auto& round_shift = View(in.msm_round_shift); - const auto& q_add = View(in.msm_add); - const auto& q_add_shift = View(in.msm_add_shift); - const auto& q_skew = View(in.msm_skew); - const auto& q_skew_shift = View(in.msm_skew_shift); - const auto& q_double = View(in.msm_double); - const auto& q_double_shift = View(in.msm_double_shift); - const auto& msm_size = View(in.msm_size_of_msm); - // const auto& msm_size_shift = View(in.msm_size_of_msm_shift); - const auto& pc = View(in.msm_pc); - const auto& pc_shift = View(in.msm_pc_shift); - const auto& count = View(in.msm_count); - const auto& count_shift = View(in.msm_count_shift); - auto is_not_first_row = (-lagrange_first + 1); - - /** - * @brief Evaluating ADDITION rounds - * - * This comment describes the algorithm we want the Prover to perform. - * The relations we constrain are supposed to make an honest Prover compute witnesses consistent with the following: - * - * For an MSM of size-k... - * - * Algorithm to determine if round at shifted row is an ADDITION round: - * 1. count_shift < msm_size - * 2. round != 32 - * - * Algorithm to process MSM ADDITION round: - * 1. If `round == 0` set `count = 0` - * 2. For j = pc + count, perform the following: - * 2a. If j + 3 < k: [P_{j + 3}] = T_{j+ 3}[slice_{j + 3}] - * 2b. If j + 2 < k: [P_{j + 2}] = T_{j+ 2}[slice_{j + 2}] - * 2c. If j + 1 < k: [P_{j + 1}] = T_{j+ 1}[slice_{j + 1}] - * 2d. [P_{j}] = T_{j}[slice_{j}] - * 2e. If j + 3 < k: [Acc_shift] = [Acc] + [P_j] + [P_{j+1}] + [P_{j+2}] + [P_{j+3}] - * 2f. Else If j + 2 < k: [Acc_shift] = [Acc] + [P_j] + [P_{j+1}] + [P_{j+2}] - * 2g. Else IF j + 1 < k: [Acc_shift] = [Acc] + [P_j] + [P_{j+1}] - * 2h. Else : [Acc_shift] = [Acc] + [P_j] - * 3. `count_shift = count + 1 + (j + 1 < k) + (j + 2 < k) + (j + 3 < k)` - */ - - /** - * @brief Constraining addition rounds - * - * The boolean column q_add describes whether a round is an ADDITION round. - * The values of q_add are Prover-defined. We need to ensure they set q_add correctly. - * We rely on the following statements that we assume are constrained to be true (from other relations): - * 1. The set of reads into (pc, round, wnaf_slice) is constructed when q_add = 1 - * 2. The set of reads into (pc, round, wnaf_slice) must match the set of writes from the point_table columns - * 3. The set of writes into (pc, round, wnaf_slice) from the point table columns is correct - * 4. `round` only updates when `q_add = 1` at current row and `q_add = 0` at next row - * If a Prover sets `q_add = 0` when an honest Prover would set `q_add = 1`, - * this will produce an inequality in the set of reads / writes into the (pc, round, wnaf_slice) table. - * - * The addition algorithm has several IF/ELSE statements based on comparing `count` with `msm_size`. - * Instead of directly constraining these, we define 4 boolean columns `q_add1, q_add2, q_add3, q_add4`. - * Like `q_add`, their values are Prover-defined. We need to ensure they are set correctly. - * We update the above conditions on reads into (pc, round, wnaf_slice) to the following: - * 1. The set of reads into (pc_{count}, round, wnaf_slice_{count}) is constructed when q_add = 1 AND q_add1 = - * 1 - * 2. The set of reads into (pc_{count + 1}, round, wnaf_slice_{count + 1}) is constructed when q_add = 1 AND - * q_add2 = 1 - * 3. The set of reads into (pc_{count + 2}, round, wnaf_slice_{count + 2}) is constructed when q_add = 1 AND - * q_add3 = 1 - * 4. The set of reads into (pc_{count + 3}, round, wnaf_slice_{count + 3}) is constructed when q_add = 1 AND - * q_add4 = 1 - * - * To ensure that all q_addi values are correctly set we apply consistency checks to q_add1/q_add2/q_add3/q_add4: - * 1. If q_add2 = 1, require q_add1 = 1 - * 2. If q_add3 = 1, require q_add2 = 1 - * 3. If q_add4 = 1, require q_add3 = 1 - * 4. If q_add1_shift = 1 AND round does not update between rows, require q_add4 = 1 - * - * We want to use all of the above to reason about the set of reads into (pc, round, wnaf_slice). - * The goal is to conclude that any case where the Prover incorrectly sets q_add/q_add1/q_add2/q_add3/q_add4 will - * produce a set inequality between the reads/writes into (pc, round, wnaf_slice) - */ - - /** - * @brief Addition relation - * - * All addition operations in ECCVMMSMRelationImpl are conditional additions! - * This method returns two Accumulators that represent x/y coord of output. - * Output is either an addition of inputs, or xa/ya dpeending on value of `selector`. - * Additionally, we require `lambda = 0` if `selector = 0`. - * The `collision_relation` accumulator tracks a subrelation that validates xb != xa. - * Repeated calls to this method will increase the max degree of the Accumulator output - * Degree of x_out, y_out = max degree of x_a/x_b + 1 - * 4 Iterations will produce an output degree of 6 - */ - auto add = [&](auto& xb, - auto& yb, - auto& xa, - auto& ya, - auto& lambda, - auto& selector, - auto& relation, - auto& collision_relation) { - // (L * (xb - xa) - yb - ya) * s = 0 - // L * (1 - s) = 0 - // (combine) (L * (xb - xa - 1) - yb - ya) * s + L = 0 - relation += selector * (lambda * (xb - xa - 1) - (yb - ya)) + lambda; - collision_relation += selector * (xb - xa); - // x3 = L.L + (-xb - xa) * q + (1 - q) xa - auto x_out = lambda * lambda + (-xb - xa - xa) * selector + xa; - - // y3 = L . (xa - x3) - ya * q + (1 - q) ya - auto y_out = lambda * (xa - x_out) + (-ya - ya) * selector + ya; - return std::array{ x_out, y_out }; - }; - - // ADD operations (if row represents ADD round, not SKEW or DOUBLE) - Accumulator add_relation(0); - Accumulator x1_collision_relation(0); - Accumulator x2_collision_relation(0); - Accumulator x3_collision_relation(0); - Accumulator x4_collision_relation(0); - // If msm_transition = 1, we have started a new MSM. We need to treat the current value of [Acc] as the point at - // infinity! - auto add_into_accumulator = -msm_transition + 1; - auto [x_t1, y_t1] = add(acc_x, acc_y, x1, y1, lambda1, add_into_accumulator, add_relation, x1_collision_relation); - auto [x_t2, y_t2] = add(x2, y2, x_t1, y_t1, lambda2, add2, add_relation, x2_collision_relation); - auto [x_t3, y_t3] = add(x3, y3, x_t2, y_t2, lambda3, add3, add_relation, x3_collision_relation); - auto [x_t4, y_t4] = add(x4, y4, x_t3, y_t3, lambda4, add4, add_relation, x4_collision_relation); - - // Validate accumulator output matches ADD output if q_add = 1 - // (this is a degree-6 relation) - std::get<0>(accumulator) += q_add * (acc_x_shift - x_t4) * scaling_factor; - std::get<1>(accumulator) += q_add * (acc_y_shift - y_t4) * scaling_factor; - std::get<2>(accumulator) += q_add * add_relation * scaling_factor; - - /** - * @brief doubles a point. - * - * Degree of x_out = 2 - * Degree of y_out = 3 - * Degree of relation = 4 - */ - auto dbl = [&](auto& x, auto& y, auto& lambda, auto& relation) { - auto two_x = x + x; - relation += lambda * (y + y) - (two_x + x) * x; - auto x_out = lambda * lambda - two_x; - auto y_out = lambda * (x - x_out) - y; - return std::array{ x_out, y_out }; - }; - - /** - * @brief - * - * Algorithm to determine if round is a DOUBLE round: - * 1. count_shift >= msm_size - * 2. round != 32 - * - * Algorithm to process MSM DOUBLE round: - * [Acc_shift] = (([Acc].double()).double()).double() - * - * As with additions, the column q_double describes whether row is a double round. It is Prover-defined. - * The value of `msm_round` can only update when `q_double = 1` and we use this to ensure Prover correctly sets - * `q_double`. (see round transition relations further down) - */ - Accumulator double_relation(0); - auto [x_d1, y_d1] = dbl(acc_x, acc_y, lambda1, double_relation); - auto [x_d2, y_d2] = dbl(x_d1, y_d1, lambda2, double_relation); - auto [x_d3, y_d3] = dbl(x_d2, y_d2, lambda3, double_relation); - auto [x_d4, y_d4] = dbl(x_d3, y_d3, lambda4, double_relation); - std::get<10>(accumulator) += q_double * (acc_x_shift - x_d4) * scaling_factor; - std::get<11>(accumulator) += q_double * (acc_y_shift - y_d4) * scaling_factor; - std::get<12>(accumulator) += q_double * double_relation * scaling_factor; - - /** - * @brief SKEW operations - * When computing x * [P], if x is even we must subtract [P] from accumulator - * (this is because our windowed non-adjacent-form can only represent odd numbers) - * Round 32 represents "skew" round. - * If scalar slice == 7, we add into accumulator (point_table[7] maps to -[P]) - * If scalar slice == 0, we do not add into accumulator - * i.e. for the skew round we can use the slice values as our "selector" when doing conditional point adds - */ - Accumulator skew_relation(0); - static constexpr FF inverse_seven = FF(7).invert(); - auto skew1_select = slice1 * inverse_seven; - auto skew2_select = slice2 * inverse_seven; - auto skew3_select = slice3 * inverse_seven; - auto skew4_select = slice4 * inverse_seven; - Accumulator x1_skew_collision_relation(0); - Accumulator x2_skew_collision_relation(0); - Accumulator x3_skew_collision_relation(0); - Accumulator x4_skew_collision_relation(0); - // add skew points iff row is a SKEW row AND slice = 7 (point_table[7] maps to -[P]) - // N.B. while it would be nice to have one `add` relation for both ADD and SKEW rounds, - // this would increase degree of sumcheck identity vs evaluating them separately. - // This is because, for add rounds, the result of adding [P1], [Acc] is [P1 + Acc] or [P1] - // but for skew rounds, the result of adding [P1], [Acc] is [P1 + Acc] or [Acc] - auto [x_s1, y_s1] = add(x1, y1, acc_x, acc_y, lambda1, skew1_select, skew_relation, x1_skew_collision_relation); - auto [x_s2, y_s2] = add(x2, y2, x_s1, y_s1, lambda2, skew2_select, skew_relation, x2_skew_collision_relation); - auto [x_s3, y_s3] = add(x3, y3, x_s2, y_s2, lambda3, skew3_select, skew_relation, x3_skew_collision_relation); - auto [x_s4, y_s4] = add(x4, y4, x_s3, y_s3, lambda4, skew4_select, skew_relation, x4_skew_collision_relation); - - // Validate accumulator output matches SKEW output if q_skew = 1 - // (this is a degree-6 relation) - std::get<3>(accumulator) += q_skew * (acc_x_shift - x_s4) * scaling_factor; - std::get<4>(accumulator) += q_skew * (acc_y_shift - y_s4) * scaling_factor; - std::get<5>(accumulator) += q_skew * skew_relation * scaling_factor; - - // Check x-coordinates do not collide if row is an ADD row or a SKEW row - // if either q_add or q_skew = 1, an inverse should exist for each computed relation - // Step 1: construct boolean selectors that describe whether we added a point at the current row - const auto add_first_point = add_into_accumulator * q_add + q_skew * skew1_select; - const auto add_second_point = add2 * q_add + q_skew * skew2_select; - const auto add_third_point = add3 * q_add + q_skew * skew3_select; - const auto add_fourth_point = add4 * q_add + q_skew * skew4_select; - // Step 2: construct the delta between x-coordinates for each point add (depending on if row is ADD or SKEW) - const auto x1_delta = x1_skew_collision_relation * q_skew + x1_collision_relation * q_add; - const auto x2_delta = x2_skew_collision_relation * q_skew + x2_collision_relation * q_add; - const auto x3_delta = x3_skew_collision_relation * q_skew + x3_collision_relation * q_add; - const auto x4_delta = x4_skew_collision_relation * q_skew + x4_collision_relation * q_add; - // Step 3: x_delta * inverse - 1 = 0 if we performed a point addition (else x_delta * inverse = 0) - std::get<6>(accumulator) += (x1_delta * collision_inverse1 - add_first_point) * scaling_factor; - std::get<7>(accumulator) += (x2_delta * collision_inverse2 - add_second_point) * scaling_factor; - std::get<8>(accumulator) += (x3_delta * collision_inverse3 - add_third_point) * scaling_factor; - std::get<9>(accumulator) += (x4_delta * collision_inverse4 - add_fourth_point) * scaling_factor; - - // Validate that if q_add = 1 or q_skew = 1, add1 also is 1 - // TODO(@zac-williamson) Once we have a stable base to work off of, remove q_add1 and replace with q_msm_add + - // q_msm_skew (issue #2222) - std::get<32>(accumulator) += (add1 - q_add - q_skew) * scaling_factor; - - // If add_i = 0, slice_i = 0 - // When add_i = 0, force slice_i to ALSO be 0 - std::get<13>(accumulator) += (-add1 + 1) * slice1 * scaling_factor; - std::get<14>(accumulator) += (-add2 + 1) * slice2 * scaling_factor; - std::get<15>(accumulator) += (-add3 + 1) * slice3 * scaling_factor; - std::get<16>(accumulator) += (-add4 + 1) * slice4 * scaling_factor; - - // only one of q_skew, q_double, q_add can be nonzero - std::get<17>(accumulator) += (q_add * q_double + q_add * q_skew + q_double * q_skew) * scaling_factor; - - // We look up wnaf slices by mapping round + pc -> slice - // We use an exact set membership check to validate that - // wnafs written in wnaf_relation == wnafs read in msm relation - // We use `add1/add2/add3/add4` to flag whether we are performing a wnaf read op - // We can set these to be Prover-defined as the set membership check implicitly ensures that the correct reads - // have occurred. - // if msm_transition = 0, round_shift - round = 0 or 1 - const auto round_delta = round_shift - round; - - // ROUND TRANSITION LOGIC (when round does not change) - // If msm_transition = 0 (next row) then round_delta = 0 or 1 - const auto round_transition = round_delta * (-msm_transition_shift + 1); - std::get<18>(accumulator) += round_transition * (round_delta - 1) * scaling_factor; - - // ROUND TRANSITION LOGIC (when round DOES change) - // round_transition describes whether we are transitioning between rounds of an MSM - // If round_transition = 1, the next row is either a double (if round != 31) or we are adding skew (if round == - // 31) round_transition * skew * (round - 31) = 0 (if round tx and skew, round == 31) round_transition * (skew + - // double - 1) = 0 (if round tx, skew XOR double = 1) i.e. if round tx and round != 31, double = 1 - std::get<19>(accumulator) += round_transition * q_skew_shift * (round - 31) * scaling_factor; - std::get<20>(accumulator) += round_transition * (q_skew_shift + q_double_shift - 1) * scaling_factor; - - // if no double or no skew, round_delta = 0 - std::get<21>(accumulator) += round_transition * (-q_double_shift + 1) * (-q_skew_shift + 1) * scaling_factor; - - // if double, next double != 1 - std::get<22>(accumulator) += q_double * q_double_shift * scaling_factor; - - // if double, next add = 1 - std::get<23>(accumulator) += q_double * (-q_add_shift + 1) * scaling_factor; - - // updating count - // if msm_transition = 0 and round_transition = 0, count_shift = count + add1 + add2 + add3 + add4 - // todo: we need this? - std::get<24>(accumulator) += (-msm_transition_shift + 1) * (-round_delta + 1) * - (count_shift - count - add1 - add2 - add3 - add4) * scaling_factor; - - std::get<25>(accumulator) += - is_not_first_row * (-msm_transition_shift + 1) * round_delta * count_shift * scaling_factor; - - // if msm_transition = 1, count_shift = 0 - std::get<26>(accumulator) += is_not_first_row * msm_transition_shift * count_shift * scaling_factor; - - // if msm_transition = 1, pc = pc_shift + msm_size - // `ecc_set_relation` ensures `msm_size` maps to `transcript.msm_count` for the current value of `pc` - std::get<27>(accumulator) += is_not_first_row * msm_transition_shift * (msm_size + pc_shift - pc) * scaling_factor; - - // Addition continuity checks - // We want to RULE OUT the following scenarios: - // Case 1: add2 = 1, add1 = 0 - // Case 2: add3 = 1, add2 = 0 - // Case 3: add4 = 1, add3 = 0 - // These checks ensure that the current row does not skip points (for both ADD and SKEW ops) - // This is part of a wider set of checks we use to ensure that all point data is used in the assigned - // multiscalar multiplication operation. - // (and not in a different MSM operation) - std::get<28>(accumulator) += add2 * (-add1 + 1) * scaling_factor; - std::get<29>(accumulator) += add3 * (-add2 + 1) * scaling_factor; - std::get<30>(accumulator) += add4 * (-add3 + 1) * scaling_factor; - - // Final continuity check. - // If an addition spans two rows, we need to make sure that the following scenario is RULED OUT: - // add4 = 0 on the CURRENT row, add1 = 1 on the NEXT row - // We must apply the above for the two cases: - // Case 1: q_add = 1 on the CURRENT row, q_add = 1 on the NEXT row - // Case 2: q_skew = 1 on the CURRENT row, q_skew = 1 on the NEXT row - // (i.e. if q_skew = 1, q_add_shift = 1 this implies an MSM transition so we skip this continuity check) - std::get<31>(accumulator) += - (q_add * q_add_shift + q_skew * q_skew_shift) * (-add4 + 1) * add1_shift * scaling_factor; - - // remaining checks (done in ecc_set_relation.hpp, ecc_lookup_relation.hpp) - // when transition occurs, perform set membership lookup on (accumulator / pc / msm_size) - // perform set membership lookups on add_i * (pc / round / slice_i) - // perform lookups on (pc / slice_i / x / y) -} - template class ECCVMMSMRelationImpl; DEFINE_SUMCHECK_RELATION_CLASS(ECCVMMSMRelationImpl, ECCVMFlavor); diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation_impl.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation_impl.hpp new file mode 100644 index 00000000000..c9e870accc8 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_msm_relation_impl.hpp @@ -0,0 +1,391 @@ +#pragma once +#include "ecc_msm_relation.hpp" +namespace bb { + +/** + * @brief MSM relations that evaluate the Strauss multiscalar multiplication algorithm. + * + * @details + * The Strauss algorithm for a size-k MSM takes scalars/points (a_i, [P_i]) for i = 0 to k-1. + * The specific algoritm we use is the following: + * + * PHASE 1: Precomputation (performed in ecc_wnaf_relation.hpp, ecc_point_table_relation.hpp) + * Each scalar a_i is split into 4-bit WNAF slices s_{j, i} for j = 0 to 31, and a skew bool skew_i + * For each point [P_i] a size-16 lookup table of points, T_i, is computed { [-15 P_i], [-13 P_i], ..., [15 P_i] } + * + * PHASE 2: MSM evaluation + * MSM evaluation is split into 32 rounds that operate on an accumulator point [Acc] + * The first 31 rounds are composed of an ADDITION round and a DOUBLE round. + * The final 32nd round is composed of an ADDITION round and a SKEW round. + * + * ADDITION round (round = j): + * [Acc] = [Acc] + T_i[a_{i, j}] for all i in [0, ... k-1] + * + * DOUBLE round: + * [Acc] = 16 * [Acc] (four point doublings) + * + * SKEW round: + * If skew_i == 1, [Acc] = [Acc] - [P_i] for all i in [0, ..., k - 1] + * + * The relations in ECCVMMSMRelationImpl constrain the ADDITION, DOUBLE and SKEW rounds + * @param evals transformed to `evals + C(in(X)...)*scaling_factor` + * @param in an std::array containing the fully extended Accumulator edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ +template +template +void ECCVMMSMRelationImpl::accumulate(ContainerOverSubrelations& accumulator, + const AllEntities& in, + const Parameters& /*unused*/, + const FF& scaling_factor) +{ + using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; + using View = typename Accumulator::View; + + const auto& x1 = View(in.msm_x1); + const auto& y1 = View(in.msm_y1); + const auto& x2 = View(in.msm_x2); + const auto& y2 = View(in.msm_y2); + const auto& x3 = View(in.msm_x3); + const auto& y3 = View(in.msm_y3); + const auto& x4 = View(in.msm_x4); + const auto& y4 = View(in.msm_y4); + const auto& collision_inverse1 = View(in.msm_collision_x1); + const auto& collision_inverse2 = View(in.msm_collision_x2); + const auto& collision_inverse3 = View(in.msm_collision_x3); + const auto& collision_inverse4 = View(in.msm_collision_x4); + const auto& lambda1 = View(in.msm_lambda1); + const auto& lambda2 = View(in.msm_lambda2); + const auto& lambda3 = View(in.msm_lambda3); + const auto& lambda4 = View(in.msm_lambda4); + const auto& lagrange_first = View(in.lagrange_first); + const auto& add1 = View(in.msm_add1); + const auto& add1_shift = View(in.msm_add1_shift); + const auto& add2 = View(in.msm_add2); + const auto& add3 = View(in.msm_add3); + const auto& add4 = View(in.msm_add4); + const auto& acc_x = View(in.msm_accumulator_x); + const auto& acc_y = View(in.msm_accumulator_y); + const auto& acc_x_shift = View(in.msm_accumulator_x_shift); + const auto& acc_y_shift = View(in.msm_accumulator_y_shift); + const auto& slice1 = View(in.msm_slice1); + const auto& slice2 = View(in.msm_slice2); + const auto& slice3 = View(in.msm_slice3); + const auto& slice4 = View(in.msm_slice4); + const auto& msm_transition = View(in.msm_transition); + const auto& msm_transition_shift = View(in.msm_transition_shift); + const auto& round = View(in.msm_round); + const auto& round_shift = View(in.msm_round_shift); + const auto& q_add = View(in.msm_add); + const auto& q_add_shift = View(in.msm_add_shift); + const auto& q_skew = View(in.msm_skew); + const auto& q_skew_shift = View(in.msm_skew_shift); + const auto& q_double = View(in.msm_double); + const auto& q_double_shift = View(in.msm_double_shift); + const auto& msm_size = View(in.msm_size_of_msm); + // const auto& msm_size_shift = View(in.msm_size_of_msm_shift); + const auto& pc = View(in.msm_pc); + const auto& pc_shift = View(in.msm_pc_shift); + const auto& count = View(in.msm_count); + const auto& count_shift = View(in.msm_count_shift); + auto is_not_first_row = (-lagrange_first + 1); + + /** + * @brief Evaluating ADDITION rounds + * + * This comment describes the algorithm we want the Prover to perform. + * The relations we constrain are supposed to make an honest Prover compute witnesses consistent with the following: + * + * For an MSM of size-k... + * + * Algorithm to determine if round at shifted row is an ADDITION round: + * 1. count_shift < msm_size + * 2. round != 32 + * + * Algorithm to process MSM ADDITION round: + * 1. If `round == 0` set `count = 0` + * 2. For j = pc + count, perform the following: + * 2a. If j + 3 < k: [P_{j + 3}] = T_{j+ 3}[slice_{j + 3}] + * 2b. If j + 2 < k: [P_{j + 2}] = T_{j+ 2}[slice_{j + 2}] + * 2c. If j + 1 < k: [P_{j + 1}] = T_{j+ 1}[slice_{j + 1}] + * 2d. [P_{j}] = T_{j}[slice_{j}] + * 2e. If j + 3 < k: [Acc_shift] = [Acc] + [P_j] + [P_{j+1}] + [P_{j+2}] + [P_{j+3}] + * 2f. Else If j + 2 < k: [Acc_shift] = [Acc] + [P_j] + [P_{j+1}] + [P_{j+2}] + * 2g. Else IF j + 1 < k: [Acc_shift] = [Acc] + [P_j] + [P_{j+1}] + * 2h. Else : [Acc_shift] = [Acc] + [P_j] + * 3. `count_shift = count + 1 + (j + 1 < k) + (j + 2 < k) + (j + 3 < k)` + */ + + /** + * @brief Constraining addition rounds + * + * The boolean column q_add describes whether a round is an ADDITION round. + * The values of q_add are Prover-defined. We need to ensure they set q_add correctly. + * We rely on the following statements that we assume are constrained to be true (from other relations): + * 1. The set of reads into (pc, round, wnaf_slice) is constructed when q_add = 1 + * 2. The set of reads into (pc, round, wnaf_slice) must match the set of writes from the point_table columns + * 3. The set of writes into (pc, round, wnaf_slice) from the point table columns is correct + * 4. `round` only updates when `q_add = 1` at current row and `q_add = 0` at next row + * If a Prover sets `q_add = 0` when an honest Prover would set `q_add = 1`, + * this will produce an inequality in the set of reads / writes into the (pc, round, wnaf_slice) table. + * + * The addition algorithm has several IF/ELSE statements based on comparing `count` with `msm_size`. + * Instead of directly constraining these, we define 4 boolean columns `q_add1, q_add2, q_add3, q_add4`. + * Like `q_add`, their values are Prover-defined. We need to ensure they are set correctly. + * We update the above conditions on reads into (pc, round, wnaf_slice) to the following: + * 1. The set of reads into (pc_{count}, round, wnaf_slice_{count}) is constructed when q_add = 1 AND q_add1 = + * 1 + * 2. The set of reads into (pc_{count + 1}, round, wnaf_slice_{count + 1}) is constructed when q_add = 1 AND + * q_add2 = 1 + * 3. The set of reads into (pc_{count + 2}, round, wnaf_slice_{count + 2}) is constructed when q_add = 1 AND + * q_add3 = 1 + * 4. The set of reads into (pc_{count + 3}, round, wnaf_slice_{count + 3}) is constructed when q_add = 1 AND + * q_add4 = 1 + * + * To ensure that all q_addi values are correctly set we apply consistency checks to q_add1/q_add2/q_add3/q_add4: + * 1. If q_add2 = 1, require q_add1 = 1 + * 2. If q_add3 = 1, require q_add2 = 1 + * 3. If q_add4 = 1, require q_add3 = 1 + * 4. If q_add1_shift = 1 AND round does not update between rows, require q_add4 = 1 + * + * We want to use all of the above to reason about the set of reads into (pc, round, wnaf_slice). + * The goal is to conclude that any case where the Prover incorrectly sets q_add/q_add1/q_add2/q_add3/q_add4 will + * produce a set inequality between the reads/writes into (pc, round, wnaf_slice) + */ + + /** + * @brief Addition relation + * + * All addition operations in ECCVMMSMRelationImpl are conditional additions! + * This method returns two Accumulators that represent x/y coord of output. + * Output is either an addition of inputs, or xa/ya dpeending on value of `selector`. + * Additionally, we require `lambda = 0` if `selector = 0`. + * The `collision_relation` accumulator tracks a subrelation that validates xb != xa. + * Repeated calls to this method will increase the max degree of the Accumulator output + * Degree of x_out, y_out = max degree of x_a/x_b + 1 + * 4 Iterations will produce an output degree of 6 + */ + auto add = [&](auto& xb, + auto& yb, + auto& xa, + auto& ya, + auto& lambda, + auto& selector, + auto& relation, + auto& collision_relation) { + // (L * (xb - xa) - yb - ya) * s = 0 + // L * (1 - s) = 0 + // (combine) (L * (xb - xa - 1) - yb - ya) * s + L = 0 + relation += selector * (lambda * (xb - xa - 1) - (yb - ya)) + lambda; + collision_relation += selector * (xb - xa); + // x3 = L.L + (-xb - xa) * q + (1 - q) xa + auto x_out = lambda * lambda + (-xb - xa - xa) * selector + xa; + + // y3 = L . (xa - x3) - ya * q + (1 - q) ya + auto y_out = lambda * (xa - x_out) + (-ya - ya) * selector + ya; + return std::array{ x_out, y_out }; + }; + + // ADD operations (if row represents ADD round, not SKEW or DOUBLE) + Accumulator add_relation(0); + Accumulator x1_collision_relation(0); + Accumulator x2_collision_relation(0); + Accumulator x3_collision_relation(0); + Accumulator x4_collision_relation(0); + // If msm_transition = 1, we have started a new MSM. We need to treat the current value of [Acc] as the point at + // infinity! + auto add_into_accumulator = -msm_transition + 1; + auto [x_t1, y_t1] = add(acc_x, acc_y, x1, y1, lambda1, add_into_accumulator, add_relation, x1_collision_relation); + auto [x_t2, y_t2] = add(x2, y2, x_t1, y_t1, lambda2, add2, add_relation, x2_collision_relation); + auto [x_t3, y_t3] = add(x3, y3, x_t2, y_t2, lambda3, add3, add_relation, x3_collision_relation); + auto [x_t4, y_t4] = add(x4, y4, x_t3, y_t3, lambda4, add4, add_relation, x4_collision_relation); + + // Validate accumulator output matches ADD output if q_add = 1 + // (this is a degree-6 relation) + std::get<0>(accumulator) += q_add * (acc_x_shift - x_t4) * scaling_factor; + std::get<1>(accumulator) += q_add * (acc_y_shift - y_t4) * scaling_factor; + std::get<2>(accumulator) += q_add * add_relation * scaling_factor; + + /** + * @brief doubles a point. + * + * Degree of x_out = 2 + * Degree of y_out = 3 + * Degree of relation = 4 + */ + auto dbl = [&](auto& x, auto& y, auto& lambda, auto& relation) { + auto two_x = x + x; + relation += lambda * (y + y) - (two_x + x) * x; + auto x_out = lambda * lambda - two_x; + auto y_out = lambda * (x - x_out) - y; + return std::array{ x_out, y_out }; + }; + + /** + * @brief + * + * Algorithm to determine if round is a DOUBLE round: + * 1. count_shift >= msm_size + * 2. round != 32 + * + * Algorithm to process MSM DOUBLE round: + * [Acc_shift] = (([Acc].double()).double()).double() + * + * As with additions, the column q_double describes whether row is a double round. It is Prover-defined. + * The value of `msm_round` can only update when `q_double = 1` and we use this to ensure Prover correctly sets + * `q_double`. (see round transition relations further down) + */ + Accumulator double_relation(0); + auto [x_d1, y_d1] = dbl(acc_x, acc_y, lambda1, double_relation); + auto [x_d2, y_d2] = dbl(x_d1, y_d1, lambda2, double_relation); + auto [x_d3, y_d3] = dbl(x_d2, y_d2, lambda3, double_relation); + auto [x_d4, y_d4] = dbl(x_d3, y_d3, lambda4, double_relation); + std::get<10>(accumulator) += q_double * (acc_x_shift - x_d4) * scaling_factor; + std::get<11>(accumulator) += q_double * (acc_y_shift - y_d4) * scaling_factor; + std::get<12>(accumulator) += q_double * double_relation * scaling_factor; + + /** + * @brief SKEW operations + * When computing x * [P], if x is even we must subtract [P] from accumulator + * (this is because our windowed non-adjacent-form can only represent odd numbers) + * Round 32 represents "skew" round. + * If scalar slice == 7, we add into accumulator (point_table[7] maps to -[P]) + * If scalar slice == 0, we do not add into accumulator + * i.e. for the skew round we can use the slice values as our "selector" when doing conditional point adds + */ + Accumulator skew_relation(0); + static FF inverse_seven = FF(7).invert(); + auto skew1_select = slice1 * inverse_seven; + auto skew2_select = slice2 * inverse_seven; + auto skew3_select = slice3 * inverse_seven; + auto skew4_select = slice4 * inverse_seven; + Accumulator x1_skew_collision_relation(0); + Accumulator x2_skew_collision_relation(0); + Accumulator x3_skew_collision_relation(0); + Accumulator x4_skew_collision_relation(0); + // add skew points iff row is a SKEW row AND slice = 7 (point_table[7] maps to -[P]) + // N.B. while it would be nice to have one `add` relation for both ADD and SKEW rounds, + // this would increase degree of sumcheck identity vs evaluating them separately. + // This is because, for add rounds, the result of adding [P1], [Acc] is [P1 + Acc] or [P1] + // but for skew rounds, the result of adding [P1], [Acc] is [P1 + Acc] or [Acc] + auto [x_s1, y_s1] = add(x1, y1, acc_x, acc_y, lambda1, skew1_select, skew_relation, x1_skew_collision_relation); + auto [x_s2, y_s2] = add(x2, y2, x_s1, y_s1, lambda2, skew2_select, skew_relation, x2_skew_collision_relation); + auto [x_s3, y_s3] = add(x3, y3, x_s2, y_s2, lambda3, skew3_select, skew_relation, x3_skew_collision_relation); + auto [x_s4, y_s4] = add(x4, y4, x_s3, y_s3, lambda4, skew4_select, skew_relation, x4_skew_collision_relation); + + // Validate accumulator output matches SKEW output if q_skew = 1 + // (this is a degree-6 relation) + std::get<3>(accumulator) += q_skew * (acc_x_shift - x_s4) * scaling_factor; + std::get<4>(accumulator) += q_skew * (acc_y_shift - y_s4) * scaling_factor; + std::get<5>(accumulator) += q_skew * skew_relation * scaling_factor; + + // Check x-coordinates do not collide if row is an ADD row or a SKEW row + // if either q_add or q_skew = 1, an inverse should exist for each computed relation + // Step 1: construct boolean selectors that describe whether we added a point at the current row + const auto add_first_point = add_into_accumulator * q_add + q_skew * skew1_select; + const auto add_second_point = add2 * q_add + q_skew * skew2_select; + const auto add_third_point = add3 * q_add + q_skew * skew3_select; + const auto add_fourth_point = add4 * q_add + q_skew * skew4_select; + // Step 2: construct the delta between x-coordinates for each point add (depending on if row is ADD or SKEW) + const auto x1_delta = x1_skew_collision_relation * q_skew + x1_collision_relation * q_add; + const auto x2_delta = x2_skew_collision_relation * q_skew + x2_collision_relation * q_add; + const auto x3_delta = x3_skew_collision_relation * q_skew + x3_collision_relation * q_add; + const auto x4_delta = x4_skew_collision_relation * q_skew + x4_collision_relation * q_add; + // Step 3: x_delta * inverse - 1 = 0 if we performed a point addition (else x_delta * inverse = 0) + std::get<6>(accumulator) += (x1_delta * collision_inverse1 - add_first_point) * scaling_factor; + std::get<7>(accumulator) += (x2_delta * collision_inverse2 - add_second_point) * scaling_factor; + std::get<8>(accumulator) += (x3_delta * collision_inverse3 - add_third_point) * scaling_factor; + std::get<9>(accumulator) += (x4_delta * collision_inverse4 - add_fourth_point) * scaling_factor; + + // Validate that if q_add = 1 or q_skew = 1, add1 also is 1 + // TODO(@zac-williamson) Once we have a stable base to work off of, remove q_add1 and replace with q_msm_add + + // q_msm_skew (issue #2222) + std::get<32>(accumulator) += (add1 - q_add - q_skew) * scaling_factor; + + // If add_i = 0, slice_i = 0 + // When add_i = 0, force slice_i to ALSO be 0 + std::get<13>(accumulator) += (-add1 + 1) * slice1 * scaling_factor; + std::get<14>(accumulator) += (-add2 + 1) * slice2 * scaling_factor; + std::get<15>(accumulator) += (-add3 + 1) * slice3 * scaling_factor; + std::get<16>(accumulator) += (-add4 + 1) * slice4 * scaling_factor; + + // only one of q_skew, q_double, q_add can be nonzero + std::get<17>(accumulator) += (q_add * q_double + q_add * q_skew + q_double * q_skew) * scaling_factor; + + // We look up wnaf slices by mapping round + pc -> slice + // We use an exact set membership check to validate that + // wnafs written in wnaf_relation == wnafs read in msm relation + // We use `add1/add2/add3/add4` to flag whether we are performing a wnaf read op + // We can set these to be Prover-defined as the set membership check implicitly ensures that the correct reads + // have occurred. + // if msm_transition = 0, round_shift - round = 0 or 1 + const auto round_delta = round_shift - round; + + // ROUND TRANSITION LOGIC (when round does not change) + // If msm_transition = 0 (next row) then round_delta = 0 or 1 + const auto round_transition = round_delta * (-msm_transition_shift + 1); + std::get<18>(accumulator) += round_transition * (round_delta - 1) * scaling_factor; + + // ROUND TRANSITION LOGIC (when round DOES change) + // round_transition describes whether we are transitioning between rounds of an MSM + // If round_transition = 1, the next row is either a double (if round != 31) or we are adding skew (if round == + // 31) round_transition * skew * (round - 31) = 0 (if round tx and skew, round == 31) round_transition * (skew + + // double - 1) = 0 (if round tx, skew XOR double = 1) i.e. if round tx and round != 31, double = 1 + std::get<19>(accumulator) += round_transition * q_skew_shift * (round - 31) * scaling_factor; + std::get<20>(accumulator) += round_transition * (q_skew_shift + q_double_shift - 1) * scaling_factor; + + // if no double or no skew, round_delta = 0 + std::get<21>(accumulator) += round_transition * (-q_double_shift + 1) * (-q_skew_shift + 1) * scaling_factor; + + // if double, next double != 1 + std::get<22>(accumulator) += q_double * q_double_shift * scaling_factor; + + // if double, next add = 1 + std::get<23>(accumulator) += q_double * (-q_add_shift + 1) * scaling_factor; + + // updating count + // if msm_transition = 0 and round_transition = 0, count_shift = count + add1 + add2 + add3 + add4 + // todo: we need this? + std::get<24>(accumulator) += (-msm_transition_shift + 1) * (-round_delta + 1) * + (count_shift - count - add1 - add2 - add3 - add4) * scaling_factor; + + std::get<25>(accumulator) += + is_not_first_row * (-msm_transition_shift + 1) * round_delta * count_shift * scaling_factor; + + // if msm_transition = 1, count_shift = 0 + std::get<26>(accumulator) += is_not_first_row * msm_transition_shift * count_shift * scaling_factor; + + // if msm_transition = 1, pc = pc_shift + msm_size + // `ecc_set_relation` ensures `msm_size` maps to `transcript.msm_count` for the current value of `pc` + std::get<27>(accumulator) += is_not_first_row * msm_transition_shift * (msm_size + pc_shift - pc) * scaling_factor; + + // Addition continuity checks + // We want to RULE OUT the following scenarios: + // Case 1: add2 = 1, add1 = 0 + // Case 2: add3 = 1, add2 = 0 + // Case 3: add4 = 1, add3 = 0 + // These checks ensure that the current row does not skip points (for both ADD and SKEW ops) + // This is part of a wider set of checks we use to ensure that all point data is used in the assigned + // multiscalar multiplication operation. + // (and not in a different MSM operation) + std::get<28>(accumulator) += add2 * (-add1 + 1) * scaling_factor; + std::get<29>(accumulator) += add3 * (-add2 + 1) * scaling_factor; + std::get<30>(accumulator) += add4 * (-add3 + 1) * scaling_factor; + + // Final continuity check. + // If an addition spans two rows, we need to make sure that the following scenario is RULED OUT: + // add4 = 0 on the CURRENT row, add1 = 1 on the NEXT row + // We must apply the above for the two cases: + // Case 1: q_add = 1 on the CURRENT row, q_add = 1 on the NEXT row + // Case 2: q_skew = 1 on the CURRENT row, q_skew = 1 on the NEXT row + // (i.e. if q_skew = 1, q_add_shift = 1 this implies an MSM transition so we skip this continuity check) + std::get<31>(accumulator) += + (q_add * q_add_shift + q_skew * q_skew_shift) * (-add4 + 1) * add1_shift * scaling_factor; + + // remaining checks (done in ecc_set_relation.hpp, ecc_lookup_relation.hpp) + // when transition occurs, perform set membership lookup on (accumulator / pc / msm_size) + // perform set membership lookups on add_i * (pc / round / slice_i) + // perform lookups on (pc / slice_i / x / y) +} +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation.cpp index 0efec02d548..ce12ce062ac 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation.cpp @@ -1,177 +1,8 @@ -#include "ecc_point_table_relation.hpp" #include "barretenberg/eccvm/eccvm_flavor.hpp" #include "barretenberg/flavor/relation_definitions.hpp" +#include "ecc_point_table_relation_impl.hpp" namespace bb { - -/** - * @brief ECCVMPointTableRelationImpl - * @details These relations define the set of point lookup tables we will use in `ecc_msm_relation.hpp`, to evaluate - * multiscalar multiplication. For every point [P] = (Px, Py) involved in an MSM, we need to do define a lookup - * table out of the following points: { -15[P], -13[P], -11[P], -9[P], -7[P], -5[P], -3[P], -[P] } - * ECCVMPointTableRelationImpl defines relations that define the lookup table. - * - * @param evals transformed to `evals + C(in(X)...)*scaling_factor` - * @param in an std::array containing the fully extended Accumulator edges. - * @param parameters contains beta, gamma, and public_input_delta, .... - * @param scaling_factor optional term to scale the evaluation before adding to evals. - */ -template -template -void ECCVMPointTableRelationImpl::accumulate(ContainerOverSubrelations& accumulator, - const AllEntities& in, - const Parameters& /*unused*/, - const FF& scaling_factor) -{ - using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - - const auto& Tx = View(in.precompute_tx); - const auto& Tx_shift = View(in.precompute_tx_shift); - const auto& Ty = View(in.precompute_ty); - const auto& Ty_shift = View(in.precompute_ty_shift); - const auto& Dx = View(in.precompute_dx); - const auto& Dx_shift = View(in.precompute_dx_shift); - const auto& Dy = View(in.precompute_dy); - const auto& Dy_shift = View(in.precompute_dy_shift); - const auto& precompute_point_transition = View(in.precompute_point_transition); - const auto& lagrange_first = View(in.lagrange_first); - - /** - * @brief Row structure - * - * Consider the set of (128-bit scalar multiplier, point, pc) tuples in the transcript columns. - * The point table columns process one tuple every 8 rows. The tuple with the largest pc value is first. - * When transitioning between tuple elements, pc decrements by 1. - * - * The following table gives an example for two points. - * In the table, the point associated with `pc = 1` is labelled P. - * the point associated with `pc = 0` is labelled Q. - * - * | precompute_pc | precompute_point_transition | precompute_round | Tx | Ty | Dx | Dy | - * | -------- | ----------------------- | ----------- | ----- | ----- | ---- | ---- | - * | 1 | 0 | 0 |15P.x | 15P.y | 2P.x | 2P.y | - * | 1 | 0 | 1 |13P.x | 13P.y | 2P.x | 2P.y | - * | 1 | 0 | 2 |11P.x | 11P.y | 2P.x | 2P.y | - * | 1 | 0 | 3 | 9P.x | 9P.y | 2P.x | 2P.y | - * | 1 | 0 | 4 | 7P.x | 7P.y | 2P.x | 2P.y | - * | 1 | 0 | 5 | 5P.x | 5P.y | 2P.x | 2P.y | - * | 1 | 0 | 6 | 3P.x | 3P.y | 2P.x | 2P.y | - * | 1 | 1 | 7 | P.x | P.y | 2P.x | 2P.y | - * | 0 | 0 | 0 |15Q.x | 15Q.y | 2Q.x | 2Q.y | - * | 0 | 0 | 1 |13Q.x | 13Q.y | 2Q.x | 2Q.y | - * | 0 | 0 | 2 |11Q.x | 11Q.y | 2Q.x | 2Q.y | - * | 0 | 0 | 3 | 9Q.x | 9Q.y | 2Q.x | 2Q.y | - * | 0 | 0 | 4 | 7Q.x | 7Q.y | 2Q.x | 2Q.y | - * | 0 | 0 | 5 | 5Q.x | 5Q.y | 2Q.x | 2Q.y | - * | 0 | 0 | 6 | 3Q.x | 3Q.y | 2Q.x | 2Q.y | - * | 0 | 1 | 7 | Q.x | Q.y | 2Q.x | 2Q.y | - * - * We apply the following relations to constrain the above table: - * - * 1. If precompute_point_transition = 0, (Dx, Dy) = (Dx_shift, Dy_shift) - * 2. If precompute_point_transition = 1, (Dx, Dy) = 2 (Px, Py) - * 3. If precompute_point_transition = 0, (Tx, Ty) = (Tx_shift, Ty_shift) + (Dx, Dy) - * - * The relations that constrain `precompute_point_transition` and `precompute_pc` are in `ecc_wnaf_relation.hpp` - * - * When precompute_point_transition = 1, we use a strict lookup protocol in `ecc_set_relation.hpp` to validate (pc, - * Tx, Ty) belong to the set of points present in our transcript columns. - * ("strict" lookup protocol = every item in the table must be read from once, and only once) - * - * For every row, we use a lookup protocol in `ecc_lookup_relation.hpp` to write the following tuples into a lookup - * table: - * 1. (pc, 15 - precompute_round, Tx, Ty) - * 2. (pc, precompute_round, Tx, -Ty) - * - * The value `15 - precompute_round` describes the multiplier applied to P at the current row. - * (this can be expanded into a wnaf value by taking `2x - 15` where `x = 15 - precompute_round`) . - * The value `precompute_round` describes the *negative multiplier* applied to P at the current row. - * This is also expanded into a wnaf value by taking `2x - 15` where `x = precompute_round`. - * - * The following table describes how taking (15 - precompute_round) for positive values and (precompute_round) for - * negative values produces the WNAF slice values that correspond to the multipliers for (Tx, Ty) and (Tx, -Ty): - * - * | Tx | Ty | x = 15 - precompute_round | 2x - 15 | y = precompute_round | 2y - 15 | - * | ----- | ----- | -------------------- | ------- | --------------- | ------- | - * | 15P.x | 15P.y | 15 | 15 | 0 | -15 | - * | 13P.x | 13P.y | 14 | 13 | 1 | -13 | - * | 11P.x | 11P.y | 13 | 11 | 2 | -11 | - * | 9P.x | 9P.y | 12 | 9 | 3 | -9 | - * | 7P.x | 7P.y | 11 | 7 | 4 | -7 | - * | 5P.x | 5P.y | 10 | 5 | 5 | -5 | - * | 3P.x | 3P.y | 9 | 3 | 6 | -3 | - * | P.x | P.y | 8 | 1 | 7 | -1 | - */ - - /** - * @brief Validate Dx, Dy correctness relation - * - * When computing a point table for point [P] = (Px, Py), we require [D] (Dx, Dy) = 2.[P] - * If all other relations are satisfied, we know that (Tx, Ty) = (Px, Py) - * i.e. (Dx, Dy) = 2(Px, Py) when precompute_round_transition = 1. - * - * Double formula: - * x_3 = 9x^4 / 4y^2 - 2x - * y_3 = (3x^2 / 2y) * (x - x_3) - y - * - * Expanding into relations: - * (x_3 + 2x) * 4y^2 - 9x^4 = 0 - * (y3 + y) * 2y - 3x^2 * (x - x_3) = 0 - */ - auto two_x = Tx + Tx; - auto three_x = two_x + Tx; - auto three_xx = Tx * three_x; - auto nine_xxxx = three_xx * three_xx; - auto two_y = Ty + Ty; - auto four_yy = two_y * two_y; - auto x_double_check = (Dx + two_x) * four_yy - nine_xxxx; - auto y_double_check = (Ty + Dy) * two_y + three_xx * (Dx - Tx); - std::get<0>(accumulator) += precompute_point_transition * x_double_check * scaling_factor; - std::get<1>(accumulator) += precompute_point_transition * y_double_check * scaling_factor; - - /** - * @brief If precompute_round_transition = 0, (Dx_shift, Dy_shift) = (Dx, Dy) - * - * 1st row is empty => don't apply if lagrange_first == 1 - */ - std::get<2>(accumulator) += - (-lagrange_first + 1) * (-precompute_point_transition + 1) * (Dx - Dx_shift) * scaling_factor; - std::get<3>(accumulator) += - (-lagrange_first + 1) * (-precompute_point_transition + 1) * (Dy - Dy_shift) * scaling_factor; - - /** - * @brief Valdiate (Tx, Ty) is correctly computed from (Tx_shift, Ty_shift), (Dx, Dy). - * If precompute_round_transition = 0, [T] = [T_shift] + [D]. - * - * Add formula: - * x_3 = (y_2 - y_1)^2 / (x_2 - x_1)^2 - x_2 - x_1 - * y_3 = ((y_2 - y_1) / (x_2 - x_1)) * (x_1 - x_3) - y_1 - * - * Expanding into relations: - * (x_3 + x_2 + x_1) * (x_2 - x_1)^2 - (y_2 - y_1)^2 = 0 - * (y_3 + y_1) * (x_2 - x_1) + (x_3 - x_1) * (y_2 - y_1) = 0 - * - * We don't need to check for incomplete point addition edge case (x_1 == x_2) - * TODO explain why (computing simple point multiples cannot trigger the edge cases, but need to serve a proof of - * this...) - */ - const auto& x1 = Tx_shift; - const auto& y1 = Ty_shift; - const auto& x2 = Dx; - const auto& y2 = Dy; - const auto& x3 = Tx; - const auto& y3 = Ty; - const auto lambda_numerator = y2 - y1; - const auto lambda_denominator = x2 - x1; - auto x_add_check = (x3 + x2 + x1) * lambda_denominator * lambda_denominator - lambda_numerator * lambda_numerator; - auto y_add_check = (y3 + y1) * lambda_denominator + (x3 - x1) * lambda_numerator; - std::get<4>(accumulator) += - (-lagrange_first + 1) * (-precompute_point_transition + 1) * x_add_check * scaling_factor; - std::get<5>(accumulator) += - (-lagrange_first + 1) * (-precompute_point_transition + 1) * y_add_check * scaling_factor; -} - template class ECCVMPointTableRelationImpl; DEFINE_SUMCHECK_RELATION_CLASS(ECCVMPointTableRelationImpl, ECCVMFlavor); diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation_impl.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation_impl.hpp new file mode 100644 index 00000000000..efc80367f28 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_point_table_relation_impl.hpp @@ -0,0 +1,173 @@ +#pragma once +#include "ecc_point_table_relation.hpp" + +namespace bb { + +/** + * @brief ECCVMPointTableRelationImpl + * @details These relations define the set of point lookup tables we will use in `ecc_msm_relation.hpp`, to evaluate + * multiscalar multiplication. For every point [P] = (Px, Py) involved in an MSM, we need to do define a lookup + * table out of the following points: { -15[P], -13[P], -11[P], -9[P], -7[P], -5[P], -3[P], -[P] } + * ECCVMPointTableRelationImpl defines relations that define the lookup table. + * + * @param evals transformed to `evals + C(in(X)...)*scaling_factor` + * @param in an std::array containing the fully extended Accumulator edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ +template +template +void ECCVMPointTableRelationImpl::accumulate(ContainerOverSubrelations& accumulator, + const AllEntities& in, + const Parameters& /*unused*/, + const FF& scaling_factor) +{ + using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; + using View = typename Accumulator::View; + + const auto& Tx = View(in.precompute_tx); + const auto& Tx_shift = View(in.precompute_tx_shift); + const auto& Ty = View(in.precompute_ty); + const auto& Ty_shift = View(in.precompute_ty_shift); + const auto& Dx = View(in.precompute_dx); + const auto& Dx_shift = View(in.precompute_dx_shift); + const auto& Dy = View(in.precompute_dy); + const auto& Dy_shift = View(in.precompute_dy_shift); + const auto& precompute_point_transition = View(in.precompute_point_transition); + const auto& lagrange_first = View(in.lagrange_first); + + /** + * @brief Row structure + * + * Consider the set of (128-bit scalar multiplier, point, pc) tuples in the transcript columns. + * The point table columns process one tuple every 8 rows. The tuple with the largest pc value is first. + * When transitioning between tuple elements, pc decrements by 1. + * + * The following table gives an example for two points. + * In the table, the point associated with `pc = 1` is labelled P. + * the point associated with `pc = 0` is labelled Q. + * + * | precompute_pc | precompute_point_transition | precompute_round | Tx | Ty | Dx | Dy | + * | -------- | ----------------------- | ----------- | ----- | ----- | ---- | ---- | + * | 1 | 0 | 0 |15P.x | 15P.y | 2P.x | 2P.y | + * | 1 | 0 | 1 |13P.x | 13P.y | 2P.x | 2P.y | + * | 1 | 0 | 2 |11P.x | 11P.y | 2P.x | 2P.y | + * | 1 | 0 | 3 | 9P.x | 9P.y | 2P.x | 2P.y | + * | 1 | 0 | 4 | 7P.x | 7P.y | 2P.x | 2P.y | + * | 1 | 0 | 5 | 5P.x | 5P.y | 2P.x | 2P.y | + * | 1 | 0 | 6 | 3P.x | 3P.y | 2P.x | 2P.y | + * | 1 | 1 | 7 | P.x | P.y | 2P.x | 2P.y | + * | 0 | 0 | 0 |15Q.x | 15Q.y | 2Q.x | 2Q.y | + * | 0 | 0 | 1 |13Q.x | 13Q.y | 2Q.x | 2Q.y | + * | 0 | 0 | 2 |11Q.x | 11Q.y | 2Q.x | 2Q.y | + * | 0 | 0 | 3 | 9Q.x | 9Q.y | 2Q.x | 2Q.y | + * | 0 | 0 | 4 | 7Q.x | 7Q.y | 2Q.x | 2Q.y | + * | 0 | 0 | 5 | 5Q.x | 5Q.y | 2Q.x | 2Q.y | + * | 0 | 0 | 6 | 3Q.x | 3Q.y | 2Q.x | 2Q.y | + * | 0 | 1 | 7 | Q.x | Q.y | 2Q.x | 2Q.y | + * + * We apply the following relations to constrain the above table: + * + * 1. If precompute_point_transition = 0, (Dx, Dy) = (Dx_shift, Dy_shift) + * 2. If precompute_point_transition = 1, (Dx, Dy) = 2 (Px, Py) + * 3. If precompute_point_transition = 0, (Tx, Ty) = (Tx_shift, Ty_shift) + (Dx, Dy) + * + * The relations that constrain `precompute_point_transition` and `precompute_pc` are in `ecc_wnaf_relation.hpp` + * + * When precompute_point_transition = 1, we use a strict lookup protocol in `ecc_set_relation.hpp` to validate (pc, + * Tx, Ty) belong to the set of points present in our transcript columns. + * ("strict" lookup protocol = every item in the table must be read from once, and only once) + * + * For every row, we use a lookup protocol in `ecc_lookup_relation.hpp` to write the following tuples into a lookup + * table: + * 1. (pc, 15 - precompute_round, Tx, Ty) + * 2. (pc, precompute_round, Tx, -Ty) + * + * The value `15 - precompute_round` describes the multiplier applied to P at the current row. + * (this can be expanded into a wnaf value by taking `2x - 15` where `x = 15 - precompute_round`) . + * The value `precompute_round` describes the *negative multiplier* applied to P at the current row. + * This is also expanded into a wnaf value by taking `2x - 15` where `x = precompute_round`. + * + * The following table describes how taking (15 - precompute_round) for positive values and (precompute_round) for + * negative values produces the WNAF slice values that correspond to the multipliers for (Tx, Ty) and (Tx, -Ty): + * + * | Tx | Ty | x = 15 - precompute_round | 2x - 15 | y = precompute_round | 2y - 15 | + * | ----- | ----- | -------------------- | ------- | --------------- | ------- | + * | 15P.x | 15P.y | 15 | 15 | 0 | -15 | + * | 13P.x | 13P.y | 14 | 13 | 1 | -13 | + * | 11P.x | 11P.y | 13 | 11 | 2 | -11 | + * | 9P.x | 9P.y | 12 | 9 | 3 | -9 | + * | 7P.x | 7P.y | 11 | 7 | 4 | -7 | + * | 5P.x | 5P.y | 10 | 5 | 5 | -5 | + * | 3P.x | 3P.y | 9 | 3 | 6 | -3 | + * | P.x | P.y | 8 | 1 | 7 | -1 | + */ + + /** + * @brief Validate Dx, Dy correctness relation + * + * When computing a point table for point [P] = (Px, Py), we require [D] (Dx, Dy) = 2.[P] + * If all other relations are satisfied, we know that (Tx, Ty) = (Px, Py) + * i.e. (Dx, Dy) = 2(Px, Py) when precompute_round_transition = 1. + * + * Double formula: + * x_3 = 9x^4 / 4y^2 - 2x + * y_3 = (3x^2 / 2y) * (x - x_3) - y + * + * Expanding into relations: + * (x_3 + 2x) * 4y^2 - 9x^4 = 0 + * (y3 + y) * 2y - 3x^2 * (x - x_3) = 0 + */ + auto two_x = Tx + Tx; + auto three_x = two_x + Tx; + auto three_xx = Tx * three_x; + auto nine_xxxx = three_xx * three_xx; + auto two_y = Ty + Ty; + auto four_yy = two_y * two_y; + auto x_double_check = (Dx + two_x) * four_yy - nine_xxxx; + auto y_double_check = (Ty + Dy) * two_y + three_xx * (Dx - Tx); + std::get<0>(accumulator) += precompute_point_transition * x_double_check * scaling_factor; + std::get<1>(accumulator) += precompute_point_transition * y_double_check * scaling_factor; + + /** + * @brief If precompute_round_transition = 0, (Dx_shift, Dy_shift) = (Dx, Dy) + * + * 1st row is empty => don't apply if lagrange_first == 1 + */ + std::get<2>(accumulator) += + (-lagrange_first + 1) * (-precompute_point_transition + 1) * (Dx - Dx_shift) * scaling_factor; + std::get<3>(accumulator) += + (-lagrange_first + 1) * (-precompute_point_transition + 1) * (Dy - Dy_shift) * scaling_factor; + + /** + * @brief Valdiate (Tx, Ty) is correctly computed from (Tx_shift, Ty_shift), (Dx, Dy). + * If precompute_round_transition = 0, [T] = [T_shift] + [D]. + * + * Add formula: + * x_3 = (y_2 - y_1)^2 / (x_2 - x_1)^2 - x_2 - x_1 + * y_3 = ((y_2 - y_1) / (x_2 - x_1)) * (x_1 - x_3) - y_1 + * + * Expanding into relations: + * (x_3 + x_2 + x_1) * (x_2 - x_1)^2 - (y_2 - y_1)^2 = 0 + * (y_3 + y_1) * (x_2 - x_1) + (x_3 - x_1) * (y_2 - y_1) = 0 + * + * We don't need to check for incomplete point addition edge case (x_1 == x_2) + * TODO explain why (computing simple point multiples cannot trigger the edge cases, but need to serve a proof of + * this...) + */ + const auto& x1 = Tx_shift; + const auto& y1 = Ty_shift; + const auto& x2 = Dx; + const auto& y2 = Dy; + const auto& x3 = Tx; + const auto& y3 = Ty; + const auto lambda_numerator = y2 - y1; + const auto lambda_denominator = x2 - x1; + auto x_add_check = (x3 + x2 + x1) * lambda_denominator * lambda_denominator - lambda_numerator * lambda_numerator; + auto y_add_check = (y3 + y1) * lambda_denominator + (x3 - x1) * lambda_numerator; + std::get<4>(accumulator) += + (-lagrange_first + 1) * (-precompute_point_transition + 1) * x_add_check * scaling_factor; + std::get<5>(accumulator) += + (-lagrange_first + 1) * (-precompute_point_transition + 1) * y_add_check * scaling_factor; +} +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation.cpp index 3fa0e512058..03e8565fcb4 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation.cpp @@ -1,398 +1,8 @@ #include "barretenberg/eccvm/eccvm_flavor.hpp" #include "barretenberg/flavor/relation_definitions.hpp" -#include "ecc_msm_relation.hpp" +#include "ecc_set_relation_impl.hpp" namespace bb { - -/** - * @brief Performs list-equivalence checks for the ECCVM - * - * @details ECCVMSetRelationImpl validates the correctness of the inputs/outputs of the three main algorithms evaluated - * by the ECCVM. - * - * First term: tuple of (pc, round, wnaf_slice), computed when slicing scalar multipliers into slices, - * as part of ECCVMWnafRelation - * Input source: ECCVMWnafRelation - * Output source: ECCVMMSMRelation - * - * - * Second term: tuple of (point-counter, P.x, P.y, scalar-multiplier), used in ECCVMWnafRelation and - * ECCVMPointTableRelation - * Input source: ECCVMPointTableRelation - * Output source: ECCVMMSMRelation - * - * Third term: tuple of (point-counter, P.x, P.y, msm-size) from ECCVMMSMRelation - * Input source: ECCVMMSMRelation - * Output source: ECCVMTranscriptRelation - * - * @tparam FF - * @tparam AccumulatorTypes - * @param in - * @param relation_params - * @param index - * @return ECCVMSetRelationImpl::template Accumulator - */ -template -template -Accumulator ECCVMSetRelationImpl::compute_grand_product_numerator(const AllEntities& in, const Parameters& params) -{ - using View = typename Accumulator::View; - - const auto& precompute_round = View(in.precompute_round); - const auto precompute_round2 = precompute_round + precompute_round; - const auto precompute_round4 = precompute_round2 + precompute_round2; - - const auto& gamma = params.gamma; - const auto& beta = params.beta; - const auto& beta_sqr = params.beta_sqr; - const auto& beta_cube = params.beta_cube; - const auto& precompute_pc = View(in.precompute_pc); - const auto& precompute_select = View(in.precompute_select); - - /** - * @brief First term: tuple of (pc, round, wnaf_slice), computed when slicing scalar multipliers into slices, - * as part of ECCVMWnafRelation. - * If precompute_select = 1, tuple entry = (wnaf-slice + point-counter * beta + msm-round * beta_sqr). - * There are 4 tuple entries per row. - */ - Accumulator numerator(1); // degree-0 - { - const auto& s0 = View(in.precompute_s1hi); - const auto& s1 = View(in.precompute_s1lo); - - auto wnaf_slice = s0 + s0; - wnaf_slice += wnaf_slice; - wnaf_slice += s1; - - // TODO(@zac-williamson #2226) optimize - const auto wnaf_slice_input0 = wnaf_slice + gamma + precompute_pc * beta + precompute_round4 * beta_sqr; - numerator *= wnaf_slice_input0; // degree-1 - } - { - const auto& s0 = View(in.precompute_s2hi); - const auto& s1 = View(in.precompute_s2lo); - - auto wnaf_slice = s0 + s0; - wnaf_slice += wnaf_slice; - wnaf_slice += s1; - - // TODO(@zac-williamson #2226) optimize - const auto wnaf_slice_input1 = wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 1) * beta_sqr; - numerator *= wnaf_slice_input1; // degree-2 - } - { - const auto& s0 = View(in.precompute_s3hi); - const auto& s1 = View(in.precompute_s3lo); - - auto wnaf_slice = s0 + s0; - wnaf_slice += wnaf_slice; - wnaf_slice += s1; - - // TODO(@zac-williamson #2226) optimize - const auto wnaf_slice_input2 = wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 2) * beta_sqr; - numerator *= wnaf_slice_input2; // degree-3 - } - { - const auto& s0 = View(in.precompute_s4hi); - const auto& s1 = View(in.precompute_s4lo); - - auto wnaf_slice = s0 + s0; - wnaf_slice += wnaf_slice; - wnaf_slice += s1; - // TODO(@zac-williamson #2226) optimize - const auto wnaf_slice_input3 = wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 3) * beta_sqr; - numerator *= wnaf_slice_input3; // degree-4 - } - { - // skew product if relevant - const auto& skew = View(in.precompute_skew); - const auto& precompute_point_transition = View(in.precompute_point_transition); - const auto skew_input = - precompute_point_transition * (skew + gamma + precompute_pc * beta + (precompute_round4 + 4) * beta_sqr) + - (-precompute_point_transition + 1); - numerator *= skew_input; // degree-5 - } - { - const auto& eccvm_set_permutation_delta = params.eccvm_set_permutation_delta; - numerator *= precompute_select * (-eccvm_set_permutation_delta + 1) + eccvm_set_permutation_delta; // degree-7 - } - - /** - * @brief Second term: tuple of (point-counter, P.x, P.y, scalar-multiplier), used in ECCVMWnafRelation and - * ECCVMPointTableRelation. ECCVMWnafRelation validates the sum of the wnaf slices associated with point-counter - * equals scalar-multiplier. ECCVMPointTableRelation computes a table of muliples of [P]: { -15[P], -13[P], ..., - * 15[P] }. We need to validate that scalar-multiplier and [P] = (P.x, P.y) come from MUL opcodes in the transcript - * columns. - */ - { - const auto& table_x = View(in.precompute_tx); - const auto& table_y = View(in.precompute_ty); - - const auto& precompute_skew = View(in.precompute_skew); - static constexpr FF negative_inverse_seven = FF(-7).invert(); - auto adjusted_skew = precompute_skew * negative_inverse_seven; - - const auto& wnaf_scalar_sum = View(in.precompute_scalar_sum); - const auto w0 = convert_to_wnaf(View(in.precompute_s1hi), View(in.precompute_s1lo)); - const auto w1 = convert_to_wnaf(View(in.precompute_s2hi), View(in.precompute_s2lo)); - const auto w2 = convert_to_wnaf(View(in.precompute_s3hi), View(in.precompute_s3lo)); - const auto w3 = convert_to_wnaf(View(in.precompute_s4hi), View(in.precompute_s4lo)); - - auto row_slice = w0; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += w1; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += w2; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += w3; - - auto scalar_sum_full = wnaf_scalar_sum + wnaf_scalar_sum; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += scalar_sum_full; - scalar_sum_full += row_slice + adjusted_skew; - - auto precompute_point_transition = View(in.precompute_point_transition); - - auto point_table_init_read = - (precompute_pc + table_x * beta + table_y * beta_sqr + scalar_sum_full * beta_cube); - point_table_init_read = - precompute_point_transition * (point_table_init_read + gamma) + (-precompute_point_transition + 1); - - numerator *= point_table_init_read; // degree-9 - } - /** - * @brief Third term: tuple of (point-counter, P.x, P.y, msm-size) from ECCVMMSMRelation. - * (P.x, P.y) is the output of a multi-scalar-multiplication evaluated in ECCVMMSMRelation. - * We need to validate that the same values (P.x, P.y) are present in the Transcript columns and describe a - * multi-scalar multiplication of size `msm-size`, starting at `point-counter`. - * - * If msm_transition_shift = 1, this indicates the current row is the last row of a multiscalar - * multiplication evaluation. The output of the MSM will be present on `(msm_accumulator_x_shift, - * msm_accumulator_y_shift)`. The values of `msm_accumulator_x_shift, msm_accumulator_y_shift, msm_pc, - * msm_size_of_msm` must match up with equivalent values `transcript_msm_output_x, transcript_msm_output_y, - * transcript_pc, transcript_msm_count` present in the Transcript columns - */ - { - const auto& lagrange_first = View(in.lagrange_first); - const auto& partial_msm_transition_shift = View(in.msm_transition_shift); - const auto msm_transition_shift = (-lagrange_first + 1) * partial_msm_transition_shift; - const auto& msm_pc_shift = View(in.msm_pc_shift); - - const auto& msm_x_shift = View(in.msm_accumulator_x_shift); - const auto& msm_y_shift = View(in.msm_accumulator_y_shift); - const auto& msm_size = View(in.msm_size_of_msm); - - // msm_transition = 1 when a row BEGINS a new msm - // - // row msm tx acc.x acc.y pc msm_size - // i 0 no no no yes - // i+1 1 yes yes yes no - // - // at row i we are at the final row of the current msm - // at row i the value of `msm_size` = size of current msm - // at row i + 1 we have the final accumulated value of the msm computation - // at row i + 1 we have updated `pc` to be `(pc at start of msm) + msm_count` - // at row i + 1 q_msm_transtiion = 1 - - auto msm_result_write = msm_pc_shift + msm_x_shift * beta + msm_y_shift * beta_sqr + msm_size * beta_cube; - - // msm_result_write = degree 2 - msm_result_write = msm_transition_shift * (msm_result_write + gamma) + (-msm_transition_shift + 1); - numerator *= msm_result_write; // degree-11 - } - return numerator; -} - -template -template -Accumulator ECCVMSetRelationImpl::compute_grand_product_denominator(const AllEntities& in, const Parameters& params) -{ - using View = typename Accumulator::View; - - // TODO(@zac-williamson). The degree of this contribution is 17! makes overall relation degree 19. - // Can optimise by refining the algebra, once we have a stable base to iterate off of. - const auto& gamma = params.gamma; - const auto& beta = params.beta; - const auto& beta_sqr = params.beta_sqr; - const auto& beta_cube = params.beta_cube; - const auto& msm_pc = View(in.msm_pc); - const auto& msm_count = View(in.msm_count); - const auto& msm_round = View(in.msm_round); - - /** - * @brief First term: tuple of (pc, round, wnaf_slice), used to determine which points we extract from lookup tables - * when evaluaing MSMs in ECCVMMsmRelation. - * These values must be equivalent to the values computed in the 1st term of `compute_grand_product_numerator` - */ - Accumulator denominator(1); // degree-0 - { - const auto& add1 = View(in.msm_add1); - const auto& msm_slice1 = View(in.msm_slice1); - - auto wnaf_slice_output1 = - add1 * (msm_slice1 + gamma + (msm_pc - msm_count) * beta + msm_round * beta_sqr) + (-add1 + 1); - denominator *= wnaf_slice_output1; // degree-2 - } - { - const auto& add2 = View(in.msm_add2); - const auto& msm_slice2 = View(in.msm_slice2); - - auto wnaf_slice_output2 = - add2 * (msm_slice2 + gamma + (msm_pc - msm_count - 1) * beta + msm_round * beta_sqr) + (-add2 + 1); - denominator *= wnaf_slice_output2; // degree-4 - } - { - const auto& add3 = View(in.msm_add3); - const auto& msm_slice3 = View(in.msm_slice3); - - auto wnaf_slice_output3 = - add3 * (msm_slice3 + gamma + (msm_pc - msm_count - 2) * beta + msm_round * beta_sqr) + (-add3 + 1); - denominator *= wnaf_slice_output3; // degree-6 - } - { - const auto& add4 = View(in.msm_add4); - const auto& msm_slice4 = View(in.msm_slice4); - auto wnaf_slice_output4 = - add4 * (msm_slice4 + gamma + (msm_pc - msm_count - 3) * beta + msm_round * beta_sqr) + (-add4 + 1); - denominator *= wnaf_slice_output4; // degree-8 - } - - /** - * @brief Second term: tuple of (transcript_pc, transcript_Px, transcript_Py, z1) OR (transcript_pc, \lambda * - * transcript_Px, -transcript_Py, z2) for each scalar multiplication in ECCVMTranscriptRelation columns. (the latter - * term uses the curve endomorphism: \lambda = cube root of unity). These values must be equivalent to the second - * term values in `compute_grand_product_numerator` - */ - { - const auto& transcript_pc = View(in.transcript_pc); - - auto transcript_Px = View(in.transcript_Px); - auto transcript_Py = View(in.transcript_Py); - auto z1 = View(in.transcript_z1); - auto z2 = View(in.transcript_z2); - auto z1_zero = View(in.transcript_z1zero); - auto z2_zero = View(in.transcript_z2zero); - auto transcript_mul = View(in.transcript_mul); - - auto lookup_first = (-z1_zero + 1); - auto lookup_second = (-z2_zero + 1); - FF endomorphism_base_field_shift = FF::cube_root_of_unity(); - - auto transcript_input1 = transcript_pc + transcript_Px * beta + transcript_Py * beta_sqr + z1 * beta_cube; - auto transcript_input2 = (transcript_pc - 1) + transcript_Px * endomorphism_base_field_shift * beta - - transcript_Py * beta_sqr + z2 * beta_cube; - - // | q_mul | z2_zero | z1_zero | lookup | - // | ----- | ------- | ------- | ---------------------- | - // | 0 | - | - | 1 | - // | 1 | 0 | 1 | X + gamma | - // | 1 | 1 | 0 | Y + gamma | - // | 1 | 1 | 1 | (X + gamma)(Y + gamma) | - transcript_input1 = (transcript_input1 + gamma) * lookup_first + (-lookup_first + 1); - transcript_input2 = (transcript_input2 + gamma) * lookup_second + (-lookup_second + 1); - // point_table_init_write = degree 2 - - auto point_table_init_write = transcript_mul * transcript_input1 * transcript_input2 + (-transcript_mul + 1); - denominator *= point_table_init_write; // degree-13 - - // auto point_table_init_write_1 = transcript_mul * transcript_input1 + (-transcript_mul + 1); - // denominator *= point_table_init_write_1; // degree-11 - - // auto point_table_init_write_2 = transcript_mul * transcript_input2 + (-transcript_mul + 1); - // denominator *= point_table_init_write_2; // degree-14 - } - /** - * @brief Third term: tuple of (point-counter, P.x, P.y, msm-size) from ECCVMTranscriptRelation. - * (P.x, P.y) is the *claimed* output of a multi-scalar-multiplication evaluated in ECCVMMSMRelation. - * We need to validate that the msm output produced in ECCVMMSMRelation is equivalent to the output present - * in `transcript_msm_output_x, transcript_msm_output_y`, for a given multi-scalar multiplication starting at - * `transcript_pc` and has size `transcript_msm_count` - */ - { - auto transcript_pc_shift = View(in.transcript_pc_shift); - auto transcript_msm_x = View(in.transcript_msm_x); - auto transcript_msm_y = View(in.transcript_msm_y); - auto transcript_msm_transition = View(in.transcript_msm_transition); - auto transcript_msm_count = View(in.transcript_msm_count); - auto z1_zero = View(in.transcript_z1zero); - auto z2_zero = View(in.transcript_z2zero); - auto transcript_mul = View(in.transcript_mul); - - auto full_msm_count = transcript_msm_count + transcript_mul * ((-z1_zero + 1) + (-z2_zero + 1)); - // auto count_test = transcript_msm_count - // msm_result_read = degree 2 - auto msm_result_read = - transcript_pc_shift + transcript_msm_x * beta + transcript_msm_y * beta_sqr + full_msm_count * beta_cube; - - msm_result_read = transcript_msm_transition * (msm_result_read + gamma) + (-transcript_msm_transition + 1); - denominator *= msm_result_read; // degree-17 - } - return denominator; -} - -/** - * @brief Expression for the StandardArithmetic gate. - * @dbetails The relation is defined as C(in(X)...) = - * (q_m * w_r * w_l) + (q_l * w_l) + (q_r * w_r) + (q_o * w_o) + q_c - * - * @param evals transformed to `evals + C(in(X)...)*scaling_factor` - * @param in an std::array containing the fully extended Accumulator edges. - * @param parameters contains beta, gamma, and public_input_delta, .... - * @param scaling_factor optional term to scale the evaluation before adding to evals. - */ -template -template -void ECCVMSetRelationImpl::accumulate(ContainerOverSubrelations& accumulator, - const AllEntities& in, - const Parameters& params, - const FF& scaling_factor) -{ - using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - - // degree-11 - Accumulator numerator_evaluation = compute_grand_product_numerator(in, params); - - // degree-17 - Accumulator denominator_evaluation = compute_grand_product_denominator(in, params); - - const auto& lagrange_first = View(in.lagrange_first); - const auto& lagrange_last = View(in.lagrange_last); - - const auto& z_perm = View(in.z_perm); - const auto& z_perm_shift = View(in.z_perm_shift); - - // degree-18 - std::get<0>(accumulator) += - ((z_perm + lagrange_first) * numerator_evaluation - (z_perm_shift + lagrange_last) * denominator_evaluation) * - scaling_factor; - - // Contribution (2) - std::get<1>(accumulator) += (lagrange_last * z_perm_shift) * scaling_factor; -} - template class ECCVMSetRelationImpl; DEFINE_SUMCHECK_RELATION_CLASS(ECCVMSetRelationImpl, ECCVMFlavor); DEFINE_SUMCHECK_PERMUTATION_CLASS(ECCVMSetRelationImpl, ECCVMFlavor); diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation_impl.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation_impl.hpp new file mode 100644 index 00000000000..a5c8ed40570 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation_impl.hpp @@ -0,0 +1,394 @@ +#pragma once +#include "ecc_set_relation.hpp" + +namespace bb { + +/** + * @brief Performs list-equivalence checks for the ECCVM + * + * @details ECCVMSetRelationImpl validates the correctness of the inputs/outputs of the three main algorithms evaluated + * by the ECCVM. + * + * First term: tuple of (pc, round, wnaf_slice), computed when slicing scalar multipliers into slices, + * as part of ECCVMWnafRelation + * Input source: ECCVMWnafRelation + * Output source: ECCVMMSMRelation + * + * + * Second term: tuple of (point-counter, P.x, P.y, scalar-multiplier), used in ECCVMWnafRelation and + * ECCVMPointTableRelation + * Input source: ECCVMPointTableRelation + * Output source: ECCVMMSMRelation + * + * Third term: tuple of (point-counter, P.x, P.y, msm-size) from ECCVMMSMRelation + * Input source: ECCVMMSMRelation + * Output source: ECCVMTranscriptRelation + * + * @tparam FF + * @tparam AccumulatorTypes + * @param in + * @param relation_params + * @param index + * @return ECCVMSetRelationImpl::template Accumulator + */ +template +template +Accumulator ECCVMSetRelationImpl::compute_grand_product_numerator(const AllEntities& in, const Parameters& params) +{ + using View = typename Accumulator::View; + + const auto& precompute_round = View(in.precompute_round); + const auto precompute_round2 = precompute_round + precompute_round; + const auto precompute_round4 = precompute_round2 + precompute_round2; + + const auto& gamma = params.gamma; + const auto& beta = params.beta; + const auto& beta_sqr = params.beta_sqr; + const auto& beta_cube = params.beta_cube; + const auto& precompute_pc = View(in.precompute_pc); + const auto& precompute_select = View(in.precompute_select); + + /** + * @brief First term: tuple of (pc, round, wnaf_slice), computed when slicing scalar multipliers into slices, + * as part of ECCVMWnafRelation. + * If precompute_select = 1, tuple entry = (wnaf-slice + point-counter * beta + msm-round * beta_sqr). + * There are 4 tuple entries per row. + */ + Accumulator numerator(1); // degree-0 + { + const auto& s0 = View(in.precompute_s1hi); + const auto& s1 = View(in.precompute_s1lo); + + auto wnaf_slice = s0 + s0; + wnaf_slice += wnaf_slice; + wnaf_slice += s1; + + // TODO(@zac-williamson #2226) optimize + const auto wnaf_slice_input0 = wnaf_slice + gamma + precompute_pc * beta + precompute_round4 * beta_sqr; + numerator *= wnaf_slice_input0; // degree-1 + } + { + const auto& s0 = View(in.precompute_s2hi); + const auto& s1 = View(in.precompute_s2lo); + + auto wnaf_slice = s0 + s0; + wnaf_slice += wnaf_slice; + wnaf_slice += s1; + + // TODO(@zac-williamson #2226) optimize + const auto wnaf_slice_input1 = wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 1) * beta_sqr; + numerator *= wnaf_slice_input1; // degree-2 + } + { + const auto& s0 = View(in.precompute_s3hi); + const auto& s1 = View(in.precompute_s3lo); + + auto wnaf_slice = s0 + s0; + wnaf_slice += wnaf_slice; + wnaf_slice += s1; + + // TODO(@zac-williamson #2226) optimize + const auto wnaf_slice_input2 = wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 2) * beta_sqr; + numerator *= wnaf_slice_input2; // degree-3 + } + { + const auto& s0 = View(in.precompute_s4hi); + const auto& s1 = View(in.precompute_s4lo); + + auto wnaf_slice = s0 + s0; + wnaf_slice += wnaf_slice; + wnaf_slice += s1; + // TODO(@zac-williamson #2226) optimize + const auto wnaf_slice_input3 = wnaf_slice + gamma + precompute_pc * beta + (precompute_round4 + 3) * beta_sqr; + numerator *= wnaf_slice_input3; // degree-4 + } + { + // skew product if relevant + const auto& skew = View(in.precompute_skew); + const auto& precompute_point_transition = View(in.precompute_point_transition); + const auto skew_input = + precompute_point_transition * (skew + gamma + precompute_pc * beta + (precompute_round4 + 4) * beta_sqr) + + (-precompute_point_transition + 1); + numerator *= skew_input; // degree-5 + } + { + const auto& eccvm_set_permutation_delta = params.eccvm_set_permutation_delta; + numerator *= precompute_select * (-eccvm_set_permutation_delta + 1) + eccvm_set_permutation_delta; // degree-7 + } + + /** + * @brief Second term: tuple of (point-counter, P.x, P.y, scalar-multiplier), used in ECCVMWnafRelation and + * ECCVMPointTableRelation. ECCVMWnafRelation validates the sum of the wnaf slices associated with point-counter + * equals scalar-multiplier. ECCVMPointTableRelation computes a table of muliples of [P]: { -15[P], -13[P], ..., + * 15[P] }. We need to validate that scalar-multiplier and [P] = (P.x, P.y) come from MUL opcodes in the transcript + * columns. + */ + { + const auto& table_x = View(in.precompute_tx); + const auto& table_y = View(in.precompute_ty); + + const auto& precompute_skew = View(in.precompute_skew); + static FF negative_inverse_seven = FF(-7).invert(); + auto adjusted_skew = precompute_skew * negative_inverse_seven; + + const auto& wnaf_scalar_sum = View(in.precompute_scalar_sum); + const auto w0 = convert_to_wnaf(View(in.precompute_s1hi), View(in.precompute_s1lo)); + const auto w1 = convert_to_wnaf(View(in.precompute_s2hi), View(in.precompute_s2lo)); + const auto w2 = convert_to_wnaf(View(in.precompute_s3hi), View(in.precompute_s3lo)); + const auto w3 = convert_to_wnaf(View(in.precompute_s4hi), View(in.precompute_s4lo)); + + auto row_slice = w0; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += w1; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += w2; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += w3; + + auto scalar_sum_full = wnaf_scalar_sum + wnaf_scalar_sum; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += scalar_sum_full; + scalar_sum_full += row_slice + adjusted_skew; + + auto precompute_point_transition = View(in.precompute_point_transition); + + auto point_table_init_read = + (precompute_pc + table_x * beta + table_y * beta_sqr + scalar_sum_full * beta_cube); + point_table_init_read = + precompute_point_transition * (point_table_init_read + gamma) + (-precompute_point_transition + 1); + + numerator *= point_table_init_read; // degree-9 + } + /** + * @brief Third term: tuple of (point-counter, P.x, P.y, msm-size) from ECCVMMSMRelation. + * (P.x, P.y) is the output of a multi-scalar-multiplication evaluated in ECCVMMSMRelation. + * We need to validate that the same values (P.x, P.y) are present in the Transcript columns and describe a + * multi-scalar multiplication of size `msm-size`, starting at `point-counter`. + * + * If msm_transition_shift = 1, this indicates the current row is the last row of a multiscalar + * multiplication evaluation. The output of the MSM will be present on `(msm_accumulator_x_shift, + * msm_accumulator_y_shift)`. The values of `msm_accumulator_x_shift, msm_accumulator_y_shift, msm_pc, + * msm_size_of_msm` must match up with equivalent values `transcript_msm_output_x, transcript_msm_output_y, + * transcript_pc, transcript_msm_count` present in the Transcript columns + */ + { + const auto& lagrange_first = View(in.lagrange_first); + const auto& partial_msm_transition_shift = View(in.msm_transition_shift); + const auto msm_transition_shift = (-lagrange_first + 1) * partial_msm_transition_shift; + const auto& msm_pc_shift = View(in.msm_pc_shift); + + const auto& msm_x_shift = View(in.msm_accumulator_x_shift); + const auto& msm_y_shift = View(in.msm_accumulator_y_shift); + const auto& msm_size = View(in.msm_size_of_msm); + + // msm_transition = 1 when a row BEGINS a new msm + // + // row msm tx acc.x acc.y pc msm_size + // i 0 no no no yes + // i+1 1 yes yes yes no + // + // at row i we are at the final row of the current msm + // at row i the value of `msm_size` = size of current msm + // at row i + 1 we have the final accumulated value of the msm computation + // at row i + 1 we have updated `pc` to be `(pc at start of msm) + msm_count` + // at row i + 1 q_msm_transtiion = 1 + + auto msm_result_write = msm_pc_shift + msm_x_shift * beta + msm_y_shift * beta_sqr + msm_size * beta_cube; + + // msm_result_write = degree 2 + msm_result_write = msm_transition_shift * (msm_result_write + gamma) + (-msm_transition_shift + 1); + numerator *= msm_result_write; // degree-11 + } + return numerator; +} + +template +template +Accumulator ECCVMSetRelationImpl::compute_grand_product_denominator(const AllEntities& in, const Parameters& params) +{ + using View = typename Accumulator::View; + + // TODO(@zac-williamson). The degree of this contribution is 17! makes overall relation degree 19. + // Can optimise by refining the algebra, once we have a stable base to iterate off of. + const auto& gamma = params.gamma; + const auto& beta = params.beta; + const auto& beta_sqr = params.beta_sqr; + const auto& beta_cube = params.beta_cube; + const auto& msm_pc = View(in.msm_pc); + const auto& msm_count = View(in.msm_count); + const auto& msm_round = View(in.msm_round); + + /** + * @brief First term: tuple of (pc, round, wnaf_slice), used to determine which points we extract from lookup tables + * when evaluaing MSMs in ECCVMMsmRelation. + * These values must be equivalent to the values computed in the 1st term of `compute_grand_product_numerator` + */ + Accumulator denominator(1); // degree-0 + { + const auto& add1 = View(in.msm_add1); + const auto& msm_slice1 = View(in.msm_slice1); + + auto wnaf_slice_output1 = + add1 * (msm_slice1 + gamma + (msm_pc - msm_count) * beta + msm_round * beta_sqr) + (-add1 + 1); + denominator *= wnaf_slice_output1; // degree-2 + } + { + const auto& add2 = View(in.msm_add2); + const auto& msm_slice2 = View(in.msm_slice2); + + auto wnaf_slice_output2 = + add2 * (msm_slice2 + gamma + (msm_pc - msm_count - 1) * beta + msm_round * beta_sqr) + (-add2 + 1); + denominator *= wnaf_slice_output2; // degree-4 + } + { + const auto& add3 = View(in.msm_add3); + const auto& msm_slice3 = View(in.msm_slice3); + + auto wnaf_slice_output3 = + add3 * (msm_slice3 + gamma + (msm_pc - msm_count - 2) * beta + msm_round * beta_sqr) + (-add3 + 1); + denominator *= wnaf_slice_output3; // degree-6 + } + { + const auto& add4 = View(in.msm_add4); + const auto& msm_slice4 = View(in.msm_slice4); + auto wnaf_slice_output4 = + add4 * (msm_slice4 + gamma + (msm_pc - msm_count - 3) * beta + msm_round * beta_sqr) + (-add4 + 1); + denominator *= wnaf_slice_output4; // degree-8 + } + + /** + * @brief Second term: tuple of (transcript_pc, transcript_Px, transcript_Py, z1) OR (transcript_pc, \lambda * + * transcript_Px, -transcript_Py, z2) for each scalar multiplication in ECCVMTranscriptRelation columns. (the latter + * term uses the curve endomorphism: \lambda = cube root of unity). These values must be equivalent to the second + * term values in `compute_grand_product_numerator` + */ + { + const auto& transcript_pc = View(in.transcript_pc); + + auto transcript_Px = View(in.transcript_Px); + auto transcript_Py = View(in.transcript_Py); + auto z1 = View(in.transcript_z1); + auto z2 = View(in.transcript_z2); + auto z1_zero = View(in.transcript_z1zero); + auto z2_zero = View(in.transcript_z2zero); + auto transcript_mul = View(in.transcript_mul); + + auto lookup_first = (-z1_zero + 1); + auto lookup_second = (-z2_zero + 1); + FF endomorphism_base_field_shift = FF(bb::fq::cube_root_of_unity()); + + auto transcript_input1 = transcript_pc + transcript_Px * beta + transcript_Py * beta_sqr + z1 * beta_cube; + auto transcript_input2 = (transcript_pc - 1) + transcript_Px * endomorphism_base_field_shift * beta - + transcript_Py * beta_sqr + z2 * beta_cube; + + // | q_mul | z2_zero | z1_zero | lookup | + // | ----- | ------- | ------- | ---------------------- | + // | 0 | - | - | 1 | + // | 1 | 0 | 1 | X + gamma | + // | 1 | 1 | 0 | Y + gamma | + // | 1 | 1 | 1 | (X + gamma)(Y + gamma) | + transcript_input1 = (transcript_input1 + gamma) * lookup_first + (-lookup_first + 1); + transcript_input2 = (transcript_input2 + gamma) * lookup_second + (-lookup_second + 1); + // point_table_init_write = degree 2 + + auto point_table_init_write = transcript_mul * transcript_input1 * transcript_input2 + (-transcript_mul + 1); + denominator *= point_table_init_write; // degree-13 + + // auto point_table_init_write_1 = transcript_mul * transcript_input1 + (-transcript_mul + 1); + // denominator *= point_table_init_write_1; // degree-11 + + // auto point_table_init_write_2 = transcript_mul * transcript_input2 + (-transcript_mul + 1); + // denominator *= point_table_init_write_2; // degree-14 + } + /** + * @brief Third term: tuple of (point-counter, P.x, P.y, msm-size) from ECCVMTranscriptRelation. + * (P.x, P.y) is the *claimed* output of a multi-scalar-multiplication evaluated in ECCVMMSMRelation. + * We need to validate that the msm output produced in ECCVMMSMRelation is equivalent to the output present + * in `transcript_msm_output_x, transcript_msm_output_y`, for a given multi-scalar multiplication starting at + * `transcript_pc` and has size `transcript_msm_count` + */ + { + auto transcript_pc_shift = View(in.transcript_pc_shift); + auto transcript_msm_x = View(in.transcript_msm_x); + auto transcript_msm_y = View(in.transcript_msm_y); + auto transcript_msm_transition = View(in.transcript_msm_transition); + auto transcript_msm_count = View(in.transcript_msm_count); + auto z1_zero = View(in.transcript_z1zero); + auto z2_zero = View(in.transcript_z2zero); + auto transcript_mul = View(in.transcript_mul); + + auto full_msm_count = transcript_msm_count + transcript_mul * ((-z1_zero + 1) + (-z2_zero + 1)); + // auto count_test = transcript_msm_count + // msm_result_read = degree 2 + auto msm_result_read = + transcript_pc_shift + transcript_msm_x * beta + transcript_msm_y * beta_sqr + full_msm_count * beta_cube; + + msm_result_read = transcript_msm_transition * (msm_result_read + gamma) + (-transcript_msm_transition + 1); + denominator *= msm_result_read; // degree-17 + } + return denominator; +} + +/** + * @brief Expression for the StandardArithmetic gate. + * @dbetails The relation is defined as C(in(X)...) = + * (q_m * w_r * w_l) + (q_l * w_l) + (q_r * w_r) + (q_o * w_o) + q_c + * + * @param evals transformed to `evals + C(in(X)...)*scaling_factor` + * @param in an std::array containing the fully extended Accumulator edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ +template +template +void ECCVMSetRelationImpl::accumulate(ContainerOverSubrelations& accumulator, + const AllEntities& in, + const Parameters& params, + const FF& scaling_factor) +{ + using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; + using View = typename Accumulator::View; + + // degree-11 + Accumulator numerator_evaluation = compute_grand_product_numerator(in, params); + + // degree-17 + Accumulator denominator_evaluation = compute_grand_product_denominator(in, params); + + const auto& lagrange_first = View(in.lagrange_first); + const auto& lagrange_last = View(in.lagrange_last); + + const auto& z_perm = View(in.z_perm); + const auto& z_perm_shift = View(in.z_perm_shift); + + // degree-18 + std::get<0>(accumulator) += + ((z_perm + lagrange_first) * numerator_evaluation - (z_perm_shift + lagrange_last) * denominator_evaluation) * + scaling_factor; + + // Contribution (2) + std::get<1>(accumulator) += (lagrange_last * z_perm_shift) * scaling_factor; +} +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation.cpp index 4e7a4cdbdb6..79834907a99 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation.cpp @@ -1,261 +1,11 @@ #include #include -#include "./ecc_transcript_relation.hpp" +#include "./ecc_transcript_relation_impl.hpp" #include "barretenberg/eccvm/eccvm_flavor.hpp" #include "barretenberg/flavor/relation_definitions.hpp" namespace bb { - -/** - * @brief ECCVMTranscriptRelationImpl evaluates the correctness of the ECCVM transcript columns - * - * @details The transcript relations directly evaluate the correctness of `add, eq, reset` operations. - * `mul` operations are lazily evaluated. The output of multiscalar multiplications is present in - * `transcript_msm_x, transcript_msm_y` columns. A set equality check is used to validate these - * have been correctly read from a table produced by the relations in `ecc_msm_relation.hpp`. - * - * Sequential `mul` opcodes are interpreted as a multiscalar multiplication. - * The column `transcript_msm_count` tracks the number of muls in a given multiscalar multiplication. - * - * The column `transcript_pc` tracks a "point counter" value, that describes the number of multiplications - * that must be evaluated. - * - * One mul opcode can generate up to TWO multiplications. Each 128-bit scalar `z1, z2` is treated as an independent mul. - * The purpose of this is to reduce the length of the MSM algorithm evalauted in `ecc_msm_relation.hpp` to 128 bits - * (from 256 bits). Many scalar muls required to recursively verify a proof are only 128-bits in length; this prevents - * us doing redundant computation. - * @tparam FF - * @tparam AccumulatorTypes - * @tparam PolynomialTypes - */ -template -template -void ECCVMTranscriptRelationImpl::accumulate(ContainerOverSubrelations& accumulator, - const AllEntities& in, - const Parameters& /*unused*/, - const FF& scaling_factor) -{ - using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - - auto z1 = View(in.transcript_z1); - auto z2 = View(in.transcript_z2); - auto z1_zero = View(in.transcript_z1zero); - auto z2_zero = View(in.transcript_z2zero); - auto op = View(in.transcript_op); - auto q_add = View(in.transcript_add); - auto q_mul = View(in.transcript_mul); - auto q_mul_shift = View(in.transcript_mul_shift); - auto q_eq = View(in.transcript_eq); - auto msm_transition = View(in.transcript_msm_transition); - auto msm_count = View(in.transcript_msm_count); - auto msm_count_shift = View(in.transcript_msm_count_shift); - auto pc = View(in.transcript_pc); - auto pc_shift = View(in.transcript_pc_shift); - auto transcript_accumulator_x_shift = View(in.transcript_accumulator_x_shift); - auto transcript_accumulator_y_shift = View(in.transcript_accumulator_y_shift); - auto transcript_accumulator_x = View(in.transcript_accumulator_x); - auto transcript_accumulator_y = View(in.transcript_accumulator_y); - auto transcript_msm_x = View(in.transcript_msm_x); - auto transcript_msm_y = View(in.transcript_msm_y); - auto transcript_Px = View(in.transcript_Px); - auto transcript_Py = View(in.transcript_Py); - auto is_accumulator_empty = View(in.transcript_accumulator_empty); - auto lagrange_first = View(in.lagrange_first); - auto lagrange_last = View(in.lagrange_last); - auto is_accumulator_empty_shift = View(in.transcript_accumulator_empty_shift); - auto q_reset_accumulator = View(in.transcript_reset_accumulator); - auto lagrange_second = View(in.lagrange_second); - auto transcript_collision_check = View(in.transcript_collision_check); - - auto is_not_first_row = (-lagrange_first + 1); - auto is_not_first_or_last_row = (-lagrange_first + -lagrange_last + 1); - /** - * @brief Validate correctness of z1_zero, z2_zero. - * If z1_zero = 0 and operation is a MUL, we will write a scalar mul instruction into our multiplication table. - * If z1_zero = 1 and operation is a MUL, we will NOT write a scalar mul instruction. - * (same with z2_zero). - * z1_zero / z2_zero is user-defined. - * We constraint z1_zero such that if z1_zero == 1, we require z1 == 0. (same for z2_zero). - * We do *NOT* constrain z1 != 0 if z1_zero = 0. If the user sets z1_zero = 0 and z1 = 0, - * this will add a scalar mul instruction into the multiplication table, where the scalar multiplier is 0. - * This is inefficient but will still produce the correct output. - */ - std::get<0>(accumulator) += (z1 * z1_zero) * scaling_factor; // if z1_zero = 1, z1 must be 0 - std::get<1>(accumulator) += (z2 * z2_zero) * scaling_factor; - - /** - * @brief Validate `op` opcode is well formed. - * `op` is defined to be q_reset_accumulator + 2 * q_eq + 4 * q_mul + 8 * q_add, - * where q_reset_accumulator, q_eq, q_mul, q_add are all boolean - * (TODO: bool constrain these efficiently #2223) - */ - auto tmp = q_add + q_add; - tmp += q_mul; - tmp += tmp; - tmp += q_eq; - tmp += tmp; - tmp += q_reset_accumulator; - std::get<2>(accumulator) += (tmp - op) * scaling_factor; - - /** - * @brief Validate `pc` is updated correctly. - * pc stands for Point Counter. It decrements by 1 for every 128-bit multiplication operation. - * If q_mul = 1, pc decrements by !z1_zero + !z2_zero, else pc decrements by 0 - * @note pc starts out at its max value and decrements down to 0. This keeps the degree of the pc polynomial smol - */ - Accumulator pc_delta = pc - pc_shift; - std::get<3>(accumulator) += - is_not_first_row * (pc_delta - q_mul * ((-z1_zero + 1) + (-z2_zero + 1))) * scaling_factor; - - /** - * @brief Validate `msm_transition` is well-formed. - * - * If the current row is the last mul instruction in a multiscalar multiplication, msm_transition = 1. - * i.e. if q_mul == 1 and q_mul_shift == 0, msm_transition = 1, else is 0 - */ - auto msm_transition_check = q_mul * (-q_mul_shift + 1); - std::get<4>(accumulator) += (msm_transition - msm_transition_check) * scaling_factor; - - /** - * @brief Validate `msm_count` resets when we end a multiscalar multiplication. - * msm_count tracks the number of scalar muls in the current active multiscalar multiplication. - * (if no msm active, msm_count == 0) - * If current row ends an MSM, `msm_count_shift = 0` (msm_count value at next row) - */ - std::get<5>(accumulator) += (msm_transition * msm_count_shift) * scaling_factor; - - /** - * @brief Validate `msm_count` updates correctly for mul operations. - * msm_count updates by (!z1_zero + !z2_zero) if current op is a mul instruction (and msm is not terminating at next - * row). - */ - auto msm_count_delta = msm_count_shift - msm_count; // degree 4 - std::get<6>(accumulator) += is_not_first_row * (-msm_transition + 1) * - (msm_count_delta - q_mul * ((-z1_zero + 1) + (-z2_zero + 1))) * scaling_factor; - - /** - * @brief Add multiscalar multiplication result into transcript accumulator. - * If `msm_transition == 1`, we expect msm output to be present on (transcript_msm_x, transcript_msm_y). - * (this is enforced via a lookup protocol). - * If `is_accumulator_empty == 0`, we ADD msm output into transcript_accumulator. - * If `is_accumulator_empty = =1`, we ASSIGN msm output to transcript_accumulator. - * @note the output of an msm cannot be point at infinity (will create unsatisfiable constraints in - * ecc_msm_relation). We assume this does not affect statistical completeness for honest provers. We should validate - * this! - */ - auto add_msm_into_accumulator = msm_transition * (-is_accumulator_empty + 1); - auto x3 = transcript_accumulator_x_shift; - auto y3 = transcript_accumulator_y_shift; - auto x1 = transcript_accumulator_x; - auto y1 = transcript_accumulator_y; - auto x2 = transcript_msm_x; - auto y2 = transcript_msm_y; - auto tmpx = (x3 + x2 + x1) * (x2 - x1) * (x2 - x1) - (y2 - y1) * (y2 - y1); - auto tmpy = (y3 + y1) * (x2 - x1) - (y2 - y1) * (x1 - x3); - std::get<7>(accumulator) += tmpx * add_msm_into_accumulator * scaling_factor; // degree 5 - std::get<8>(accumulator) += tmpy * add_msm_into_accumulator * scaling_factor; // degree 4 - - /** - * @brief If is_accumulator_empty == 1, assign transcript_accumulator output into accumulator - * - * @note The accumulator point for all operations at row `i` is the accumulator point at row `i + 1`! - */ - auto assign_msm_into_accumulator = msm_transition * is_accumulator_empty; - std::get<9>(accumulator) += assign_msm_into_accumulator * (x3 - x2) * scaling_factor; // degree 3 - std::get<10>(accumulator) += assign_msm_into_accumulator * (y3 - y2) * scaling_factor; - - /** - * @brief Constrain `add` opcode. - * - * add will add the input point in (transcript_Px, transcript_Py) into the accumulator. - * Correctly handles case where accumulator is point at infinity. - * TODO: need to add constraints to rule out point doubling case (x2 != x1) - * TODO: need to assert input point is on the curve! - */ - x2 = transcript_Px; - y2 = transcript_Py; - auto add_into_accumulator = q_add * (-is_accumulator_empty + 1); - tmpx = (x3 + x2 + x1) * (x2 - x1) * (x2 - x1) - (y2 - y1) * (y2 - y1); - tmpy = (y3 + y1) * (x2 - x1) - (y2 - y1) * (x1 - x3); - std::get<11>(accumulator) += tmpx * add_into_accumulator * scaling_factor; // degree 5 - std::get<12>(accumulator) += tmpy * add_into_accumulator * scaling_factor; // degree 4 - auto assign_into_accumulator = q_add * is_accumulator_empty; - std::get<13>(accumulator) += (x3 - x2) * assign_into_accumulator * scaling_factor; // degree 3 - std::get<14>(accumulator) += (y3 - y2) * assign_into_accumulator * scaling_factor; - - /** - * @brief Opcode exclusion tests. We have the following assertions: - * 1. If q_mul = 1, (q_add, eq, reset) are zero - * 2. If q_reset = 1, is_accumulator_empty at next row = 1 - * 3. If q_add = 1 OR msm_transition = 1, is_accumulator_empty at next row = 0 - * 4. If q_add = 0 AND msm_transition = 0 AND q_reset_accumulator = 0, is_accumulator at next row = current row - * value - * @note point 3: both q_add = 1, msm_transition = 1 cannot occur because of point 1 (msm_transition only 1 when - * q_mul 1) we can use a slightly more efficient relation than a pure binary OR - */ - std::get<15>(accumulator) += q_mul * (q_add + q_eq + q_reset_accumulator) * scaling_factor; - std::get<16>(accumulator) += q_add * (q_mul + q_eq + q_reset_accumulator) * scaling_factor; - std::get<17>(accumulator) += q_reset_accumulator * (-is_accumulator_empty_shift + 1) * scaling_factor; - std::get<18>(accumulator) += (q_add + msm_transition) * is_accumulator_empty_shift * scaling_factor; - auto accumulator_state_not_modified = -(q_add + msm_transition + q_reset_accumulator) + 1; - std::get<19>(accumulator) += accumulator_state_not_modified * is_not_first_or_last_row * - (is_accumulator_empty_shift - is_accumulator_empty) * scaling_factor; - - /** - * @brief `eq` opcode. - * If eq = 1, assert transcript_Px/y = transcript_accumulator_x/y. - * If eq = 1, assert is_accumulator_empty = 0 (input point cannot be point at infinity) - */ - std::get<20>(accumulator) += q_eq * (transcript_accumulator_x - transcript_Px) * scaling_factor; - std::get<21>(accumulator) += - q_eq * (-is_accumulator_empty + 1) * (transcript_accumulator_y - transcript_Py) * scaling_factor; - std::get<22>(accumulator) += q_eq * is_accumulator_empty * scaling_factor; - - // validate selectors are boolean (put somewhere else? these are low degree) - std::get<23>(accumulator) += q_eq * (q_eq - 1) * scaling_factor; - std::get<24>(accumulator) += q_add * (q_add - 1) * scaling_factor; - std::get<25>(accumulator) += q_mul * (q_mul - 1) * scaling_factor; - std::get<26>(accumulator) += q_reset_accumulator * (q_reset_accumulator - 1) * scaling_factor; - std::get<27>(accumulator) += msm_transition * (msm_transition - 1) * scaling_factor; - std::get<28>(accumulator) += is_accumulator_empty * (is_accumulator_empty - 1) * scaling_factor; - std::get<29>(accumulator) += z1_zero * (z1_zero - 1) * scaling_factor; - std::get<30>(accumulator) += z2_zero * (z2_zero - 1) * scaling_factor; - - /** - * @brief Initial condition check on 1st row. - * We require the following values are 0 on 1st row: - * is_accumulator_empty = 1 - * msm_count = 0 - * note...actually second row? bleurgh - * NOTE: we want pc = 0 at lagrange_last :o - */ - std::get<31>(accumulator) += lagrange_second * (-is_accumulator_empty + 1) * scaling_factor; - std::get<32>(accumulator) += lagrange_second * msm_count * scaling_factor; - - /** - * @brief On-curve validation checks. - * If q_mul = 1 OR q_add = 1 OR q_eq = 1, require (transcript_Px, transcript_Py) is valid ecc point - * q_mul/q_add/q_eq mutually exclusive, can represent as sum of 3 - */ - const auto validate_on_curve = q_mul; // q_add + q_mul + q_eq; - const auto on_curve_check = - transcript_Py * transcript_Py - transcript_Px * transcript_Px * transcript_Px - get_curve_b(); - std::get<33>(accumulator) += validate_on_curve * on_curve_check * scaling_factor; - - /** - * @brief If performing an add, validate x-coordintes of inputs do not collide. - * If adding msm output into accumulator, validate x-coordinates of inputs do not collide - */ - auto x_coordinate_collision_check = - add_msm_into_accumulator * ((transcript_msm_x - transcript_accumulator_x) * transcript_collision_check - FF(1)); - x_coordinate_collision_check += - add_into_accumulator * ((transcript_Px - transcript_accumulator_x) * transcript_collision_check - FF(1)); - std::get<34>(accumulator) += x_coordinate_collision_check * scaling_factor; -} - template class ECCVMTranscriptRelationImpl; DEFINE_SUMCHECK_RELATION_CLASS(ECCVMTranscriptRelationImpl, ECCVMFlavor); - } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation_impl.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation_impl.hpp new file mode 100644 index 00000000000..037f79a6390 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_transcript_relation_impl.hpp @@ -0,0 +1,256 @@ +#pragma once +#include +#include + +#include "./ecc_transcript_relation.hpp" + +namespace bb { + +/** + * @brief ECCVMTranscriptRelationImpl evaluates the correctness of the ECCVM transcript columns + * + * @details The transcript relations directly evaluate the correctness of `add, eq, reset` operations. + * `mul` operations are lazily evaluated. The output of multiscalar multiplications is present in + * `transcript_msm_x, transcript_msm_y` columns. A set equality check is used to validate these + * have been correctly read from a table produced by the relations in `ecc_msm_relation.hpp`. + * + * Sequential `mul` opcodes are interpreted as a multiscalar multiplication. + * The column `transcript_msm_count` tracks the number of muls in a given multiscalar multiplication. + * + * The column `transcript_pc` tracks a "point counter" value, that describes the number of multiplications + * that must be evaluated. + * + * One mul opcode can generate up to TWO multiplications. Each 128-bit scalar `z1, z2` is treated as an independent mul. + * The purpose of this is to reduce the length of the MSM algorithm evalauted in `ecc_msm_relation.hpp` to 128 bits + * (from 256 bits). Many scalar muls required to recursively verify a proof are only 128-bits in length; this prevents + * us doing redundant computation. + * @tparam FF + * @tparam AccumulatorTypes + * @tparam PolynomialTypes + */ +template +template +void ECCVMTranscriptRelationImpl::accumulate(ContainerOverSubrelations& accumulator, + const AllEntities& in, + const Parameters& /*unused*/, + const FF& scaling_factor) +{ + using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; + using View = typename Accumulator::View; + + auto z1 = View(in.transcript_z1); + auto z2 = View(in.transcript_z2); + auto z1_zero = View(in.transcript_z1zero); + auto z2_zero = View(in.transcript_z2zero); + auto op = View(in.transcript_op); + auto q_add = View(in.transcript_add); + auto q_mul = View(in.transcript_mul); + auto q_mul_shift = View(in.transcript_mul_shift); + auto q_eq = View(in.transcript_eq); + auto msm_transition = View(in.transcript_msm_transition); + auto msm_count = View(in.transcript_msm_count); + auto msm_count_shift = View(in.transcript_msm_count_shift); + auto pc = View(in.transcript_pc); + auto pc_shift = View(in.transcript_pc_shift); + auto transcript_accumulator_x_shift = View(in.transcript_accumulator_x_shift); + auto transcript_accumulator_y_shift = View(in.transcript_accumulator_y_shift); + auto transcript_accumulator_x = View(in.transcript_accumulator_x); + auto transcript_accumulator_y = View(in.transcript_accumulator_y); + auto transcript_msm_x = View(in.transcript_msm_x); + auto transcript_msm_y = View(in.transcript_msm_y); + auto transcript_Px = View(in.transcript_Px); + auto transcript_Py = View(in.transcript_Py); + auto is_accumulator_empty = View(in.transcript_accumulator_empty); + auto lagrange_first = View(in.lagrange_first); + auto lagrange_last = View(in.lagrange_last); + auto is_accumulator_empty_shift = View(in.transcript_accumulator_empty_shift); + auto q_reset_accumulator = View(in.transcript_reset_accumulator); + auto lagrange_second = View(in.lagrange_second); + auto transcript_collision_check = View(in.transcript_collision_check); + + auto is_not_first_row = (-lagrange_first + 1); + auto is_not_first_or_last_row = (-lagrange_first + -lagrange_last + 1); + /** + * @brief Validate correctness of z1_zero, z2_zero. + * If z1_zero = 0 and operation is a MUL, we will write a scalar mul instruction into our multiplication table. + * If z1_zero = 1 and operation is a MUL, we will NOT write a scalar mul instruction. + * (same with z2_zero). + * z1_zero / z2_zero is user-defined. + * We constraint z1_zero such that if z1_zero == 1, we require z1 == 0. (same for z2_zero). + * We do *NOT* constrain z1 != 0 if z1_zero = 0. If the user sets z1_zero = 0 and z1 = 0, + * this will add a scalar mul instruction into the multiplication table, where the scalar multiplier is 0. + * This is inefficient but will still produce the correct output. + */ + std::get<0>(accumulator) += (z1 * z1_zero) * scaling_factor; // if z1_zero = 1, z1 must be 0 + std::get<1>(accumulator) += (z2 * z2_zero) * scaling_factor; + + /** + * @brief Validate `op` opcode is well formed. + * `op` is defined to be q_reset_accumulator + 2 * q_eq + 4 * q_mul + 8 * q_add, + * where q_reset_accumulator, q_eq, q_mul, q_add are all boolean + * (TODO: bool constrain these efficiently #2223) + */ + auto tmp = q_add + q_add; + tmp += q_mul; + tmp += tmp; + tmp += q_eq; + tmp += tmp; + tmp += q_reset_accumulator; + std::get<2>(accumulator) += (tmp - op) * scaling_factor; + + /** + * @brief Validate `pc` is updated correctly. + * pc stands for Point Counter. It decrements by 1 for every 128-bit multiplication operation. + * If q_mul = 1, pc decrements by !z1_zero + !z2_zero, else pc decrements by 0 + * @note pc starts out at its max value and decrements down to 0. This keeps the degree of the pc polynomial smol + */ + Accumulator pc_delta = pc - pc_shift; + std::get<3>(accumulator) += + is_not_first_row * (pc_delta - q_mul * ((-z1_zero + 1) + (-z2_zero + 1))) * scaling_factor; + + /** + * @brief Validate `msm_transition` is well-formed. + * + * If the current row is the last mul instruction in a multiscalar multiplication, msm_transition = 1. + * i.e. if q_mul == 1 and q_mul_shift == 0, msm_transition = 1, else is 0 + */ + auto msm_transition_check = q_mul * (-q_mul_shift + 1); + std::get<4>(accumulator) += (msm_transition - msm_transition_check) * scaling_factor; + + /** + * @brief Validate `msm_count` resets when we end a multiscalar multiplication. + * msm_count tracks the number of scalar muls in the current active multiscalar multiplication. + * (if no msm active, msm_count == 0) + * If current row ends an MSM, `msm_count_shift = 0` (msm_count value at next row) + */ + std::get<5>(accumulator) += (msm_transition * msm_count_shift) * scaling_factor; + + /** + * @brief Validate `msm_count` updates correctly for mul operations. + * msm_count updates by (!z1_zero + !z2_zero) if current op is a mul instruction (and msm is not terminating at next + * row). + */ + auto msm_count_delta = msm_count_shift - msm_count; // degree 4 + std::get<6>(accumulator) += is_not_first_row * (-msm_transition + 1) * + (msm_count_delta - q_mul * ((-z1_zero + 1) + (-z2_zero + 1))) * scaling_factor; + + /** + * @brief Add multiscalar multiplication result into transcript accumulator. + * If `msm_transition == 1`, we expect msm output to be present on (transcript_msm_x, transcript_msm_y). + * (this is enforced via a lookup protocol). + * If `is_accumulator_empty == 0`, we ADD msm output into transcript_accumulator. + * If `is_accumulator_empty = =1`, we ASSIGN msm output to transcript_accumulator. + * @note the output of an msm cannot be point at infinity (will create unsatisfiable constraints in + * ecc_msm_relation). We assume this does not affect statistical completeness for honest provers. We should validate + * this! + */ + auto add_msm_into_accumulator = msm_transition * (-is_accumulator_empty + 1); + auto x3 = transcript_accumulator_x_shift; + auto y3 = transcript_accumulator_y_shift; + auto x1 = transcript_accumulator_x; + auto y1 = transcript_accumulator_y; + auto x2 = transcript_msm_x; + auto y2 = transcript_msm_y; + auto tmpx = (x3 + x2 + x1) * (x2 - x1) * (x2 - x1) - (y2 - y1) * (y2 - y1); + auto tmpy = (y3 + y1) * (x2 - x1) - (y2 - y1) * (x1 - x3); + std::get<7>(accumulator) += tmpx * add_msm_into_accumulator * scaling_factor; // degree 5 + std::get<8>(accumulator) += tmpy * add_msm_into_accumulator * scaling_factor; // degree 4 + + /** + * @brief If is_accumulator_empty == 1, assign transcript_accumulator output into accumulator + * + * @note The accumulator point for all operations at row `i` is the accumulator point at row `i + 1`! + */ + auto assign_msm_into_accumulator = msm_transition * is_accumulator_empty; + std::get<9>(accumulator) += assign_msm_into_accumulator * (x3 - x2) * scaling_factor; // degree 3 + std::get<10>(accumulator) += assign_msm_into_accumulator * (y3 - y2) * scaling_factor; + + /** + * @brief Constrain `add` opcode. + * + * add will add the input point in (transcript_Px, transcript_Py) into the accumulator. + * Correctly handles case where accumulator is point at infinity. + * TODO: need to add constraints to rule out point doubling case (x2 != x1) + * TODO: need to assert input point is on the curve! + */ + x2 = transcript_Px; + y2 = transcript_Py; + auto add_into_accumulator = q_add * (-is_accumulator_empty + 1); + tmpx = (x3 + x2 + x1) * (x2 - x1) * (x2 - x1) - (y2 - y1) * (y2 - y1); + tmpy = (y3 + y1) * (x2 - x1) - (y2 - y1) * (x1 - x3); + std::get<11>(accumulator) += tmpx * add_into_accumulator * scaling_factor; // degree 5 + std::get<12>(accumulator) += tmpy * add_into_accumulator * scaling_factor; // degree 4 + auto assign_into_accumulator = q_add * is_accumulator_empty; + std::get<13>(accumulator) += (x3 - x2) * assign_into_accumulator * scaling_factor; // degree 3 + std::get<14>(accumulator) += (y3 - y2) * assign_into_accumulator * scaling_factor; + + /** + * @brief Opcode exclusion tests. We have the following assertions: + * 1. If q_mul = 1, (q_add, eq, reset) are zero + * 2. If q_reset = 1, is_accumulator_empty at next row = 1 + * 3. If q_add = 1 OR msm_transition = 1, is_accumulator_empty at next row = 0 + * 4. If q_add = 0 AND msm_transition = 0 AND q_reset_accumulator = 0, is_accumulator at next row = current row + * value + * @note point 3: both q_add = 1, msm_transition = 1 cannot occur because of point 1 (msm_transition only 1 when + * q_mul 1) we can use a slightly more efficient relation than a pure binary OR + */ + std::get<15>(accumulator) += q_mul * (q_add + q_eq + q_reset_accumulator) * scaling_factor; + std::get<16>(accumulator) += q_add * (q_mul + q_eq + q_reset_accumulator) * scaling_factor; + std::get<17>(accumulator) += q_reset_accumulator * (-is_accumulator_empty_shift + 1) * scaling_factor; + std::get<18>(accumulator) += (q_add + msm_transition) * is_accumulator_empty_shift * scaling_factor; + auto accumulator_state_not_modified = -(q_add + msm_transition + q_reset_accumulator) + 1; + std::get<19>(accumulator) += accumulator_state_not_modified * is_not_first_or_last_row * + (is_accumulator_empty_shift - is_accumulator_empty) * scaling_factor; + + /** + * @brief `eq` opcode. + * If eq = 1, assert transcript_Px/y = transcript_accumulator_x/y. + * If eq = 1, assert is_accumulator_empty = 0 (input point cannot be point at infinity) + */ + std::get<20>(accumulator) += q_eq * (transcript_accumulator_x - transcript_Px) * scaling_factor; + std::get<21>(accumulator) += + q_eq * (-is_accumulator_empty + 1) * (transcript_accumulator_y - transcript_Py) * scaling_factor; + std::get<22>(accumulator) += q_eq * is_accumulator_empty * scaling_factor; + + // validate selectors are boolean (put somewhere else? these are low degree) + std::get<23>(accumulator) += q_eq * (q_eq - 1) * scaling_factor; + std::get<24>(accumulator) += q_add * (q_add - 1) * scaling_factor; + std::get<25>(accumulator) += q_mul * (q_mul - 1) * scaling_factor; + std::get<26>(accumulator) += q_reset_accumulator * (q_reset_accumulator - 1) * scaling_factor; + std::get<27>(accumulator) += msm_transition * (msm_transition - 1) * scaling_factor; + std::get<28>(accumulator) += is_accumulator_empty * (is_accumulator_empty - 1) * scaling_factor; + std::get<29>(accumulator) += z1_zero * (z1_zero - 1) * scaling_factor; + std::get<30>(accumulator) += z2_zero * (z2_zero - 1) * scaling_factor; + + /** + * @brief Initial condition check on 1st row. + * We require the following values are 0 on 1st row: + * is_accumulator_empty = 1 + * msm_count = 0 + * note...actually second row? bleurgh + * NOTE: we want pc = 0 at lagrange_last :o + */ + std::get<31>(accumulator) += lagrange_second * (-is_accumulator_empty + 1) * scaling_factor; + std::get<32>(accumulator) += lagrange_second * msm_count * scaling_factor; + + /** + * @brief On-curve validation checks. + * If q_mul = 1 OR q_add = 1 OR q_eq = 1, require (transcript_Px, transcript_Py) is valid ecc point + * q_mul/q_add/q_eq mutually exclusive, can represent as sum of 3 + */ + const auto validate_on_curve = q_mul; // q_add + q_mul + q_eq; + const auto on_curve_check = + transcript_Py * transcript_Py - transcript_Px * transcript_Px * transcript_Px - get_curve_b(); + std::get<33>(accumulator) += validate_on_curve * on_curve_check * scaling_factor; + + /** + * @brief If performing an add, validate x-coordintes of inputs do not collide. + * If adding msm output into accumulator, validate x-coordinates of inputs do not collide + */ + auto x_coordinate_collision_check = + add_msm_into_accumulator * ((transcript_msm_x - transcript_accumulator_x) * transcript_collision_check - FF(1)); + x_coordinate_collision_check += + add_into_accumulator * ((transcript_Px - transcript_accumulator_x) * transcript_collision_check - FF(1)); + std::get<34>(accumulator) += x_coordinate_collision_check * scaling_factor; +} +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation.cpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation.cpp index 3c7e7ca8433..2ab7a00a381 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation.cpp @@ -1,221 +1,9 @@ -#include "ecc_wnaf_relation.hpp" #include "barretenberg/eccvm/eccvm_flavor.hpp" #include "barretenberg/flavor/relation_definitions.hpp" +#include "ecc_wnaf_relation_impl.hpp" namespace bb { -/** - * @brief ECCVMWnafRelationImpl evaluates relations that convert scalar multipliers into 4-bit WNAF slices - * @details Each WNAF slice is a 4-bit slice representing one of 16 integers { -15, -13, ..., 15 } - * Each WNAF slice is represented via two 2-bit columns (precompute_s1hi, ..., precompute_s4lo) - * One 128-bit scalar multiplier is processed across 8 rows, indexed by a round variable. - * The following table describes the structure for one scalar. - * - * | point_transition | round | slices | skew | scalar_sum | - * | ---------------- | ----- | --------------- | ------ | ------------------------------- | - * | 0 | 0 | s0,s1,s2,s3 | 0 | 0 | - * | 0 | 1 | s4,s5,s6,s7 | 0 | \sum_{i=0}^4 16^i * s_{31 - i} | - * | 0 | 2 | s8,s9,s10,s11 | 0 | \sum_{i=0}^8 16^i * s_{31 - i} | - * | 0 | 3 | s12,s13,s14,s14 | 0 | \sum_{i=0}^12 16^i * s_{31 - i} | - * | 0 | 4 | s16,s17,s18,s19 | 0 | \sum_{i=0}^16 16^i * s_{31 - i} | - * | 0 | 5 | s20,s21,s22,s23 | 0 | \sum_{i=0}^20 16^i * s_{31 - i} | - * | 0 | 6 | s24,s25,s26,s27 | 0 | \sum_{i=0}^24 16^i * s_{31 - i} | - * | 1 | 7 | s28,s29,s30,s31 | s_skew | \sum_{i=0}^28 16^i * s_{31 - i} | - * - * The value of the input scalar is equal to the following: - * - * scalar = 2^16 * scalar_sum + 2^12 * s31 + 2^8 * s30 + 2^4 * s29 + s28 - s_skew - * We use a set equality check in `ecc_set_relation.hpp` to validate the above value maps to the correct input - * scalar for a given value of `pc`. - * - * The column `point_transition` is committed to by the Prover, we must constrain it is correctly computed (see - * `ecc_point_table_relation.cpp` for details) - * - * @tparam FF - * @tparam AccumulatorTypes - */ -template -template -void ECCVMWnafRelationImpl::accumulate(ContainerOverSubrelations& accumulator, - const AllEntities& in, - const Parameters& /*unused*/, - const FF& scaling_factor) -{ - using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - - auto scalar_sum = View(in.precompute_scalar_sum); - auto scalar_sum_new = View(in.precompute_scalar_sum_shift); - auto q_transition = View(in.precompute_point_transition); - auto round = View(in.precompute_round); - auto round_shift = View(in.precompute_round_shift); - auto pc = View(in.precompute_pc); - auto pc_shift = View(in.precompute_pc_shift); - // precompute_select is a boolean column. We only evaluate the ecc_wnaf_relation and the ecc_point_table_relation if - // `precompute_select=1` - auto precompute_select = View(in.precompute_select); - auto precompute_select_shift = View(in.precompute_select_shift); - - const auto& precompute_skew = View(in.precompute_skew); - - const std::array slices{ - View(in.precompute_s1hi), View(in.precompute_s1lo), View(in.precompute_s2hi), View(in.precompute_s2lo), - View(in.precompute_s3hi), View(in.precompute_s3lo), View(in.precompute_s4hi), View(in.precompute_s4lo), - }; - - const auto range_constraint_slice_to_2_bits = [&scaling_factor](const View& s, auto& acc) { - acc += s * (s - 1) * (s - 2) * (s - 3) * scaling_factor; - }; - - const auto convert_to_wnaf = [](const View& s0, const View& s1) { - auto t = s0 + s0; - t += t; - t += s1; - auto naf = t + t - 15; - return naf; - }; - - const auto scaled_transition = q_transition * scaling_factor; - const auto scaled_transition_is_zero = -scaled_transition + scaling_factor; - /** - * @brief Constrain each of our scalar slice chunks (s1, ..., s8) to be 2 bits. - * Doing range checks this way vs permutation-based range check removes need to create sorted list + grand product - * polynomial. Probably cheaper even if we have to split each 4-bit WNAF slice into 2-bit chunks. - */ - range_constraint_slice_to_2_bits(slices[0], std::get<0>(accumulator)); - range_constraint_slice_to_2_bits(slices[1], std::get<1>(accumulator)); - range_constraint_slice_to_2_bits(slices[2], std::get<2>(accumulator)); - range_constraint_slice_to_2_bits(slices[3], std::get<3>(accumulator)); - range_constraint_slice_to_2_bits(slices[4], std::get<4>(accumulator)); - range_constraint_slice_to_2_bits(slices[5], std::get<5>(accumulator)); - range_constraint_slice_to_2_bits(slices[6], std::get<6>(accumulator)); - range_constraint_slice_to_2_bits(slices[7], std::get<7>(accumulator)); - - /** - * @brief If we are processing a new scalar (q_transition = 1), validate that the first slice is positive. - * This requires us to validate slice1 is in the range [8, ... 15]. - * (when converted into wnaf form this maps to the range [1, 3, ..., 15]). - * We do this to ensure the final scalar sum is positive. - * We already know slice1 is in the range [0, ..., 15] - * To check the range [8, ..., 15] we validate the most significant 2 bits (s1) are >=2 - */ - const auto s1_shift = View(in.precompute_s1hi_shift); - const auto s1_shift_msb_set = (s1_shift - 2) * (s1_shift - 3); - std::get<20>(accumulator) += scaled_transition * precompute_select_shift * s1_shift_msb_set; - - /** - * @brief Convert each pair of 2-bit scalar slices into a 4-bit windowed-non-adjacent-form slice. - * Conversion from binary -> wnaf = 2 * binary - 15. - * Converts a value in [0, ..., 15] into [-15, -13, -11, -9, -7, -5, -3, -1, 1, 3, 5, 7, 9, 11 , 13, 15]. - * We use WNAF representation to avoid case where we are conditionally adding a point in our MSM algo. - */ - const auto w0 = convert_to_wnaf(slices[0], slices[1]); - const auto w1 = convert_to_wnaf(slices[2], slices[3]); - const auto w2 = convert_to_wnaf(slices[4], slices[5]); - const auto w3 = convert_to_wnaf(slices[6], slices[7]); - - /** - * @brief Slice consistency check. - * We require that `scalar_sum` on the next row correctly accumulates the 4 WNAF slices present on the current row - * (i.e. 16 WNAF bits). - * i.e. next_scalar_sum - 2^{16} * current_scalar_sum - 2^12 * w_0 - 2^8 * w_1 - 2^4 * w_2 - w_3 = 0 - * @note We only perform slice_consistency check when next row is processing the same scalar as the current row! - * i.e. when q_transition = 0 - * TODO(@zac-williamson) Optimize WNAF use (#2224) - */ - auto row_slice = w0; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += w1; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += w2; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += row_slice; - row_slice += w3; - auto sum_delta = scalar_sum * FF(1ULL << 16) + row_slice; - const auto check_sum = scalar_sum_new - sum_delta; - std::get<8>(accumulator) += precompute_select * check_sum * scaled_transition_is_zero; - - /** - * @brief Round transition logic. - * Goal: `round` is an integer in [0, ... 7] that tracks how many slices we have processed for a given scalar. - * i.e. number of 4-bit WNAF slices processed = round * 4. - * We apply the following constraints: - * If q_transition = 0, round increments by 1 between rows. - * If q_transition = 1, round value at current row = 7 - * If q_transition = 1, round value at next row = 0 - * Question: is this sufficient? We don't actually range constrain `round` (expensive if we don't need to!). - * Let us analyze... - * 1. When `q_transition = 1`, we use a set membership check to map the tuple of (pc, scalar_sum) into a set. - * We compare this set with an equivalent set generated from the transcript columns. The sets must match. - * 2. Only case where, at row `i`, a Prover can set `round` to value > 7 is if `q_transition = 0` for all j > i. - * `precompute_pc` decrements by 1 when `q_transition` = 1 - * We can infer from 1, 2, that if `round > 7`, the resulting wnafs will map into a set at a value of `pc` that is - * greater than all valid msm pc values (assuming the set equivalence check on the scalar sums is satisfied). - * The resulting msm output of such a computation cannot be mapped into the set of msm outputs in - * the transcript columns (see relations in ecc_msm_relation.cpp). - * Conclusion: not applying a strict range-check on `round` does not affect soundness (TODO(@zac-williamson) - * validate this! #2225) - */ - // We combine checks 0, 1 into a single relation - // q_transition * (round - 7) + (-q_transition + 1) * (round_shift - round - 1) - // => q_transition * (round - 7 - round_shift + round + 1) + (round_shift - round - 1) - // => q_transition * (2 * round - round_shift - 6) + (round_shift - round - 1) - const auto round_check = round_shift - round - 1; - std::get<9>(accumulator) += precompute_select * scaled_transition * ((round - round_check - 7) + round_check); - std::get<10>(accumulator) += precompute_select * scaled_transition * round_shift; - - /** - * @brief Scalar transition checks. - * 1: if q_transition = 1, scalar_sum_new = 0 - * 2: if q_transition = 0, pc at next row = pc at current row - * 3: if q_transition = 1, pc at next row = pc at current row - 1 (decrements by 1) - * (we combine 2 and 3 into a single relation) - */ - std::get<11>(accumulator) += precompute_select * scalar_sum_new * scaled_transition; - // (2, 3 combined): q_transition * (pc - pc_shift - 1) + (-q_transition + 1) * (pc_shift - pc) - // => q_transition * (-2 * (pc_shift - pc) - 1) + (pc_shift - pc) - const auto pc_delta = pc_shift - pc; - std::get<12>(accumulator) += - precompute_select * (scaled_transition * ((-pc_delta - pc_delta - 1)) + pc_delta * scaling_factor); - - /** - * @brief Validate skew is 0 or 7 - * 7 is the wnaf representation of -1. - * We have one skew variable per scalar multiplier. We can only represent odd integers in WNAF form. - * If input scalar is even, we must subtract 1 from WNAF scalar sum to get actual value (i.e. where skew = 7) - * We use skew in two places. - * 1: when validating sum of wnaf slices matches input scalar (we add skew to scalar_sum in ecc_set_relation) - * 2: in ecc_msm_relation. Final MSM round uses skew to conditionally subtract a point from the accumulator - */ - std::get<13>(accumulator) += precompute_select * (precompute_skew * (precompute_skew - 7)) * scaling_factor; - - const auto precompute_select_zero = (-precompute_select + 1) * scaling_factor; - std::get<14>(accumulator) += precompute_select_zero * (w0 + 15); - std::get<15>(accumulator) += precompute_select_zero * (w1 + 15); - std::get<16>(accumulator) += precompute_select_zero * (w2 + 15); - std::get<17>(accumulator) += precompute_select_zero * (w3 + 15); - - std::get<18>(accumulator) += precompute_select_zero * round; - std::get<19>(accumulator) += precompute_select_zero * pc; - - // TODO(@zac-williamson #2226) - // if precompute_select = 0, validate pc, round, slice values are all zero - // If we do this we can reduce the degree of the set equivalence relations - // (currently when checking pc/round/wnaf tuples from WNAF columns match those from MSM columns, - // we conditionally include tuples depending on if precompute_select = 1 (for WNAF columns) or if q_add1/2/3/4 = 1 - // (for MSM columns). - // If we KNOW that the wnaf tuple values are 0 when precompute_select = 0, we can remove the conditional checks in - // the set equivalence relation -} - template class ECCVMWnafRelationImpl; DEFINE_SUMCHECK_RELATION_CLASS(ECCVMWnafRelationImpl, ECCVMFlavor); diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation_impl.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation_impl.hpp new file mode 100644 index 00000000000..3af5063c1a5 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_wnaf_relation_impl.hpp @@ -0,0 +1,216 @@ +#include "ecc_wnaf_relation.hpp" + +namespace bb { + +/** + * @brief ECCVMWnafRelationImpl evaluates relations that convert scalar multipliers into 4-bit WNAF slices + * @details Each WNAF slice is a 4-bit slice representing one of 16 integers { -15, -13, ..., 15 } + * Each WNAF slice is represented via two 2-bit columns (precompute_s1hi, ..., precompute_s4lo) + * One 128-bit scalar multiplier is processed across 8 rows, indexed by a round variable. + * The following table describes the structure for one scalar. + * + * | point_transition | round | slices | skew | scalar_sum | + * | ---------------- | ----- | --------------- | ------ | ------------------------------- | + * | 0 | 0 | s0,s1,s2,s3 | 0 | 0 | + * | 0 | 1 | s4,s5,s6,s7 | 0 | \sum_{i=0}^4 16^i * s_{31 - i} | + * | 0 | 2 | s8,s9,s10,s11 | 0 | \sum_{i=0}^8 16^i * s_{31 - i} | + * | 0 | 3 | s12,s13,s14,s14 | 0 | \sum_{i=0}^12 16^i * s_{31 - i} | + * | 0 | 4 | s16,s17,s18,s19 | 0 | \sum_{i=0}^16 16^i * s_{31 - i} | + * | 0 | 5 | s20,s21,s22,s23 | 0 | \sum_{i=0}^20 16^i * s_{31 - i} | + * | 0 | 6 | s24,s25,s26,s27 | 0 | \sum_{i=0}^24 16^i * s_{31 - i} | + * | 1 | 7 | s28,s29,s30,s31 | s_skew | \sum_{i=0}^28 16^i * s_{31 - i} | + * + * The value of the input scalar is equal to the following: + * + * scalar = 2^16 * scalar_sum + 2^12 * s31 + 2^8 * s30 + 2^4 * s29 + s28 - s_skew + * We use a set equality check in `ecc_set_relation.hpp` to validate the above value maps to the correct input + * scalar for a given value of `pc`. + * + * The column `point_transition` is committed to by the Prover, we must constrain it is correctly computed (see + * `ecc_point_table_relation.cpp` for details) + * + * @tparam FF + * @tparam AccumulatorTypes + */ +template +template +void ECCVMWnafRelationImpl::accumulate(ContainerOverSubrelations& accumulator, + const AllEntities& in, + const Parameters& /*unused*/, + const FF& scaling_factor) +{ + using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; + using View = typename Accumulator::View; + + auto scalar_sum = View(in.precompute_scalar_sum); + auto scalar_sum_new = View(in.precompute_scalar_sum_shift); + auto q_transition = View(in.precompute_point_transition); + auto round = View(in.precompute_round); + auto round_shift = View(in.precompute_round_shift); + auto pc = View(in.precompute_pc); + auto pc_shift = View(in.precompute_pc_shift); + // precompute_select is a boolean column. We only evaluate the ecc_wnaf_relation and the ecc_point_table_relation if + // `precompute_select=1` + auto precompute_select = View(in.precompute_select); + auto precompute_select_shift = View(in.precompute_select_shift); + + const auto& precompute_skew = View(in.precompute_skew); + + const std::array slices{ + View(in.precompute_s1hi), View(in.precompute_s1lo), View(in.precompute_s2hi), View(in.precompute_s2lo), + View(in.precompute_s3hi), View(in.precompute_s3lo), View(in.precompute_s4hi), View(in.precompute_s4lo), + }; + + const auto range_constraint_slice_to_2_bits = [&scaling_factor](const View& s, auto& acc) { + acc += s * (s - 1) * (s - 2) * (s - 3) * scaling_factor; + }; + + const auto convert_to_wnaf = [](const View& s0, const View& s1) { + auto t = s0 + s0; + t += t; + t += s1; + auto naf = t + t - 15; + return naf; + }; + + const auto scaled_transition = q_transition * scaling_factor; + const auto scaled_transition_is_zero = -scaled_transition + scaling_factor; + /** + * @brief Constrain each of our scalar slice chunks (s1, ..., s8) to be 2 bits. + * Doing range checks this way vs permutation-based range check removes need to create sorted list + grand product + * polynomial. Probably cheaper even if we have to split each 4-bit WNAF slice into 2-bit chunks. + */ + range_constraint_slice_to_2_bits(slices[0], std::get<0>(accumulator)); + range_constraint_slice_to_2_bits(slices[1], std::get<1>(accumulator)); + range_constraint_slice_to_2_bits(slices[2], std::get<2>(accumulator)); + range_constraint_slice_to_2_bits(slices[3], std::get<3>(accumulator)); + range_constraint_slice_to_2_bits(slices[4], std::get<4>(accumulator)); + range_constraint_slice_to_2_bits(slices[5], std::get<5>(accumulator)); + range_constraint_slice_to_2_bits(slices[6], std::get<6>(accumulator)); + range_constraint_slice_to_2_bits(slices[7], std::get<7>(accumulator)); + + /** + * @brief If we are processing a new scalar (q_transition = 1), validate that the first slice is positive. + * This requires us to validate slice1 is in the range [8, ... 15]. + * (when converted into wnaf form this maps to the range [1, 3, ..., 15]). + * We do this to ensure the final scalar sum is positive. + * We already know slice1 is in the range [0, ..., 15] + * To check the range [8, ..., 15] we validate the most significant 2 bits (s1) are >=2 + */ + const auto s1_shift = View(in.precompute_s1hi_shift); + const auto s1_shift_msb_set = (s1_shift - 2) * (s1_shift - 3); + std::get<20>(accumulator) += scaled_transition * precompute_select_shift * s1_shift_msb_set; + + /** + * @brief Convert each pair of 2-bit scalar slices into a 4-bit windowed-non-adjacent-form slice. + * Conversion from binary -> wnaf = 2 * binary - 15. + * Converts a value in [0, ..., 15] into [-15, -13, -11, -9, -7, -5, -3, -1, 1, 3, 5, 7, 9, 11 , 13, 15]. + * We use WNAF representation to avoid case where we are conditionally adding a point in our MSM algo. + */ + const auto w0 = convert_to_wnaf(slices[0], slices[1]); + const auto w1 = convert_to_wnaf(slices[2], slices[3]); + const auto w2 = convert_to_wnaf(slices[4], slices[5]); + const auto w3 = convert_to_wnaf(slices[6], slices[7]); + + /** + * @brief Slice consistency check. + * We require that `scalar_sum` on the next row correctly accumulates the 4 WNAF slices present on the current row + * (i.e. 16 WNAF bits). + * i.e. next_scalar_sum - 2^{16} * current_scalar_sum - 2^12 * w_0 - 2^8 * w_1 - 2^4 * w_2 - w_3 = 0 + * @note We only perform slice_consistency check when next row is processing the same scalar as the current row! + * i.e. when q_transition = 0 + * TODO(@zac-williamson) Optimize WNAF use (#2224) + */ + auto row_slice = w0; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += w1; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += w2; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += row_slice; + row_slice += w3; + auto sum_delta = scalar_sum * FF(1ULL << 16) + row_slice; + const auto check_sum = scalar_sum_new - sum_delta; + std::get<8>(accumulator) += precompute_select * check_sum * scaled_transition_is_zero; + + /** + * @brief Round transition logic. + * Goal: `round` is an integer in [0, ... 7] that tracks how many slices we have processed for a given scalar. + * i.e. number of 4-bit WNAF slices processed = round * 4. + * We apply the following constraints: + * If q_transition = 0, round increments by 1 between rows. + * If q_transition = 1, round value at current row = 7 + * If q_transition = 1, round value at next row = 0 + * Question: is this sufficient? We don't actually range constrain `round` (expensive if we don't need to!). + * Let us analyze... + * 1. When `q_transition = 1`, we use a set membership check to map the tuple of (pc, scalar_sum) into a set. + * We compare this set with an equivalent set generated from the transcript columns. The sets must match. + * 2. Only case where, at row `i`, a Prover can set `round` to value > 7 is if `q_transition = 0` for all j > i. + * `precompute_pc` decrements by 1 when `q_transition` = 1 + * We can infer from 1, 2, that if `round > 7`, the resulting wnafs will map into a set at a value of `pc` that is + * greater than all valid msm pc values (assuming the set equivalence check on the scalar sums is satisfied). + * The resulting msm output of such a computation cannot be mapped into the set of msm outputs in + * the transcript columns (see relations in ecc_msm_relation.cpp). + * Conclusion: not applying a strict range-check on `round` does not affect soundness (TODO(@zac-williamson) + * validate this! #2225) + */ + // We combine checks 0, 1 into a single relation + // q_transition * (round - 7) + (-q_transition + 1) * (round_shift - round - 1) + // => q_transition * (round - 7 - round_shift + round + 1) + (round_shift - round - 1) + // => q_transition * (2 * round - round_shift - 6) + (round_shift - round - 1) + const auto round_check = round_shift - round - 1; + std::get<9>(accumulator) += precompute_select * scaled_transition * ((round - round_check - 7) + round_check); + std::get<10>(accumulator) += precompute_select * scaled_transition * round_shift; + + /** + * @brief Scalar transition checks. + * 1: if q_transition = 1, scalar_sum_new = 0 + * 2: if q_transition = 0, pc at next row = pc at current row + * 3: if q_transition = 1, pc at next row = pc at current row - 1 (decrements by 1) + * (we combine 2 and 3 into a single relation) + */ + std::get<11>(accumulator) += precompute_select * scalar_sum_new * scaled_transition; + // (2, 3 combined): q_transition * (pc - pc_shift - 1) + (-q_transition + 1) * (pc_shift - pc) + // => q_transition * (-2 * (pc_shift - pc) - 1) + (pc_shift - pc) + const auto pc_delta = pc_shift - pc; + std::get<12>(accumulator) += + precompute_select * (scaled_transition * ((-pc_delta - pc_delta - 1)) + pc_delta * scaling_factor); + + /** + * @brief Validate skew is 0 or 7 + * 7 is the wnaf representation of -1. + * We have one skew variable per scalar multiplier. We can only represent odd integers in WNAF form. + * If input scalar is even, we must subtract 1 from WNAF scalar sum to get actual value (i.e. where skew = 7) + * We use skew in two places. + * 1: when validating sum of wnaf slices matches input scalar (we add skew to scalar_sum in ecc_set_relation) + * 2: in ecc_msm_relation. Final MSM round uses skew to conditionally subtract a point from the accumulator + */ + std::get<13>(accumulator) += precompute_select * (precompute_skew * (precompute_skew - 7)) * scaling_factor; + + const auto precompute_select_zero = (-precompute_select + 1) * scaling_factor; + std::get<14>(accumulator) += precompute_select_zero * (w0 + 15); + std::get<15>(accumulator) += precompute_select_zero * (w1 + 15); + std::get<16>(accumulator) += precompute_select_zero * (w2 + 15); + std::get<17>(accumulator) += precompute_select_zero * (w3 + 15); + + std::get<18>(accumulator) += precompute_select_zero * round; + std::get<19>(accumulator) += precompute_select_zero * pc; + + // TODO(@zac-williamson #2226) + // if precompute_select = 0, validate pc, round, slice values are all zero + // If we do this we can reduce the degree of the set equivalence relations + // (currently when checking pc/round/wnaf tuples from WNAF columns match those from MSM columns, + // we conditionally include tuples depending on if precompute_select = 1 (for WNAF columns) or if q_add1/2/3/4 = 1 + // (for MSM columns). + // If we KNOW that the wnaf tuple values are 0 when precompute_select = 0, we can remove the conditional checks in + // the set equivalence relation +} +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp index e015988c5c3..62f1aa233f3 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp @@ -14,8 +14,9 @@ namespace bb::stdlib { template class bigfield { public: - typedef T TParams; - typedef bb::field native; + using View = bigfield; + using TParams = T; + using native = bb::field; struct Basis { uint512_t modulus; @@ -61,6 +62,14 @@ template class bigfield { bigfield(Builder* parent_context = nullptr); bigfield(Builder* parent_context, const uint256_t& value); + explicit bigfield(const uint256_t& value) { bigfield(nullptr, value); } + bigfield(const int value) + { + uint256_t result = uint256_t(bb::fr(value)); + bigfield(nullptr, result); + } + bigfield(const bb::fq value) { bigfield(nullptr, uint256_t(value)); } + // we assume the limbs have already been normalized! bigfield(const field_t& a, const field_t& b, @@ -256,6 +265,8 @@ template class bigfield { bool is_constant() const { return prime_basis_limb.witness_index == IS_CONSTANT; } + bigfield invert() const { return (bigfield(1) / bigfield(*this)); } + /** * Create a public one constant * */ From 75cd6a3ae3c240b234c698df614a9d98dbe059d8 Mon Sep 17 00:00:00 2001 From: maramihali Date: Thu, 23 May 2024 22:04:28 +0000 Subject: [PATCH 18/40] something doesn't work... --- .../eccvm_recursion/CMakeLists.txt | 2 +- .../ecc_relation_consistency.test.cpp | 70 ++++++++++++------- .../stdlib/primitives/bigfield/bigfield.hpp | 18 +++++ 3 files changed, 62 insertions(+), 28 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/eccvm_recursion/CMakeLists.txt index 5d8da511431..110656e75da 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/CMakeLists.txt @@ -1 +1 @@ -barretenberg_module(eccvm_recursion eccvm stdlib_circuit_builders commitment_schemes stdlib_primitives) \ No newline at end of file +barretenberg_module(eccvm_recursion eccvm relations stdlib_circuit_builders commitment_schemes stdlib_primitives) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_relation_consistency.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_relation_consistency.test.cpp index 1da429e99d4..767969eff3d 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_relation_consistency.test.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/ecc_relation_consistency.test.cpp @@ -8,48 +8,64 @@ namespace bb { class EccRelationsConsistency : public testing::Test { public: - using Flavor = ECCVMRecursiveFlavor_; - using FF = typename Flavor::FF; - using NativeFF = bb::fq; - using InputElements = typename Flavor::AllValues; - - InputElements get_random_input() + template static InputElements get_input() { InputElements result; - for (FF& element : result.get_all()) { - element = FF(NativeFF::random_element()); + for (auto& element : result.get_all()) { + element = 4; } return result; } - template - static void validate_relation_execution(const InputElements& input_elements, const auto& parameters) + template