diff --git a/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/protogalaxy.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/protogalaxy.bench.cpp index a4164418e4a..b0a4f05afbf 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/protogalaxy.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/protogalaxy.bench.cpp @@ -30,7 +30,7 @@ void fold_one(State& state) noexcept std::shared_ptr instance_1 = construct_instance(); std::shared_ptr instance_2 = construct_instance(); - auto folding_prover = composer.create_folding_prover({ instance_1, instance_2 }, composer.commitment_key); + auto folding_prover = composer.create_folding_prover({ instance_1, instance_2 }); for (auto _ : state) { auto proof = folding_prover.fold_instances(); diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp index 3a43b2ac69e..217e2b42bd2 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp @@ -112,6 +112,13 @@ template struct alignas(32) field { *this = field(value); } + constexpr explicit operator bool() const + { + field out = from_montgomery_form(); + ASSERT(out.data[0] == 0 || out.data[0] == 1); + return static_cast(out.data[0]); + } + constexpr explicit operator uint32_t() const { field out = from_montgomery_form(); diff --git a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra.hpp b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra.hpp index 8629356b726..5f13c7ac66b 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra.hpp @@ -155,7 +155,7 @@ class GoblinUltra { // GoblinUltra needs to expose more public classes than most flavors due to GoblinUltraRecursive reuse, but these // are internal: - private: + public: // WireEntities for basic witness entities template class WireEntities { public: @@ -418,42 +418,8 @@ class GoblinUltra { template class VerifierCommitments_ : public AllEntities { public: - VerifierCommitments_(const std::shared_ptr& verification_key) - { - this->q_m = verification_key->q_m; - this->q_l = verification_key->q_l; - this->q_r = verification_key->q_r; - this->q_o = verification_key->q_o; - this->q_4 = verification_key->q_4; - this->q_c = verification_key->q_c; - this->q_arith = verification_key->q_arith; - this->q_sort = verification_key->q_sort; - this->q_elliptic = verification_key->q_elliptic; - this->q_aux = verification_key->q_aux; - this->q_lookup = verification_key->q_lookup; - this->q_busread = verification_key->q_busread; - this->q_poseidon2_external = verification_key->q_poseidon2_external; - this->q_poseidon2_internal = verification_key->q_poseidon2_internal; - this->sigma_1 = verification_key->sigma_1; - this->sigma_2 = verification_key->sigma_2; - this->sigma_3 = verification_key->sigma_3; - this->sigma_4 = verification_key->sigma_4; - this->id_1 = verification_key->id_1; - this->id_2 = verification_key->id_2; - this->id_3 = verification_key->id_3; - this->id_4 = verification_key->id_4; - this->table_1 = verification_key->table_1; - this->table_2 = verification_key->table_2; - this->table_3 = verification_key->table_3; - this->table_4 = verification_key->table_4; - this->lagrange_first = verification_key->lagrange_first; - this->lagrange_last = verification_key->lagrange_last; - this->lagrange_ecc_op = verification_key->lagrange_ecc_op; - this->databus_id = verification_key->databus_id; - } - VerifierCommitments_(const std::shared_ptr& verification_key, - const WitnessCommitments& witness_commitments) + const std::optional>& witness_commitments = std::nullopt) { this->q_m = verification_key->q_m; this->q_l = verification_key->q_l; @@ -486,19 +452,22 @@ class GoblinUltra { this->lagrange_ecc_op = verification_key->lagrange_ecc_op; this->databus_id = verification_key->databus_id; - this->w_l = witness_commitments.w_l; - this->w_r = witness_commitments.w_r; - this->w_o = witness_commitments.w_o; - this->sorted_accum = witness_commitments.sorted_accum; - this->w_4 = witness_commitments.w_4; - this->z_perm = witness_commitments.z_perm; - this->z_lookup = witness_commitments.z_lookup; - this->ecc_op_wire_1 = witness_commitments.ecc_op_wire_1; - this->ecc_op_wire_2 = witness_commitments.ecc_op_wire_2; - this->ecc_op_wire_3 = witness_commitments.ecc_op_wire_3; - this->calldata = witness_commitments.calldata; - this->calldata = witness_commitments.calldata_read_counts; - this->lookup_inverses = witness_commitments.lookup_inverses; + if (witness_commitments.has_value()) { + auto commitments = witness_commitments.value(); + this->w_l = commitments.w_l; + this->w_r = commitments.w_r; + this->w_o = commitments.w_o; + this->sorted_accum = commitments.sorted_accum; + this->w_4 = commitments.w_4; + this->z_perm = commitments.z_perm; + this->z_lookup = commitments.z_lookup; + this->ecc_op_wire_1 = commitments.ecc_op_wire_1; + this->ecc_op_wire_2 = commitments.ecc_op_wire_2; + this->ecc_op_wire_3 = commitments.ecc_op_wire_3; + this->calldata = commitments.calldata; + this->calldata = commitments.calldata_read_counts; + this->lookup_inverses = commitments.lookup_inverses; + } } }; // Specialize for GoblinUltra (general case used in GoblinUltraRecursive). diff --git a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp index 8c47654f6fe..e1146ea4b70 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp @@ -67,12 +67,14 @@ template class GoblinUltraRecursive_ { using Relations = GoblinUltra::Relations_; static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); + static constexpr size_t MAX_TOTAL_RELATION_LENGTH = compute_max_total_relation_length(); // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` // 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; + static constexpr size_t BATCHED_RELATION_TOTAL_LENGTH = MAX_TOTAL_RELATION_LENGTH + 1; + static constexpr size_t NUM_RELATIONS = std::tuple_size_v; // For instances of this flavour, used in folding, we need a unique sumcheck batching challenge for each // subrelation. This is because using powers of alpha would increase the degree of Protogalaxy polynomial $G$ (the @@ -104,6 +106,12 @@ template class GoblinUltraRecursive_ { */ class VerificationKey : public VerificationKey_> { 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 @@ -112,9 +120,10 @@ template class GoblinUltraRecursive_ { * @param native_key Native verification key from which to extract the precomputed commitments */ VerificationKey(CircuitBuilder* builder, const std::shared_ptr& native_key) - : VerificationKey_>(native_key->circuit_size, - native_key->num_public_inputs) { + 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->q_m = Commitment::from_witness(builder, native_key->q_m); this->q_l = Commitment::from_witness(builder, native_key->q_l); this->q_r = Commitment::from_witness(builder, native_key->q_r); @@ -148,6 +157,11 @@ template class GoblinUltraRecursive_ { }; }; + /** + * @brief A container for the witness commitments. + */ + using WitnessCommitments = GoblinUltra::WitnessEntities; + using CommitmentLabels = GoblinUltra::CommitmentLabels; // Reuse the VerifierCommitments from GoblinUltra using VerifierCommitments = GoblinUltra::VerifierCommitments_; diff --git a/barretenberg/cpp/src/barretenberg/flavor/ultra.hpp b/barretenberg/cpp/src/barretenberg/flavor/ultra.hpp index 3bf08e29b8f..2fd21fab351 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/ultra.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/ultra.hpp @@ -412,37 +412,8 @@ class Ultra { */ class VerifierCommitments : public AllEntities { public: - VerifierCommitments(const std::shared_ptr& verification_key) - { - q_m = verification_key->q_m; - q_c = verification_key->q_c; - q_l = verification_key->q_l; - q_r = verification_key->q_r; - q_o = verification_key->q_o; - q_4 = verification_key->q_4; - q_arith = verification_key->q_arith; - q_sort = verification_key->q_sort; - q_elliptic = verification_key->q_elliptic; - q_aux = verification_key->q_aux; - q_lookup = verification_key->q_lookup; - sigma_1 = verification_key->sigma_1; - sigma_2 = verification_key->sigma_2; - sigma_3 = verification_key->sigma_3; - sigma_4 = verification_key->sigma_4; - id_1 = verification_key->id_1; - id_2 = verification_key->id_2; - id_3 = verification_key->id_3; - id_4 = verification_key->id_4; - table_1 = verification_key->table_1; - table_2 = verification_key->table_2; - table_3 = verification_key->table_3; - table_4 = verification_key->table_4; - lagrange_first = verification_key->lagrange_first; - lagrange_last = verification_key->lagrange_last; - } - VerifierCommitments(const std::shared_ptr& verification_key, - const WitnessCommitments& witness_commitments) + const std::optional& witness_commitments = std::nullopt) { q_m = verification_key->q_m; q_c = verification_key->q_c; @@ -470,13 +441,16 @@ class Ultra { lagrange_first = verification_key->lagrange_first; lagrange_last = verification_key->lagrange_last; - w_l = witness_commitments.w_l; - w_r = witness_commitments.w_r; - w_o = witness_commitments.w_o; - sorted_accum = witness_commitments.sorted_accum; - w_4 = witness_commitments.w_4; - z_perm = witness_commitments.z_perm; - z_lookup = witness_commitments.z_lookup; + if (witness_commitments.has_value()) { + auto commitments = witness_commitments.value(); + this->w_l = commitments.w_l; + this->w_r = commitments.w_r; + this->w_o = commitments.w_o; + this->sorted_accum = commitments.sorted_accum; + this->w_4 = commitments.w_4; + this->z_perm = commitments.z_perm; + this->z_lookup = commitments.z_lookup; + } } }; diff --git a/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp b/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp index ea9303c7c87..94955d72f75 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp @@ -79,11 +79,15 @@ template class UltraRecursive_ { bb::AuxiliaryRelation>; static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); + static_assert(MAX_PARTIAL_RELATION_LENGTH == 6); + static constexpr size_t MAX_TOTAL_RELATION_LENGTH = compute_max_total_relation_length(); + static_assert(MAX_TOTAL_RELATION_LENGTH == 12); // 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 BATCHED_RELATION_TOTAL_LENGTH = MAX_TOTAL_RELATION_LENGTH + 1; static constexpr size_t NUM_RELATIONS = std::tuple_size::value; // For instances of this flavour, used in folding, we need a unique sumcheck batching challenges for each @@ -161,6 +165,12 @@ template class UltraRecursive_ { RefVector get_wires() { return { w_l, w_r, w_o, w_4 }; }; }; + public: + /** + * @brief A container for the witness commitments. + */ + using WitnessCommitments = WitnessEntities; + /** * @brief A base class labelling all entities (for instance, all of the polynomials used by the prover during * sumcheck) in this Honk variant along with particular subsets of interest @@ -229,6 +239,17 @@ template class UltraRecursive_ { }; }; + RefVector get_precomputed() + { + return { q_m, q_c, q_l, q_r, q_o, q_4, q_arith, q_sort, + q_elliptic, q_aux, q_lookup, sigma_1, sigma_2, sigma_3, sigma_4, id_1, + id_2, id_3, id_4, table_1, table_2, table_3, table_4, lagrange_first, + lagrange_last + + }; + } + + RefVector get_witness() { return { w_l, w_r, w_o, w_4, sorted_accum, z_perm, z_lookup }; }; RefVector get_to_be_shifted() { return { table_1, table_2, table_3, table_4, w_l, w_r, w_o, w_4, sorted_accum, z_perm, z_lookup }; @@ -251,6 +272,12 @@ template class UltraRecursive_ { */ class VerificationKey : public VerificationKey_> { 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 * @@ -258,8 +285,10 @@ template class UltraRecursive_ { * @param native_key Native verification key from which to extract the precomputed commitments */ VerificationKey(CircuitBuilder* builder, const std::shared_ptr& native_key) - : VerificationKey_>(native_key->circuit_size, native_key->num_public_inputs) { + 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->q_m = Commitment::from_witness(builder, native_key->q_m); this->q_l = Commitment::from_witness(builder, native_key->q_l); this->q_r = Commitment::from_witness(builder, native_key->q_r); @@ -317,38 +346,38 @@ template class UltraRecursive_ { this->z_lookup = "Z_LOOKUP"; this->sorted_accum = "SORTED_ACCUM"; - // The ones beginning with "__" are only used for debugging - this->q_c = "__Q_C"; - this->q_l = "__Q_L"; - this->q_r = "__Q_R"; - this->q_o = "__Q_O"; - this->q_4 = "__Q_4"; - this->q_m = "__Q_M"; - this->q_arith = "__Q_ARITH"; - this->q_sort = "__Q_SORT"; - this->q_elliptic = "__Q_ELLIPTIC"; - this->q_aux = "__Q_AUX"; - this->q_lookup = "__Q_LOOKUP"; - this->sigma_1 = "__SIGMA_1"; - this->sigma_2 = "__SIGMA_2"; - this->sigma_3 = "__SIGMA_3"; - this->sigma_4 = "__SIGMA_4"; - this->id_1 = "__ID_1"; - this->id_2 = "__ID_2"; - this->id_3 = "__ID_3"; - this->id_4 = "__ID_4"; - this->table_1 = "__TABLE_1"; - this->table_2 = "__TABLE_2"; - this->table_3 = "__TABLE_3"; - this->table_4 = "__TABLE_4"; - this->lagrange_first = "__LAGRANGE_FIRST"; - this->lagrange_last = "__LAGRANGE_LAST"; + this->q_c = "Q_C"; + this->q_l = "Q_L"; + this->q_r = "Q_R"; + this->q_o = "Q_O"; + this->q_4 = "Q_4"; + this->q_m = "Q_M"; + this->q_arith = "Q_ARITH"; + this->q_sort = "Q_SORT"; + this->q_elliptic = "Q_ELLIPTIC"; + this->q_aux = "Q_AUX"; + this->q_lookup = "Q_LOOKUP"; + this->sigma_1 = "SIGMA_1"; + this->sigma_2 = "SIGMA_2"; + this->sigma_3 = "SIGMA_3"; + this->sigma_4 = "SIGMA_4"; + this->id_1 = "ID_1"; + this->id_2 = "ID_2"; + this->id_3 = "ID_3"; + this->id_4 = "ID_4"; + this->table_1 = "TABLE_1"; + this->table_2 = "TABLE_2"; + this->table_3 = "TABLE_3"; + this->table_4 = "TABLE_4"; + this->lagrange_first = "LAGRANGE_FIRST"; + this->lagrange_last = "LAGRANGE_LAST"; }; }; class VerifierCommitments : public AllEntities { public: - VerifierCommitments(const std::shared_ptr& verification_key) + VerifierCommitments(const std::shared_ptr& verification_key, + const std::optional& witness_commitments = std::nullopt) { this->q_m = verification_key->q_m; this->q_l = verification_key->q_l; @@ -375,6 +404,17 @@ template class UltraRecursive_ { this->table_4 = verification_key->table_4; this->lagrange_first = verification_key->lagrange_first; this->lagrange_last = verification_key->lagrange_last; + + if (witness_commitments.has_value()) { + auto commitments = witness_commitments.value(); + this->w_l = commitments.w_l; + this->w_r = commitments.w_r; + this->w_o = commitments.w_o; + this->sorted_accum = commitments.sorted_accum; + this->w_4 = commitments.w_4; + this->z_perm = commitments.z_perm; + this->z_lookup = commitments.z_lookup; + } } }; diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp index ca78b870604..6a138c51a51 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp @@ -269,14 +269,10 @@ std::shared_ptr ProtoGalaxyProver_ FoldingResult ProtoGalaxyProver_::fold_instances() { prepare_for_folding(); - - // TODO(#https://github.com/AztecProtocol/barretenberg/issues/740): Handle the case where we are folding for the - // first time and accumulator is 0 FF delta = transcript->get_challenge("delta"); auto accumulator = get_accumulator(); diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp index 0e33b27cd77..bf7d4c50117 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp @@ -6,17 +6,20 @@ template void ProtoGalaxyVerifier_::receive_accumulator(const std::shared_ptr& inst, const std::string& domain_separator) { + // Get circuit parameters inst->instance_size = transcript->template receive_from_prover(domain_separator + "_instance_size"); inst->log_instance_size = static_cast(numeric::get_msb(inst->instance_size)); inst->public_input_size = transcript->template receive_from_prover(domain_separator + "_public_input_size"); + // Get folded public inputs for (size_t i = 0; i < inst->public_input_size; ++i) { auto public_input_i = transcript->template receive_from_prover(domain_separator + "_public_input_" + std::to_string(i)); inst->public_inputs.emplace_back(public_input_i); } + // Get folded relation parameters auto eta = transcript->template receive_from_prover(domain_separator + "_eta"); auto beta = transcript->template receive_from_prover(domain_separator + "_beta"); auto gamma = transcript->template receive_from_prover(domain_separator + "_gamma"); @@ -26,6 +29,7 @@ void ProtoGalaxyVerifier_::receive_accumulator(const std::sha inst->relation_parameters = RelationParameters{ eta, beta, gamma, public_input_delta, lookup_grand_product_delta }; + // Get the folded relation separator challenges \vec{α} for (size_t idx = 0; idx < NUM_SUBRELATIONS - 1; idx++) { inst->alphas[idx] = transcript->template receive_from_prover(domain_separator + "_alpha_" + std::to_string(idx)); @@ -33,11 +37,14 @@ void ProtoGalaxyVerifier_::receive_accumulator(const std::sha inst->target_sum = transcript->template receive_from_prover(domain_separator + "_target_sum"); + // Get the folded gate challenges, \vec{β} in the paper inst->gate_challenges = std::vector(inst->log_instance_size); for (size_t idx = 0; idx < inst->log_instance_size; idx++) { inst->gate_challenges[idx] = transcript->template receive_from_prover(domain_separator + "_gate_challenge_" + std::to_string(idx)); } + + // Get the folded commitments to all witness polynomials auto comm_view = inst->witness_commitments.get_all(); auto witness_labels = inst->commitment_labels.get_witness(); for (size_t idx = 0; idx < witness_labels.size(); idx++) { @@ -45,6 +52,7 @@ void ProtoGalaxyVerifier_::receive_accumulator(const std::sha transcript->template receive_from_prover(domain_separator + "_" + witness_labels[idx]); } + // Get the folded commitments to selector polynomials inst->verification_key = std::make_shared(inst->instance_size, inst->public_input_size); auto vk_view = inst->verification_key->get_all(); auto vk_labels = inst->commitment_labels.get_precomputed(); @@ -57,6 +65,7 @@ template void ProtoGalaxyVerifier_::receive_and_finalise_instance(const std::shared_ptr& inst, const std::string& domain_separator) { + // Get circuit parameters and the public inputs inst->instance_size = transcript->template receive_from_prover(domain_separator + "_instance_size"); inst->log_instance_size = static_cast(numeric::get_msb(inst->instance_size)); inst->public_input_size = @@ -71,33 +80,39 @@ void ProtoGalaxyVerifier_::receive_and_finalise_instance(cons inst->pub_inputs_offset = transcript->template receive_from_prover(domain_separator + "_pub_inputs_offset"); + // Get commitments to first three wire polynomials auto labels = inst->commitment_labels; auto& witness_commitments = inst->witness_commitments; witness_commitments.w_l = transcript->template receive_from_prover(domain_separator + "_" + labels.w_l); witness_commitments.w_r = transcript->template receive_from_prover(domain_separator + "_" + labels.w_r); witness_commitments.w_o = transcript->template receive_from_prover(domain_separator + "_" + labels.w_o); + // Get challenge for sorted list batching and wire four memory records commitment auto eta = transcript->get_challenge(domain_separator + "_eta"); witness_commitments.sorted_accum = transcript->template receive_from_prover(domain_separator + "_" + labels.sorted_accum); witness_commitments.w_4 = transcript->template receive_from_prover(domain_separator + "_" + labels.w_4); + // Get permutation challenges and commitment to permutation and lookup grand products auto [beta, gamma] = transcript->get_challenges(domain_separator + "_beta", domain_separator + "_gamma"); witness_commitments.z_perm = transcript->template receive_from_prover(domain_separator + "_" + labels.z_perm); witness_commitments.z_lookup = transcript->template receive_from_prover(domain_separator + "_" + labels.z_lookup); + // Compute correction terms for grand products const FF public_input_delta = compute_public_input_delta( inst->public_inputs, beta, gamma, inst->instance_size, inst->pub_inputs_offset); const FF lookup_grand_product_delta = compute_lookup_grand_product_delta(beta, gamma, inst->instance_size); inst->relation_parameters = RelationParameters{ eta, beta, gamma, public_input_delta, lookup_grand_product_delta }; + // Get the relation separation challenges for (size_t idx = 0; idx < NUM_SUBRELATIONS - 1; idx++) { inst->alphas[idx] = transcript->get_challenge(domain_separator + "_alpha_" + std::to_string(idx)); } + // Get the commitments to the selector polynomials for the given instance inst->verification_key = std::make_shared(inst->instance_size, inst->public_input_size); auto vk_view = inst->verification_key->get_all(); auto vk_labels = labels.get_precomputed(); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp index 594ac29d75d..d437cee5044 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp @@ -17,6 +17,8 @@ template struct bn254 { using ScalarFieldNative = curve::BN254::ScalarField; using BaseFieldNative = curve::BN254::BaseField; using GroupNative = curve::BN254::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 diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.cpp new file mode 100644 index 00000000000..6578f48cc99 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.cpp @@ -0,0 +1,96 @@ +#include "barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.hpp" +#include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" +#include "barretenberg/numeric/bitop/get_msb.hpp" +#include "barretenberg/sumcheck/instance/verifier_instance.hpp" +#include "barretenberg/transcript/transcript.hpp" + +namespace bb::stdlib::recursion::honk { + +template +DeciderRecursiveVerifier_::DeciderRecursiveVerifier_(Builder* builder) + : builder(builder) +{} + +/** + * @brief This function verifies an Ultra Honk proof for a given Flavor, produced for a relaxed instance (ϕ, \vec{β*}, + * e*). + * + */ +template +std::array DeciderRecursiveVerifier_::verify_proof( + const bb::plonk::proof& proof) +{ + using Sumcheck = ::bb::honk::sumcheck::SumcheckVerifier; + using Curve = typename Flavor::Curve; + using ZeroMorph = ::bb::honk::pcs::zeromorph::ZeroMorphVerifier_; + using VerifierCommitments = typename Flavor::VerifierCommitments; + using Transcript = typename Flavor::Transcript; + using Instance = typename ::bb::honk::VerifierInstance_; + + static constexpr size_t NUM_SUBRELATIONS = Flavor::NUM_SUBRELATIONS; + transcript = std::make_shared(builder, proof.proof_data); + auto inst = std::make_unique(); + + const auto instance_size = transcript->template receive_from_prover("instance_size"); + const auto public_input_size = transcript->template receive_from_prover("public_input_size"); + const auto log_instance_size = static_cast(numeric::get_msb(uint32_t(instance_size.get_value()))); + + for (size_t i = 0; i < uint32_t(public_input_size.get_value()); ++i) { + auto public_input_i = transcript->template receive_from_prover("public_input_" + std::to_string(i)); + inst->public_inputs.emplace_back(public_input_i); + } + + auto eta = transcript->template receive_from_prover("eta"); + auto beta = transcript->template receive_from_prover("beta"); + auto gamma = transcript->template receive_from_prover("gamma"); + auto public_input_delta = transcript->template receive_from_prover("public_input_delta"); + auto lookup_grand_product_delta = transcript->template receive_from_prover("lookup_grand_product_delta"); + inst->relation_parameters = + RelationParameters{ eta, beta, gamma, public_input_delta, lookup_grand_product_delta }; + + for (size_t idx = 0; idx < NUM_SUBRELATIONS - 1; idx++) { + inst->alphas[idx] = transcript->template receive_from_prover("alpha" + std::to_string(idx)); + } + + inst->target_sum = transcript->template receive_from_prover("target_sum"); + + inst->gate_challenges = std::vector(log_instance_size); + for (size_t idx = 0; idx < log_instance_size; idx++) { + inst->gate_challenges[idx] = + transcript->template receive_from_prover("gate_challenge_" + std::to_string(idx)); + } + auto comm_view = inst->witness_commitments.get_all(); + auto witness_labels = inst->commitment_labels.get_witness(); + for (size_t idx = 0; idx < witness_labels.size(); idx++) { + comm_view[idx] = transcript->template receive_from_prover(witness_labels[idx]); + } + + inst->verification_key = std::make_shared(inst->instance_size, inst->public_input_size); + auto vk_view = inst->verification_key->get_all(); + auto vk_labels = inst->commitment_labels.get_precomputed(); + for (size_t idx = 0; idx < vk_labels.size(); idx++) { + vk_view[idx] = transcript->template receive_from_prover(vk_labels[idx]); + } + + VerifierCommitments commitments{ inst->verification_key, inst->witness_commitments }; + + auto sumcheck = Sumcheck(log_instance_size, transcript, inst->target_sum); + + auto [multivariate_challenge, claimed_evaluations, sumcheck_verified] = + sumcheck.verify(inst->relation_parameters, inst->alphas, inst->gate_challenges); + + // Execute ZeroMorph rounds. See https://hackmd.io/dlf9xEwhTQyE3hiGbq4FsA?view for a complete description of the + // unrolled protocol. + auto pairing_points = ZeroMorph::verify(commitments.get_unshifted(), + commitments.get_to_be_shifted(), + claimed_evaluations.get_unshifted(), + claimed_evaluations.get_shifted(), + multivariate_challenge, + transcript); + + return pairing_points; +} + +template class DeciderRecursiveVerifier_>; +template class DeciderRecursiveVerifier_>; +} // namespace bb::stdlib::recursion::honk diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.hpp new file mode 100644 index 00000000000..18e6b75fa0b --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.hpp @@ -0,0 +1,30 @@ +#pragma once +#include "barretenberg/flavor/goblin_ultra_recursive.hpp" +#include "barretenberg/flavor/ultra_recursive.hpp" +#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/stdlib/recursion/honk/transcript/transcript.hpp" +#include "barretenberg/sumcheck/sumcheck.hpp" + +namespace bb::stdlib::recursion::honk { +template class DeciderRecursiveVerifier_ { + using FF = typename Flavor::FF; + using Commitment = typename Flavor::Commitment; + using GroupElement = typename Flavor::GroupElement; + using VerificationKey = typename Flavor::VerificationKey; + using VerifierCommitmentKey = typename Flavor::VerifierCommitmentKey; + using Builder = typename Flavor::CircuitBuilder; + using RelationSeparator = typename Flavor::RelationSeparator; + using PairingPoints = std::array; + + public: + explicit DeciderRecursiveVerifier_(Builder* builder); + + PairingPoints verify_proof(const bb::plonk::proof& proof); + + std::map commitments; + std::shared_ptr pcs_verification_key; + Builder* builder; + std::shared_ptr> transcript; +}; + +} // namespace bb::stdlib::recursion::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.cpp new file mode 100644 index 00000000000..68c366de5b9 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.cpp @@ -0,0 +1,320 @@ +#include "protogalaxy_recursive_verifier.hpp" +#include "barretenberg/polynomials/polynomial.hpp" +#include "barretenberg/proof_system/library/grand_product_delta.hpp" +namespace bb::stdlib::recursion::honk { + +template +void ProtoGalaxyRecursiveVerifier_::receive_accumulator(const std::shared_ptr& inst, + const std::string& domain_separator) +{ + // Get circuit parameters + const auto instance_size = transcript->template receive_from_prover(domain_separator + "_instance_size"); + const auto public_input_size = + transcript->template receive_from_prover(domain_separator + "_public_input_size"); + inst->instance_size = uint32_t(instance_size.get_value()); + inst->log_instance_size = uint32_t(numeric::get_msb(inst->instance_size)); + inst->public_input_size = uint32_t(public_input_size.get_value()); + + // Get folded public inputs + for (size_t i = 0; i < inst->public_input_size; ++i) { + auto public_input_i = + transcript->template receive_from_prover(domain_separator + "_public_input_" + std::to_string(i)); + inst->public_inputs.emplace_back(public_input_i); + } + + // Get folded relation parameters + auto eta = transcript->template receive_from_prover(domain_separator + "_eta"); + auto beta = transcript->template receive_from_prover(domain_separator + "_beta"); + auto gamma = transcript->template receive_from_prover(domain_separator + "_gamma"); + auto public_input_delta = transcript->template receive_from_prover(domain_separator + "_public_input_delta"); + auto lookup_grand_product_delta = + transcript->template receive_from_prover(domain_separator + "_lookup_grand_product_delta"); + inst->relation_parameters = + RelationParameters{ eta, beta, gamma, public_input_delta, lookup_grand_product_delta }; + + // Get the folded relation separator challenges \vec{α} + for (size_t idx = 0; idx < NUM_SUBRELATIONS - 1; idx++) { + inst->alphas[idx] = + transcript->template receive_from_prover(domain_separator + "_alpha_" + std::to_string(idx)); + } + + inst->target_sum = transcript->template receive_from_prover(domain_separator + "_target_sum"); + + // Get the folded gate challenges, \vec{β} in the paper + inst->gate_challenges = std::vector(inst->log_instance_size); + for (size_t idx = 0; idx < inst->log_instance_size; idx++) { + inst->gate_challenges[idx] = + transcript->template receive_from_prover(domain_separator + "_gate_challenge_" + std::to_string(idx)); + } + + // Get the folded commitments to all witness polynomials + auto comm_view = inst->witness_commitments.get_all(); + auto witness_labels = inst->commitment_labels.get_witness(); + for (size_t idx = 0; idx < witness_labels.size(); idx++) { + comm_view[idx] = + transcript->template receive_from_prover(domain_separator + "_" + witness_labels[idx]); + } + + // Get the folded commitments to selector polynomials + inst->verification_key = std::make_shared(inst->instance_size, inst->public_input_size); + auto vk_view = inst->verification_key->get_all(); + auto vk_labels = inst->commitment_labels.get_precomputed(); + for (size_t idx = 0; idx < vk_labels.size(); idx++) { + vk_view[idx] = transcript->template receive_from_prover(domain_separator + "_" + vk_labels[idx]); + } +} + +template +void ProtoGalaxyRecursiveVerifier_::receive_and_finalise_instance( + const std::shared_ptr& inst, const std::string& domain_separator) +{ + // Get circuit parameters and the public inputs + const auto instance_size = transcript->template receive_from_prover(domain_separator + "_instance_size"); + const auto public_input_size = + transcript->template receive_from_prover(domain_separator + "_public_input_size"); + inst->instance_size = uint32_t(instance_size.get_value()); + inst->log_instance_size = static_cast(numeric::get_msb(inst->instance_size)); + inst->public_input_size = uint32_t(public_input_size.get_value()); + + for (size_t i = 0; i < inst->public_input_size; ++i) { + auto public_input_i = + transcript->template receive_from_prover(domain_separator + "_public_input_" + std::to_string(i)); + inst->public_inputs.emplace_back(public_input_i); + } + + const auto pub_inputs_offset = + transcript->template receive_from_prover(domain_separator + "_pub_inputs_offset"); + + inst->pub_inputs_offset = uint32_t(pub_inputs_offset.get_value()); + + // Get commitments to first three wire polynomials + auto labels = inst->commitment_labels; + auto& witness_commitments = inst->witness_commitments; + witness_commitments.w_l = transcript->template receive_from_prover(domain_separator + "_" + labels.w_l); + witness_commitments.w_r = transcript->template receive_from_prover(domain_separator + "_" + labels.w_r); + witness_commitments.w_o = transcript->template receive_from_prover(domain_separator + "_" + labels.w_o); + + // Get challenge for sorted list batching and wire four memory records commitment + auto eta = transcript->get_challenge(domain_separator + "_eta"); + witness_commitments.sorted_accum = + transcript->template receive_from_prover(domain_separator + "_" + labels.sorted_accum); + witness_commitments.w_4 = transcript->template receive_from_prover(domain_separator + "_" + labels.w_4); + + // Get permutation challenges and commitment to permutation and lookup grand products + auto [beta, gamma] = transcript->get_challenges(domain_separator + "_beta", domain_separator + "_gamma"); + witness_commitments.z_perm = + transcript->template receive_from_prover(domain_separator + "_" + labels.z_perm); + witness_commitments.z_lookup = + transcript->template receive_from_prover(domain_separator + "_" + labels.z_lookup); + + // Compute correction terms for grand products + const FF public_input_delta = bb::honk::compute_public_input_delta( + inst->public_inputs, beta, gamma, inst->instance_size, inst->pub_inputs_offset); + const FF lookup_grand_product_delta = + bb::honk::compute_lookup_grand_product_delta(beta, gamma, inst->instance_size); + inst->relation_parameters = + RelationParameters{ eta, beta, gamma, public_input_delta, lookup_grand_product_delta }; + + // Get the relation separation challenges + for (size_t idx = 0; idx < NUM_SUBRELATIONS - 1; idx++) { + inst->alphas[idx] = transcript->get_challenge(domain_separator + "_alpha_" + std::to_string(idx)); + } + + // Get the commitments to the selector polynomials for the given instance + inst->verification_key = std::make_shared(inst->instance_size, inst->public_input_size); + auto vk_view = inst->verification_key->get_all(); + auto vk_labels = labels.get_precomputed(); + for (size_t idx = 0; idx < vk_labels.size(); idx++) { + vk_view[idx] = transcript->template receive_from_prover(domain_separator + "_" + vk_labels[idx]); + } +} + +// TODO(https://github.com/AztecProtocol/barretenberg/issues/795): The rounds prior to actual verifying are common +// between decider and folding verifier and could be somehow shared so we do not duplicate code so much. +template void ProtoGalaxyRecursiveVerifier_::prepare_for_folding() +{ + auto index = 0; + auto inst = instances[0]; + auto domain_separator = std::to_string(index); + const auto is_accumulator = transcript->template receive_from_prover(domain_separator + "is_accumulator"); + inst->is_accumulator = static_cast(is_accumulator.get_value()); + if (inst->is_accumulator) { + receive_accumulator(inst, domain_separator); + } else { + // This is the first round of folding and we need to generate some gate challenges. + // TODO(https://github.com/AztecProtocol/barretenberg/issues/740): implement option 2 to make this more + // efficient by avoiding the computation of the perturbator + receive_and_finalise_instance(inst, domain_separator); + inst->target_sum = 0; + auto beta = transcript->get_challenge(domain_separator + "_initial_gate_challenge"); + std::vector gate_challenges(inst->log_instance_size); + gate_challenges[0] = beta; + for (size_t i = 1; i < inst->log_instance_size; i++) { + gate_challenges[i] = gate_challenges[i - 1].sqr(); + } + inst->gate_challenges = gate_challenges; + } + index++; + + for (auto it = instances.begin() + 1; it != instances.end(); it++, index++) { + auto inst = *it; + auto domain_separator = std::to_string(index); + receive_and_finalise_instance(inst, domain_separator); + } +} + +template +void ProtoGalaxyRecursiveVerifier_::verify_folding_proof(std::vector proof) +{ + using Transcript = typename Flavor::Transcript; + using ElementNative = typename Flavor::Curve::ElementNative; + using AffineElementNative = typename Flavor::Curve::AffineElementNative; + using ScalarNative = typename Flavor::Curve::ScalarFieldNative; + + transcript = std::make_shared(builder, proof); + prepare_for_folding(); + + auto delta = transcript->get_challenge("delta"); + auto accumulator = get_accumulator(); + auto deltas = compute_round_challenge_pows(accumulator->log_instance_size, delta); + + std::vector perturbator_coeffs(accumulator->log_instance_size + 1); + for (size_t idx = 0; idx <= accumulator->log_instance_size; idx++) { + perturbator_coeffs[idx] = transcript->template receive_from_prover("perturbator_" + std::to_string(idx)); + } + + // TODO(https://github.com/AztecProtocol/barretenberg/issues/833): As currently the stdlib transcript is not + // creating proper constraints linked to Fiat-Shamir we add an additonal gate to ensure assert_equal is correct. + // This comparison to 0 can be removed here and below once we have merged the transcript. + auto zero = FF::from_witness(builder, ScalarNative(0)); + zero.assert_equal(accumulator->target_sum - perturbator_coeffs[0], "F(0) != e"); + + FF perturbator_challenge = transcript->get_challenge("perturbator_challenge"); + + auto perturbator_at_challenge = evaluate_perturbator(perturbator_coeffs, perturbator_challenge); + // The degree of K(X) is dk - k - 1 = k(d - 1) - 1. Hence we need k(d - 1) evaluations to represent it. + std::array combiner_quotient_evals; + for (size_t idx = 0; idx < VerifierInstances::BATCHED_EXTENDED_LENGTH - VerifierInstances::NUM; idx++) { + combiner_quotient_evals[idx] = transcript->template receive_from_prover( + "combiner_quotient_" + std::to_string(idx + VerifierInstances::NUM)); + } + Univariate combiner_quotient( + combiner_quotient_evals); + FF combiner_challenge = transcript->get_challenge("combiner_quotient_challenge"); + auto combiner_quotient_at_challenge = combiner_quotient.evaluate(combiner_challenge); // fine recursive i think + + auto vanishing_polynomial_at_challenge = combiner_challenge * (combiner_challenge - FF(1)); + auto lagranges = std::vector{ FF(1) - combiner_challenge, combiner_challenge }; + + // Compute next folding parameters and verify against the ones received from the prover + auto expected_next_target_sum = + perturbator_at_challenge * lagranges[0] + vanishing_polynomial_at_challenge * combiner_quotient_at_challenge; + auto next_target_sum = transcript->template receive_from_prover("next_target_sum"); + zero.assert_equal(expected_next_target_sum - next_target_sum, "next target sum mismatch"); + + auto expected_betas_star = update_gate_challenges(perturbator_challenge, accumulator->gate_challenges, deltas); + for (size_t idx = 0; idx < accumulator->log_instance_size; idx++) { + auto beta_star = transcript->template receive_from_prover("next_gate_challenge_" + std::to_string(idx)); + zero.assert_equal(beta_star - expected_betas_star[idx], + " next gate challenge mismatch at: " + std::to_string(idx)); + } + + // Compute ϕ and verify against the data received from the prover + WitnessCommitments acc_witness_commitments; + auto witness_labels = commitment_labels.get_witness(); + size_t comm_idx = 0; + auto random_generator = Commitment::from_witness(builder, AffineElementNative(ElementNative::random_element())); + for (auto& expected_comm : acc_witness_commitments.get_all()) { + expected_comm = random_generator; + size_t inst = 0; + for (auto& instance : instances) { + expected_comm = expected_comm + instance->witness_commitments.get_all()[comm_idx] * lagranges[inst]; + inst++; + } + auto comm = transcript->template receive_from_prover("next_" + witness_labels[comm_idx]); + auto res = expected_comm - comm; + random_generator.x.assert_equal(res.x); + random_generator.y.assert_equal(res.y); + comm_idx++; + } + + std::vector folded_public_inputs(instances[0]->public_inputs.size(), 0); + size_t public_input_idx = 0; + for (auto& expected_public_input : folded_public_inputs) { + size_t inst = 0; + for (auto& instance : instances) { + expected_public_input += instance->public_inputs[public_input_idx] * lagranges[inst]; + inst++; + } + auto next_public_input = + transcript->template receive_from_prover("next_public_input" + std::to_string(public_input_idx)); + zero.assert_equal(expected_public_input - next_public_input, + "folded public input mismatch at: " + std::to_string(public_input_idx)); + public_input_idx++; + } + + for (size_t alpha_idx = 0; alpha_idx < NUM_SUBRELATIONS - 1; alpha_idx++) { + FF expected_alpha(0); + size_t instance_idx = 0; + for (auto& instance : instances) { + expected_alpha += instance->alphas[alpha_idx] * lagranges[instance_idx]; + instance_idx++; + } + auto next_alpha = transcript->template receive_from_prover("next_alpha_" + std::to_string(alpha_idx)); + zero.assert_equal(expected_alpha - next_alpha, + "folded relation separator mismatch at: " + std::to_string(alpha_idx)); + } + + auto expected_parameters = bb::RelationParameters{}; + for (size_t inst_idx = 0; inst_idx < VerifierInstances::NUM; inst_idx++) { + auto instance = instances[inst_idx]; + expected_parameters.eta += instance->relation_parameters.eta * lagranges[inst_idx]; + expected_parameters.beta += instance->relation_parameters.beta * lagranges[inst_idx]; + expected_parameters.gamma += instance->relation_parameters.gamma * lagranges[inst_idx]; + expected_parameters.public_input_delta += + instance->relation_parameters.public_input_delta * lagranges[inst_idx]; + expected_parameters.lookup_grand_product_delta += + instance->relation_parameters.lookup_grand_product_delta * lagranges[inst_idx]; + } + + auto next_eta = transcript->template receive_from_prover("next_eta"); + zero.assert_equal(expected_parameters.eta - next_eta, "relation parameter eta mismatch"); + + auto next_beta = transcript->template receive_from_prover("next_beta"); + zero.assert_equal(expected_parameters.beta - next_beta, "relation parameter beta mismatch"); + + auto next_gamma = transcript->template receive_from_prover("next_gamma"); + zero.assert_equal(expected_parameters.gamma - next_gamma, "relation parameter gamma mismatch"); + + auto next_public_input_delta = transcript->template receive_from_prover("next_public_input_delta"); + zero.assert_equal(expected_parameters.public_input_delta - next_public_input_delta, + "relation parameter public input delta mismatch"); + + auto next_lookup_grand_product_delta = + transcript->template receive_from_prover("next_lookup_grand_product_delta"); + zero.assert_equal(expected_parameters.lookup_grand_product_delta - next_lookup_grand_product_delta, + "relation parameter lookup grand product delta mismatch"); + + auto acc_vk = std::make_shared(instances[0]->instance_size, instances[0]->public_input_size); + auto vk_labels = commitment_labels.get_precomputed(); + size_t vk_idx = 0; + for (auto& expected_vk : acc_vk->get_all()) { + size_t inst = 0; + expected_vk = random_generator; + for (auto& instance : instances) { + expected_vk = expected_vk + instance->verification_key->get_all()[vk_idx] * lagranges[inst]; + inst++; + } + auto vk = transcript->template receive_from_prover("next_" + vk_labels[vk_idx]); + auto res = expected_vk - vk; + random_generator.x.assert_equal(res.x); + random_generator.y.assert_equal(res.y); + vk_idx++; + } +} + +template class ProtoGalaxyRecursiveVerifier_< + bb::honk::VerifierInstances_, 2>>; +template class ProtoGalaxyRecursiveVerifier_< + bb::honk::VerifierInstances_, 2>>; +} // namespace bb::stdlib::recursion::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.hpp new file mode 100644 index 00000000000..64b75719542 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.hpp @@ -0,0 +1,118 @@ +#pragma once +#include "barretenberg/flavor/flavor.hpp" +#include "barretenberg/flavor/goblin_ultra_recursive.hpp" +#include "barretenberg/flavor/ultra_recursive.hpp" +#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/protogalaxy/folding_result.hpp" +#include "barretenberg/stdlib/recursion/honk/transcript/transcript.hpp" +#include "barretenberg/sumcheck/instance/instances.hpp" + +namespace bb::stdlib::recursion::honk { +template class ProtoGalaxyRecursiveVerifier_ { + public: + using Flavor = typename VerifierInstances::Flavor; + using FF = typename Flavor::FF; + using Commitment = typename Flavor::Commitment; + using GroupElement = typename Flavor::GroupElement; + using Instance = typename VerifierInstances::Instance; + using VerificationKey = typename Flavor::VerificationKey; + using WitnessCommitments = typename Flavor::WitnessCommitments; + using CommitmentLabels = typename Flavor::CommitmentLabels; + using Builder = typename Flavor::CircuitBuilder; + using RelationSeparator = typename Flavor::RelationSeparator; + using PairingPoints = std::array; + + static constexpr size_t NUM_SUBRELATIONS = Flavor::NUM_SUBRELATIONS; + + VerifierInstances instances; + + CommitmentLabels commitment_labels; + + Builder* builder; + std::shared_ptr> transcript; + + explicit ProtoGalaxyRecursiveVerifier_(Builder* builder) + : instances(VerifierInstances()) + , builder(builder){}; + /** + * @brief Given a new round challenge δ for each iteration of the full ProtoGalaxy protocol, compute the vector + * [δ, δ^2,..., δ^t] where t = logn and n is the size of the instance. + */ + static std::vector compute_round_challenge_pows(size_t log_instance_size, FF round_challenge) + { + std::vector pows(log_instance_size); + pows[0] = round_challenge; + for (size_t i = 1; i < log_instance_size; i++) { + pows[i] = pows[i - 1].sqr(); + } + return pows; + } + + static std::vector update_gate_challenges(const FF perturbator_challenge, + const std::vector& gate_challenges, + const std::vector& round_challenges) + { + auto log_instance_size = gate_challenges.size(); + std::vector next_gate_challenges(log_instance_size); + + for (size_t idx = 0; idx < log_instance_size; idx++) { + next_gate_challenges[idx] = gate_challenges[idx] + perturbator_challenge * round_challenges[idx]; + } + return next_gate_challenges; + } + + std::shared_ptr get_accumulator() { return instances[0]; } + + /** + * @brief Instatiate the instances and the transcript. + * + * @param fold_data The data transmitted via the transcript by the prover. + */ + void prepare_for_folding(); + + /** + * @brief Instantiate the accumulator (i.e. the relaxed instance) from the transcript. + * + */ + void receive_accumulator(const std::shared_ptr&, const std::string&); + + /** + * @brief Process the public data ϕ for the Instances to be folded. + * + */ + void receive_and_finalise_instance(const std::shared_ptr&, const std::string&); + + /** + * @brief Run the folding protocol on the verifier side to establish whether the public data ϕ of the new + * accumulator, received from the prover is the same as that produced by the verifier. + * + * @details In the recursive setting this function doesn't return anything because the equality checks performed by + * the recursive verifier, ensuring the folded ϕ*, e* and β* on the verifier side correspond to what has been sent + * by the prover, are expressed as constraints. + + */ + void verify_folding_proof(std::vector proof); + + /** + * @brief Evaluates the perturbator at a given scalar, in a sequential manner for the recursive setting. + * + * @details This method is equivalent to the one in the Polynomial class for evaluating a polynomial, represented by + * coefficients in monomial basis, at a given point. The Polynomial class is used in the native verifier for + * constructing and computing the perturbator. We implement this separate functionality here in the recursive + * folding verifier to avoid instantiating the entire Polynomial class on stdlib::bn254. Furthermore, the evaluation + * needs to be done sequentially as we don't support a parallel_for in circuits. + * + */ + static FF evaluate_perturbator(std::vector coeffs, FF point) + { + FF point_acc = FF(1); + FF result = FF(0); + for (size_t i = 0; i < coeffs.size(); i++) { + result += coeffs[i] * point_acc; + point_acc *= point; + } + return result; + }; +}; + +} // namespace bb::stdlib::recursion::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.test.cpp new file mode 100644 index 00000000000..61dee74084f --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.test.cpp @@ -0,0 +1,350 @@ +#include "barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.hpp" +#include "barretenberg/common/test.hpp" +#include "barretenberg/flavor/ultra_recursive.hpp" +#include "barretenberg/stdlib/hash/blake3s/blake3s.hpp" +#include "barretenberg/stdlib/hash/pedersen/pedersen.hpp" +#include "barretenberg/stdlib/primitives/curves/bn254.hpp" +#include "barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.hpp" +#include "barretenberg/ultra_honk/ultra_composer.hpp" + +namespace bb::stdlib::recursion::honk { +class ProtogalaxyRecursiveTest : public testing::Test { + public: + // Define types relevant for testing + using UltraFlavor = ::bb::honk::flavor::Ultra; + using GoblinUltraFlavor = ::bb::honk::flavor::GoblinUltra; + using UltraComposer = ::bb::honk::UltraComposer_; + using GoblinUltraComposer = ::bb::honk::UltraComposer_; + + using InnerFlavor = UltraFlavor; + using InnerComposer = UltraComposer; + using Instance = ::bb::honk::ProverInstance_; + using InnerBuilder = typename InnerComposer::CircuitBuilder; + using InnerCurve = bn254; + using Commitment = InnerFlavor::Commitment; + using FF = InnerFlavor::FF; + + // Types for recursive verifier circuit + // cannot do on Goblin + using OuterBuilder = GoblinUltraCircuitBuilder; + using RecursiveFlavor = ::bb::honk::flavor::UltraRecursive_; + using RecursiveVerifierInstances = ::bb::honk::VerifierInstances_; + using FoldingRecursiveVerifier = ProtoGalaxyRecursiveVerifier_; + using DeciderRecursiveVerifier = DeciderRecursiveVerifier_; + using DeciderVerifier = ::bb::honk::DeciderVerifier_; + using NativeVerifierInstances = ::bb::honk::VerifierInstances_; + using NativeFoldingVerifier = bb::honk::ProtoGalaxyVerifier_; + + // Helper for getting composer for prover/verifier of recursive (outer) circuit + template static auto get_outer_composer() + { + if constexpr (IsGoblinBuilder) { + return GoblinUltraComposer(); + } else { + return UltraComposer(); + } + } + + /** + * @brief Create a non-trivial arbitrary inner circuit, the proof of which will be recursively verified + * + * @param builder + * @param public_inputs + * @param log_num_gates + * + * TODO(https://github.com/AztecProtocol/barretenberg/issues/744): make testing utility with functionality shared + * amongst test files + */ + static void create_inner_circuit(InnerBuilder& builder, size_t log_num_gates = 10) + { + using fr_ct = InnerCurve::ScalarField; + using fq_ct = InnerCurve::BaseField; + using public_witness_ct = InnerCurve::public_witness_ct; + using witness_ct = InnerCurve::witness_ct; + using byte_array_ct = InnerCurve::byte_array_ct; + using fr = typename InnerCurve::ScalarFieldNative; + + // Create 2^log_n many add gates based on input log num gates + const size_t num_gates = 1 << log_num_gates; + for (size_t i = 0; i < num_gates; ++i) { + fr a = fr::random_element(); + uint32_t a_idx = builder.add_variable(a); + + fr b = fr::random_element(); + fr c = fr::random_element(); + fr d = a + b + c; + uint32_t b_idx = builder.add_variable(b); + uint32_t c_idx = builder.add_variable(c); + uint32_t d_idx = builder.add_variable(d); + + builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); + } + + // Define some additional non-trivial but arbitrary circuit logic + fr_ct a(public_witness_ct(&builder, fr::random_element())); + fr_ct b(public_witness_ct(&builder, fr::random_element())); + fr_ct c(public_witness_ct(&builder, fr::random_element())); + + for (size_t i = 0; i < 32; ++i) { + a = (a * b) + b + a; + a = a.madd(b, c); + } + pedersen_hash::hash({ a, b }); + byte_array_ct to_hash(&builder, "nonsense test data"); + blake3s(to_hash); + + fr bigfield_data = fr::random_element(); + fr bigfield_data_a{ bigfield_data.data[0], bigfield_data.data[1], 0, 0 }; + fr bigfield_data_b{ bigfield_data.data[2], bigfield_data.data[3], 0, 0 }; + + fq_ct big_a(fr_ct(witness_ct(&builder, bigfield_data_a.to_montgomery_form())), fr_ct(witness_ct(&builder, 0))); + fq_ct big_b(fr_ct(witness_ct(&builder, bigfield_data_b.to_montgomery_form())), fr_ct(witness_ct(&builder, 0))); + + big_a* big_b; + }; + + public: + static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } + + static std::shared_ptr fold_and_verify(const std::vector>& instances, + InnerComposer& inner_composer) + { + // Generate a folding proof + auto inner_folding_prover = inner_composer.create_folding_prover(instances); + auto inner_folding_proof = inner_folding_prover.fold_instances(); + + // Create a recursive folding verifier circuit for the folding proof of the two instances + OuterBuilder outer_folding_circuit; + FoldingRecursiveVerifier verifier{ &outer_folding_circuit }; + verifier.verify_folding_proof(inner_folding_proof.folding_data); + info("Recursive Verifier with Ultra instances: num gates = ", outer_folding_circuit.num_gates); + + // Perform native folding verification and ensure it returns the same result (either true or false) as calling + // check_circuit on the recursive folding verifier + auto native_folding_verifier = inner_composer.create_folding_verifier(); + auto native_folding_result = native_folding_verifier.verify_folding_proof(inner_folding_proof.folding_data); + EXPECT_EQ(native_folding_result, outer_folding_circuit.check_circuit()); + + // Ensure that the underlying native and recursive folding verification algorithms agree by ensuring + // the manifests produced by each agree. + auto recursive_folding_manifest = verifier.transcript->get_manifest(); + auto native_folding_manifest = native_folding_verifier.transcript->get_manifest(); + + for (size_t i = 0; i < recursive_folding_manifest.size(); ++i) { + EXPECT_EQ(recursive_folding_manifest[i], native_folding_manifest[i]); + } + + // Check for a failure flag in the recursive verifier circuit + EXPECT_EQ(outer_folding_circuit.failed(), false) << outer_folding_circuit.err(); + + return inner_folding_proof.accumulator; + } +}; +/** + * @brief Create inner circuit and call check_circuit on it + * + */ +TEST_F(ProtogalaxyRecursiveTest, InnerCircuit) +{ + InnerBuilder builder; + + create_inner_circuit(builder); + + bool result = builder.check_circuit(); + EXPECT_EQ(result, true); +} + +/** + * @brief Ensure that evaluating the perturbator in the recursive folding verifier returns the same result as + * evaluating in Polynomial class. + * + */ +TEST_F(ProtogalaxyRecursiveTest, NewEvaluate) +{ + OuterBuilder builder; + using fr_ct = bn254::ScalarField; + using fr = bn254::ScalarFieldNative; + + std::vector coeffs; + std::vector coeffs_ct; + for (size_t idx = 0; idx < 8; idx++) { + auto el = fr::random_element(); + coeffs.emplace_back(el); + coeffs_ct.emplace_back(fr_ct(&builder, el)); + } + Polynomial poly(coeffs); + fr point = fr::random_element(); + fr_ct point_ct(fr_ct(&builder, point)); + auto res1 = poly.evaluate(point); + + auto res2 = FoldingRecursiveVerifier::evaluate_perturbator(coeffs_ct, point_ct); + EXPECT_EQ(res1, res2.get_value()); +} + +/** + * @brief Tests a simple recursive fold that is valid works as expected. + * + */ +TEST_F(ProtogalaxyRecursiveTest, RecursiveFoldingTest) +{ + // Create two arbitrary circuits for the first round of folding + InnerBuilder builder1; + + create_inner_circuit(builder1); + InnerBuilder builder2; + builder2.add_public_variable(FF(1)); + create_inner_circuit(builder2); + + InnerComposer inner_composer = InnerComposer(); + auto instance1 = inner_composer.create_instance(builder1); + auto instance2 = inner_composer.create_instance(builder2); + auto instances = std::vector>{ instance1, instance2 }; + + fold_and_verify(instances, inner_composer); +} + +/** + * @brief Recursively verify two rounds of folding valid circuits and then recursive verify the final decider proof, + * make sure the verifer circuits pass check_circuit(). Ensure that the algorithm of the recursive and native verifiers + * are identical by checking the manifests + + */ +TEST_F(ProtogalaxyRecursiveTest, FullProtogalaxyRecursiveTest) +{ + + // Create two arbitrary circuits for the first round of folding + InnerBuilder builder1; + + create_inner_circuit(builder1); + InnerBuilder builder2; + builder2.add_public_variable(FF(1)); + create_inner_circuit(builder2); + + InnerComposer inner_composer = InnerComposer(); + auto instance1 = inner_composer.create_instance(builder1); + auto instance2 = inner_composer.create_instance(builder2); + auto instances = std::vector>{ instance1, instance2 }; + + auto accumulator = fold_and_verify(instances, inner_composer); + + // Create another circuit to do a second round of folding + InnerBuilder builder3; + create_inner_circuit(builder3); + auto instance3 = inner_composer.create_instance(builder3); + instances = std::vector>{ accumulator, instance3 }; + + accumulator = fold_and_verify(instances, inner_composer); + + // Create a decider proof for the relaxed instance obtained through folding + auto inner_decider_prover = inner_composer.create_decider_prover(accumulator); + auto inner_decider_proof = inner_decider_prover.construct_proof(); + + // Create a decider verifier circuit for recursively verifying the decider proof + OuterBuilder outer_decider_circuit; + DeciderRecursiveVerifier decider_verifier{ &outer_decider_circuit }; + auto pairing_points = decider_verifier.verify_proof(inner_decider_proof); + info("Decider Recursive Verifier: num gates = ", outer_decider_circuit.num_gates); + // Check for a failure flag in the recursive verifier circuit + EXPECT_EQ(outer_decider_circuit.failed(), false) << outer_decider_circuit.err(); + + // Perform native verification then perform the pairing on the outputs of the recursive + // decider verifier and check that the result agrees. + DeciderVerifier native_decider_verifier = inner_composer.create_decider_verifier(accumulator); + auto native_result = native_decider_verifier.verify_proof(inner_decider_proof); + auto recursive_result = native_decider_verifier.pcs_verification_key->pairing_check(pairing_points[0].get_value(), + pairing_points[1].get_value()); + EXPECT_EQ(native_result, recursive_result); + + // Ensure that the underlying native and recursive decider verification algorithms agree by ensuring + // the manifests produced are the same. + auto recursive_decider_manifest = decider_verifier.transcript->get_manifest(); + auto native_decider_manifest = native_decider_verifier.transcript->get_manifest(); + for (size_t i = 0; i < recursive_decider_manifest.size(); ++i) { + EXPECT_EQ(recursive_decider_manifest[i], native_decider_manifest[i]); + } + + // Construct and verify a proof of the recursive decider verifier circuit + { + auto composer = get_outer_composer(); + auto instance = composer.create_instance(outer_decider_circuit); + auto prover = composer.create_prover(instance); + auto verifier = composer.create_verifier(instance); + auto proof = prover.construct_proof(); + bool verified = verifier.verify_proof(proof); + + ASSERT(verified); + } +} + +TEST_F(ProtogalaxyRecursiveTest, TamperedDeciderProof) +{ + // Create two arbitrary circuits for the first round of folding + InnerBuilder builder1; + + create_inner_circuit(builder1); + InnerBuilder builder2; + builder2.add_public_variable(FF(1)); + create_inner_circuit(builder2); + + InnerComposer inner_composer = InnerComposer(); + auto instance1 = inner_composer.create_instance(builder1); + auto instance2 = inner_composer.create_instance(builder2); + auto instances = std::vector>{ instance1, instance2 }; + + auto accumulator = fold_and_verify(instances, inner_composer); + + // Tamper with the accumulator by changing the target sum + accumulator->target_sum = FF::random_element(); + + // Create a decider proof for the relaxed instance obtained through folding + auto inner_decider_prover = inner_composer.create_decider_prover(accumulator); + auto inner_decider_proof = inner_decider_prover.construct_proof(); + + // Create a decider verifier circuit for recursively verifying the decider proof + OuterBuilder outer_decider_circuit; + DeciderRecursiveVerifier decider_verifier{ &outer_decider_circuit }; + decider_verifier.verify_proof(inner_decider_proof); + info("Decider Recursive Verifier: num gates = ", outer_decider_circuit.num_gates); + + // We expect the decider circuit check to fail due to the bad proof + EXPECT_FALSE(outer_decider_circuit.check_circuit()); +} + +TEST_F(ProtogalaxyRecursiveTest, TamperedAccumulator) +{ + // Create two arbitrary circuits for the first round of folding + InnerBuilder builder1; + + create_inner_circuit(builder1); + InnerBuilder builder2; + builder2.add_public_variable(FF(1)); + create_inner_circuit(builder2); + + InnerComposer inner_composer = InnerComposer(); + auto instance1 = inner_composer.create_instance(builder1); + auto instance2 = inner_composer.create_instance(builder2); + auto instances = std::vector>{ instance1, instance2 }; + + auto accumulator = fold_and_verify(instances, inner_composer); + + // Create another circuit to do a second round of folding + InnerBuilder builder3; + create_inner_circuit(builder3); + auto instance3 = inner_composer.create_instance(builder3); + + // Tamper with the accumulator + instances = std::vector>{ accumulator, instance3 }; + accumulator->prover_polynomials.w_l[1] = FF::random_element(); + + // Generate a folding proof + auto inner_folding_prover = inner_composer.create_folding_prover(instances); + auto inner_folding_proof = inner_folding_prover.fold_instances(); + + // Create a recursive folding verifier circuit for the folding proof of the two instances + OuterBuilder outer_folding_circuit; + FoldingRecursiveVerifier verifier{ &outer_folding_circuit }; + verifier.verify_folding_proof(inner_folding_proof.folding_data); + EXPECT_EQ(outer_folding_circuit.check_circuit(), false); +} + +} // namespace bb::stdlib::recursion::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp index f26b44d1116..5712966f001 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp @@ -20,11 +20,6 @@ template class UltraRecursiveVerifier_ { explicit UltraRecursiveVerifier_(Builder* builder, const std::shared_ptr& native_verifier_key); - UltraRecursiveVerifier_(UltraRecursiveVerifier_&& other) = delete; - UltraRecursiveVerifier_(const UltraRecursiveVerifier_& other) = delete; - UltraRecursiveVerifier_& operator=(const UltraRecursiveVerifier_& other) = delete; - UltraRecursiveVerifier_& operator=(UltraRecursiveVerifier_&& other) = delete; - ~UltraRecursiveVerifier_() = default; // TODO(luke): Eventually this will return something like aggregation_state but I'm simplifying for now until we // determine the exact interface. Simply returns the two pairing points. diff --git a/barretenberg/cpp/src/barretenberg/stdlib/utility/utility.hpp b/barretenberg/cpp/src/barretenberg/stdlib/utility/utility.hpp index bec32c7a78c..1759f5fc412 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/utility/utility.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/utility/utility.hpp @@ -117,6 +117,10 @@ template class StdlibTypesUtility { using type = uint32_t; }; + template struct NativeType { + using type = bool; + }; + template struct NativeType { using type = FF; }; diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/protogalaxy.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/protogalaxy.test.cpp index ab2e65df40a..1f41c5325ab 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/protogalaxy.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/protogalaxy.test.cpp @@ -47,7 +47,7 @@ std::shared_ptr fold_and_verify(const std::vector& accumulator, } void decide_and_verify(std::shared_ptr& accumulator, UltraComposer& composer, bool expected_result) { - auto decider_prover = composer.create_decider_prover(accumulator, composer.commitment_key); + auto decider_prover = composer.create_decider_prover(accumulator); auto decider_verifier = composer.create_decider_verifier(accumulator); auto decision = decider_prover.construct_proof(); auto verified = decider_verifier.verify_proof(decision); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.hpp index b81d6a42178..5d2a6a71ea2 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.hpp @@ -110,8 +110,7 @@ template class UltraComposer_ { */ MergeVerifier_ create_merge_verifier() { return MergeVerifier_(); } - ProtoGalaxyProver_ create_folding_prover(const std::vector>& instances, - const std::shared_ptr& commitment_key) + ProtoGalaxyProver_ create_folding_prover(const std::vector>& instances) { ProtoGalaxyProver_ output_state(instances, commitment_key);