From 61cae4a2c9eec2367215c81918dd8ec0a94a1940 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Tue, 12 Sep 2023 21:28:49 +0000 Subject: [PATCH] splitting goblin builder into its own class --- .../composer/goblin_ultra_composer.test.cpp | 6 +- .../honk/composer/ultra_composer.cpp | 5 +- .../barretenberg/honk/flavor/goblin_ultra.hpp | 4 +- .../honk/flavor/goblin_ultra_recursive.hpp | 434 ++++++++++++++++++ .../barretenberg/honk/pcs/gemini/gemini.hpp | 6 +- .../cpp/src/barretenberg/honk/pcs/kzg/kzg.hpp | 4 +- .../barretenberg/honk/pcs/shplonk/shplonk.hpp | 4 +- .../sumcheck/relation_correctness.test.cpp | 2 +- .../goblin_ultra_circuit_builder.cpp | 163 +++++++ .../goblin_ultra_circuit_builder.hpp | 115 +++++ .../goblin_ultra_circuit_builder.test.cpp | 7 +- .../circuit_builder/ultra_circuit_builder.cpp | 141 ------ .../circuit_builder/ultra_circuit_builder.hpp | 43 +- .../proof_system/flavor/flavor.hpp | 3 +- .../stdlib/primitives/biggroup/biggroup.hpp | 1 - .../biggroup/biggroup_goblin.test.cpp | 5 +- .../primitives/biggroup/biggroup_impl.hpp | 4 +- .../circuit_builders/circuit_builders.hpp | 28 +- .../circuit_builders/circuit_builders_fwd.hpp | 22 +- .../verifier/ultra_recursive_verifier.cpp | 23 +- .../verifier/ultra_recursive_verifier.hpp | 13 +- .../recursion/honk/verifier/verifier.test.cpp | 25 +- 22 files changed, 809 insertions(+), 249 deletions(-) create mode 100644 circuits/cpp/barretenberg/cpp/src/barretenberg/honk/flavor/goblin_ultra_recursive.hpp create mode 100644 circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.cpp create mode 100644 circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/composer/goblin_ultra_composer.test.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/composer/goblin_ultra_composer.test.cpp index afda145a755..3215c146cb7 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/composer/goblin_ultra_composer.test.cpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/composer/goblin_ultra_composer.test.cpp @@ -94,7 +94,7 @@ TEST_F(GoblinUltraHonkComposerTests, SingleCircuit) // Add mock data to op queue to simulate interaction with a previous circuit populate_ecc_op_queue_with_mock_data(op_queue); - auto builder = UltraCircuitBuilder(op_queue); + auto builder = GoblinUltraCircuitBuilder(op_queue); generate_test_circuit(builder); @@ -124,7 +124,7 @@ TEST_F(GoblinUltraHonkComposerTests, MultipleCircuits) // Construct first circuit and its proof { - auto builder = UltraCircuitBuilder(op_queue); + auto builder = GoblinUltraCircuitBuilder(op_queue); generate_test_circuit(builder); expected_op_queue_size += builder.num_ecc_op_gates; @@ -139,7 +139,7 @@ TEST_F(GoblinUltraHonkComposerTests, MultipleCircuits) // Construct second circuit { - auto builder = UltraCircuitBuilder(op_queue); + auto builder = GoblinUltraCircuitBuilder(op_queue); generate_test_circuit(builder); expected_op_queue_size += builder.num_ecc_op_gates; diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/composer/ultra_composer.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/composer/ultra_composer.cpp index ca1bc657417..36b1c490852 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/composer/ultra_composer.cpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/composer/ultra_composer.cpp @@ -24,7 +24,10 @@ void UltraComposer_::compute_circuit_size_parameters(CircuitBuilder& cir // Get num conventional gates, num public inputs and num Goblin style ECC op gates const size_t num_gates = circuit_constructor.num_gates; num_public_inputs = circuit_constructor.public_inputs.size(); - num_ecc_op_gates = circuit_constructor.num_ecc_op_gates; + num_ecc_op_gates = 0; + if constexpr (IsGoblinFlavor) { + num_ecc_op_gates = circuit_constructor.num_ecc_op_gates; + } // minimum circuit size due to the length of lookups plus tables const size_t minimum_circuit_size_due_to_lookups = tables_size + lookups_size + num_zero_rows; diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/flavor/goblin_ultra.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/flavor/goblin_ultra.hpp index d5d61175e0d..a8b6f3326b1 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/flavor/goblin_ultra.hpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/flavor/goblin_ultra.hpp @@ -2,7 +2,7 @@ #include "barretenberg/honk/pcs/kzg/kzg.hpp" #include "barretenberg/honk/transcript/transcript.hpp" #include "barretenberg/polynomials/univariate.hpp" -#include "barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp" +#include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" #include "barretenberg/proof_system/flavor/flavor.hpp" #include "barretenberg/proof_system/relations/auxiliary_relation.hpp" #include "barretenberg/proof_system/relations/ecc_op_queue_relation.hpp" @@ -16,7 +16,7 @@ namespace proof_system::honk::flavor { class GoblinUltra { public: - using CircuitBuilder = UltraCircuitBuilder; + using CircuitBuilder = GoblinUltraCircuitBuilder; using Curve = curve::BN254; using PCS = pcs::kzg::KZG; using GroupElement = Curve::Element; diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/flavor/goblin_ultra_recursive.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/flavor/goblin_ultra_recursive.hpp new file mode 100644 index 00000000000..c8ea61a3358 --- /dev/null +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/flavor/goblin_ultra_recursive.hpp @@ -0,0 +1,434 @@ +#pragma once +#include "barretenberg/ecc/curves/bn254/g1.hpp" +#include "barretenberg/honk/pcs/commitment_key.hpp" +#include "barretenberg/honk/pcs/kzg/kzg.hpp" +#include "barretenberg/polynomials/barycentric.hpp" +#include "barretenberg/polynomials/univariate.hpp" + +#include "barretenberg/honk/transcript/transcript.hpp" +#include "barretenberg/polynomials/evaluation_domain.hpp" +#include "barretenberg/polynomials/polynomial.hpp" +#include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" +#include "barretenberg/proof_system/flavor/flavor.hpp" +#include "barretenberg/proof_system/relations/auxiliary_relation.hpp" +#include "barretenberg/proof_system/relations/elliptic_relation.hpp" +#include "barretenberg/proof_system/relations/gen_perm_sort_relation.hpp" +#include "barretenberg/proof_system/relations/lookup_relation.hpp" +#include "barretenberg/proof_system/relations/permutation_relation.hpp" +#include "barretenberg/proof_system/relations/ultra_arithmetic_relation.hpp" +#include "barretenberg/srs/factories/crs_factory.hpp" +#include +#include +#include +#include +#include +#include + +#include "barretenberg/stdlib/primitives/curves/bn254.hpp" +#include "barretenberg/stdlib/primitives/field/field.hpp" + +namespace proof_system::honk::flavor { + +/** + * @brief The recursive counterpart to the "native" Goblin Ultra flavor. + * @details This flavor can be used to instantiate a recursive Ultra Honk verifier for a proof created using the + * conventional Ultra flavor. It is similar in structure to its native counterpart with two main differences: 1) the + * curve types are stdlib types (e.g. field_t instead of field) and 2) it does not specify any Prover related types + * (e.g. Polynomial, ExtendedEdges, etc.) since we do not emulate prover computation in circuits, i.e. it only makes + * sense to instantiate a Verifier with this flavor. + * + */ +class GoblinUltraRecursive { + public: + using CircuitBuilder = GoblinUltraCircuitBuilder; + using Curve = plonk::stdlib::bn254; + using GroupElement = Curve::Element; + using Commitment = Curve::Element; + using CommitmentHandle = Curve::Element; + using FF = Curve::ScalarField; + + // Note(luke): Eventually this may not be needed at all + using VerifierCommitmentKey = pcs::VerifierCommitmentKey; + + static constexpr size_t NUM_WIRES = CircuitBuilder::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 = 48; // 43 (UH) + 4 op wires + 1 op wire "selector" + // 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 = 26; // 25 (UH) + 1 op wire "selector" + // The total number of witness entities not including shifts. + static constexpr size_t NUM_WITNESS_ENTITIES = 15; // 11 (UH) + 4 op wires + + // define the tuple of Relations that comprise the Sumcheck relation + using Relations = std::tuple, + proof_system::UltraPermutationRelation, + proof_system::LookupRelation, + proof_system::GenPermSortRelation, + proof_system::EllipticRelation, + proof_system::AuxiliaryRelation, + proof_system::EccOpQueueRelation>; + + static constexpr size_t MAX_RELATION_LENGTH = get_max_relation_length(); + + // MAX_RANDOM_RELATION_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 MAX_RANDOM_RELATION_LENGTH = MAX_RELATION_LENGTH + 1; + static constexpr size_t NUM_RELATIONS = std::tuple_size::value; + + // define the container for storing the univariate contribution from each relation in Sumcheck + using RelationUnivariates = decltype(create_relation_univariates_container()); + using RelationValues = decltype(create_relation_values_container()); + + private: + template + /** + * @brief A base class labelling precomputed entities and (ordered) subsets of interest. + * @details Used to build the proving key and verification key. + */ + class PrecomputedEntities : public PrecomputedEntities_ { + public: + DataType& q_m = std::get<0>(this->_data); + DataType& q_c = std::get<1>(this->_data); + DataType& q_l = std::get<2>(this->_data); + DataType& q_r = std::get<3>(this->_data); + DataType& q_o = std::get<4>(this->_data); + DataType& q_4 = std::get<5>(this->_data); + DataType& q_arith = std::get<6>(this->_data); + DataType& q_sort = std::get<7>(this->_data); + DataType& q_elliptic = std::get<8>(this->_data); + DataType& q_aux = std::get<9>(this->_data); + DataType& q_lookup = std::get<10>(this->_data); + DataType& sigma_1 = std::get<11>(this->_data); + DataType& sigma_2 = std::get<12>(this->_data); + DataType& sigma_3 = std::get<13>(this->_data); + DataType& sigma_4 = std::get<14>(this->_data); + DataType& id_1 = std::get<15>(this->_data); + DataType& id_2 = std::get<16>(this->_data); + DataType& id_3 = std::get<17>(this->_data); + DataType& id_4 = std::get<18>(this->_data); + DataType& table_1 = std::get<19>(this->_data); + DataType& table_2 = std::get<20>(this->_data); + DataType& table_3 = std::get<21>(this->_data); + DataType& table_4 = std::get<22>(this->_data); + DataType& lagrange_first = std::get<23>(this->_data); + DataType& lagrange_last = std::get<24>(this->_data); + DataType& lagrange_ecc_op = std::get<25>(this->_data); // indicator poly for ecc op gates + + static constexpr CircuitType CIRCUIT_TYPE = CircuitBuilder::CIRCUIT_TYPE; + + std::vector get_selectors() override + { + return { q_m, q_c, q_l, q_r, q_o, q_4, q_arith, q_sort, q_elliptic, q_aux, q_lookup }; + }; + std::vector get_sigma_polynomials() override { return { sigma_1, sigma_2, sigma_3, sigma_4 }; }; + std::vector get_id_polynomials() override { return { id_1, id_2, id_3, id_4 }; }; + + std::vector get_table_polynomials() { return { table_1, table_2, table_3, table_4 }; }; + }; + + /** + * @brief Container for all witness polynomials used/constructed by the prover. + * @details Shifts are not included here since they do not occupy their own memory. + */ + template + class WitnessEntities : public WitnessEntities_ { + public: + DataType& w_l = std::get<0>(this->_data); + DataType& w_r = std::get<1>(this->_data); + DataType& w_o = std::get<2>(this->_data); + DataType& w_4 = std::get<3>(this->_data); + DataType& sorted_1 = std::get<4>(this->_data); + DataType& sorted_2 = std::get<5>(this->_data); + DataType& sorted_3 = std::get<6>(this->_data); + DataType& sorted_4 = std::get<7>(this->_data); + DataType& sorted_accum = std::get<8>(this->_data); + DataType& z_perm = std::get<9>(this->_data); + DataType& z_lookup = std::get<10>(this->_data); + DataType& ecc_op_wire_1 = std::get<11>(this->_data); + DataType& ecc_op_wire_2 = std::get<12>(this->_data); + DataType& ecc_op_wire_3 = std::get<13>(this->_data); + DataType& ecc_op_wire_4 = std::get<14>(this->_data); + + std::vector get_wires() override { return { w_l, w_r, w_o, w_4 }; }; + std::vector get_ecc_op_wires() + { + return { ecc_op_wire_1, ecc_op_wire_2, ecc_op_wire_3, ecc_op_wire_4 }; + }; + // The sorted concatenations of table and witness data needed for plookup. + std::vector get_sorted_polynomials() { return { sorted_1, sorted_2, sorted_3, sorted_4 }; }; + }; + + /** + * @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 + * @details Used to build containers for: the prover's polynomial during sumcheck; the sumcheck's folded + * polynomials; the univariates consturcted during during sumcheck; the evaluations produced by sumcheck. + * + * Symbolically we have: AllEntities = PrecomputedEntities + WitnessEntities + "ShiftedEntities". It could be + * implemented as such, but we have this now. + */ + template + class AllEntities : public AllEntities_ { + public: + DataType& q_c = std::get<0>(this->_data); + DataType& q_l = std::get<1>(this->_data); + DataType& q_r = std::get<2>(this->_data); + DataType& q_o = std::get<3>(this->_data); + DataType& q_4 = std::get<4>(this->_data); + DataType& q_m = std::get<5>(this->_data); + DataType& q_arith = std::get<6>(this->_data); + DataType& q_sort = std::get<7>(this->_data); + DataType& q_elliptic = std::get<8>(this->_data); + DataType& q_aux = std::get<9>(this->_data); + DataType& q_lookup = std::get<10>(this->_data); + DataType& sigma_1 = std::get<11>(this->_data); + DataType& sigma_2 = std::get<12>(this->_data); + DataType& sigma_3 = std::get<13>(this->_data); + DataType& sigma_4 = std::get<14>(this->_data); + DataType& id_1 = std::get<15>(this->_data); + DataType& id_2 = std::get<16>(this->_data); + DataType& id_3 = std::get<17>(this->_data); + DataType& id_4 = std::get<18>(this->_data); + DataType& table_1 = std::get<19>(this->_data); + DataType& table_2 = std::get<20>(this->_data); + DataType& table_3 = std::get<21>(this->_data); + DataType& table_4 = std::get<22>(this->_data); + DataType& lagrange_first = std::get<23>(this->_data); + DataType& lagrange_last = std::get<24>(this->_data); + DataType& lagrange_ecc_op = std::get<25>(this->_data); + DataType& w_l = std::get<26>(this->_data); + DataType& w_r = std::get<27>(this->_data); + DataType& w_o = std::get<28>(this->_data); + DataType& w_4 = std::get<29>(this->_data); + DataType& sorted_accum = std::get<30>(this->_data); + DataType& z_perm = std::get<31>(this->_data); + DataType& z_lookup = std::get<32>(this->_data); + DataType& ecc_op_wire_1 = std::get<33>(this->_data); + DataType& ecc_op_wire_2 = std::get<34>(this->_data); + DataType& ecc_op_wire_3 = std::get<35>(this->_data); + DataType& ecc_op_wire_4 = std::get<36>(this->_data); + DataType& table_1_shift = std::get<37>(this->_data); + DataType& table_2_shift = std::get<38>(this->_data); + DataType& table_3_shift = std::get<39>(this->_data); + DataType& table_4_shift = std::get<40>(this->_data); + DataType& w_l_shift = std::get<41>(this->_data); + DataType& w_r_shift = std::get<42>(this->_data); + DataType& w_o_shift = std::get<43>(this->_data); + DataType& w_4_shift = std::get<44>(this->_data); + DataType& sorted_accum_shift = std::get<45>(this->_data); + DataType& z_perm_shift = std::get<46>(this->_data); + DataType& z_lookup_shift = std::get<47>(this->_data); + + std::vector get_wires() override { return { w_l, w_r, w_o, w_4 }; }; + std::vector get_ecc_op_wires() + { + return { ecc_op_wire_1, ecc_op_wire_2, ecc_op_wire_3, ecc_op_wire_4 }; + }; + // Gemini-specific getters. + std::vector get_unshifted() override + { + return { q_c, q_l, + q_r, q_o, + q_4, q_m, + 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, lagrange_ecc_op, + w_l, w_r, + w_o, w_4, + sorted_accum, z_perm, + z_lookup, ecc_op_wire_1, + ecc_op_wire_2, ecc_op_wire_3, + ecc_op_wire_4 }; + }; + std::vector get_to_be_shifted() override + { + return { table_1, table_2, table_3, table_4, w_l, w_r, w_o, w_4, sorted_accum, z_perm, z_lookup }; + }; + std::vector get_shifted() override + { + return { table_1_shift, table_2_shift, table_3_shift, table_4_shift, w_l_shift, w_r_shift, + w_o_shift, w_4_shift, sorted_accum_shift, z_perm_shift, z_lookup_shift }; + }; + + AllEntities() = default; + + AllEntities(const AllEntities& other) + : AllEntities_(other){}; + + AllEntities(AllEntities&& other) + : AllEntities_(other){}; + + AllEntities& operator=(const AllEntities& other) + { + if (this == &other) { + return *this; + } + AllEntities_::operator=(other); + return *this; + } + + AllEntities& operator=(AllEntities&& other) + { + AllEntities_::operator=(other); + return *this; + } + + ~AllEntities() = default; + }; + + public: + /** + * @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 : public VerificationKey_> { + public: + /** + * @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, auto native_key) + : VerificationKey_>(native_key->circuit_size, + native_key->num_public_inputs) + { + q_m = Commitment::from_witness(builder, native_key->q_m); + q_l = Commitment::from_witness(builder, native_key->q_l); + q_r = Commitment::from_witness(builder, native_key->q_r); + q_o = Commitment::from_witness(builder, native_key->q_o); + q_4 = Commitment::from_witness(builder, native_key->q_4); + q_c = Commitment::from_witness(builder, native_key->q_c); + q_arith = Commitment::from_witness(builder, native_key->q_arith); + q_sort = Commitment::from_witness(builder, native_key->q_sort); + q_elliptic = Commitment::from_witness(builder, native_key->q_elliptic); + q_aux = Commitment::from_witness(builder, native_key->q_aux); + q_lookup = Commitment::from_witness(builder, native_key->q_lookup); + sigma_1 = Commitment::from_witness(builder, native_key->sigma_1); + sigma_2 = Commitment::from_witness(builder, native_key->sigma_2); + sigma_3 = Commitment::from_witness(builder, native_key->sigma_3); + sigma_4 = Commitment::from_witness(builder, native_key->sigma_4); + id_1 = Commitment::from_witness(builder, native_key->id_1); + id_2 = Commitment::from_witness(builder, native_key->id_2); + id_3 = Commitment::from_witness(builder, native_key->id_3); + id_4 = Commitment::from_witness(builder, native_key->id_4); + table_1 = Commitment::from_witness(builder, native_key->table_1); + table_2 = Commitment::from_witness(builder, native_key->table_2); + table_3 = Commitment::from_witness(builder, native_key->table_3); + table_4 = Commitment::from_witness(builder, native_key->table_4); + lagrange_first = Commitment::from_witness(builder, native_key->lagrange_first); + lagrange_last = Commitment::from_witness(builder, native_key->lagrange_last); + lagrange_ecc_op = Commitment::from_witness(builder, native_key->lagrange_ecc_op); + }; + }; + + /** + * @brief A container for the polynomials evaluations produced during sumcheck, which are purported to be the + * evaluations of polynomials committed in earlier rounds. + */ + class ClaimedEvaluations : public AllEntities { + public: + using Base = AllEntities; + using Base::Base; + ClaimedEvaluations(std::array _data_in) { this->_data = _data_in; } + }; + + /** + * @brief A container for commitment labels. + * @note It's debatable whether this should inherit from AllEntities. since most entries are not strictly needed. It + * has, however, been useful during debugging to have these labels available. + * + */ + class CommitmentLabels : public AllEntities { + public: + CommitmentLabels() + { + w_l = "W_L"; + w_r = "W_R"; + w_o = "W_O"; + w_4 = "W_4"; + z_perm = "Z_PERM"; + z_lookup = "Z_LOOKUP"; + sorted_accum = "SORTED_ACCUM"; + ecc_op_wire_1 = "ECC_OP_WIRE_1"; + ecc_op_wire_2 = "ECC_OP_WIRE_2"; + ecc_op_wire_3 = "ECC_OP_WIRE_3"; + ecc_op_wire_4 = "ECC_OP_WIRE_4"; + + // The ones beginning with "__" are only used for debugging + q_c = "__Q_C"; + q_l = "__Q_L"; + q_r = "__Q_R"; + q_o = "__Q_O"; + q_4 = "__Q_4"; + q_m = "__Q_M"; + q_arith = "__Q_ARITH"; + q_sort = "__Q_SORT"; + q_elliptic = "__Q_ELLIPTIC"; + q_aux = "__Q_AUX"; + q_lookup = "__Q_LOOKUP"; + sigma_1 = "__SIGMA_1"; + sigma_2 = "__SIGMA_2"; + sigma_3 = "__SIGMA_3"; + sigma_4 = "__SIGMA_4"; + id_1 = "__ID_1"; + id_2 = "__ID_2"; + id_3 = "__ID_3"; + id_4 = "__ID_4"; + table_1 = "__TABLE_1"; + table_2 = "__TABLE_2"; + table_3 = "__TABLE_3"; + table_4 = "__TABLE_4"; + lagrange_first = "__LAGRANGE_FIRST"; + lagrange_last = "__LAGRANGE_LAST"; + }; + }; + + class VerifierCommitments : public AllEntities { + public: + VerifierCommitments(std::shared_ptr verification_key) + { + q_m = verification_key->q_m; + 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_c = verification_key->q_c; + 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; + lagrange_ecc_op = verification_key->lagrange_ecc_op; + } + }; +}; + +} // namespace proof_system::honk::flavor diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/pcs/gemini/gemini.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/pcs/gemini/gemini.hpp index f46d55ae71f..57754ad1655 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/pcs/gemini/gemini.hpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/pcs/gemini/gemini.hpp @@ -111,7 +111,7 @@ template class GeminiProver_ { const Fr& r_challenge); }; // namespace proof_system::honk::pcs::gemini -template class GeminiVerifier_ { +template class GeminiVerifier_ { using Fr = typename Curve::ScalarField; using GroupElement = typename Curve::Element; using Commitment = typename Curve::AffineElement; @@ -247,8 +247,8 @@ template class GeminiVerifier_ { // TODO(#707): these batch muls include the use of 1 as a scalar. This is handled appropriately as a non-mul // (add-accumulate) in the goblin batch_mul but is done inefficiently as a scalar mul in the conventional // emulated batch mul. - C0_r_pos = GroupElement::template batch_mul(commitments, { one, r_inv }); - C0_r_neg = GroupElement::template batch_mul(commitments, { one, -r_inv }); + C0_r_pos = GroupElement::batch_mul(commitments, { one, r_inv }); + C0_r_neg = GroupElement::batch_mul(commitments, { one, -r_inv }); } else { C0_r_pos = batched_f; C0_r_neg = batched_f; diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/pcs/kzg/kzg.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/pcs/kzg/kzg.hpp index ab2c8d8bb7a..6bd41ac7bef 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/pcs/kzg/kzg.hpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/pcs/kzg/kzg.hpp @@ -11,7 +11,7 @@ namespace proof_system::honk::pcs::kzg { -template class KZG { +template class KZG { using CK = CommitmentKey; using VK = VerifierCommitmentKey; using Fr = typename Curve::ScalarField; @@ -88,7 +88,7 @@ template class KZG { auto one = Fr(builder, 1); std::vector commitments = { claim.commitment, quotient_commitment }; std::vector scalars = { one, claim.opening_pair.challenge }; - P_0 = GroupElement::template batch_mul(commitments, scalars); + P_0 = GroupElement::batch_mul(commitments, scalars); // Note: This implementation assumes the evaluation is zero (as is the case for shplonk). ASSERT(claim.opening_pair.evaluation.get_value() == 0); } else { diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/pcs/shplonk/shplonk.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/pcs/shplonk/shplonk.hpp index 17c452471dc..784641012e1 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/pcs/shplonk/shplonk.hpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/pcs/shplonk/shplonk.hpp @@ -145,7 +145,7 @@ template class ShplonkProver_ { * @brief Shplonk Verifier * */ -template class ShplonkVerifier_ { +template class ShplonkVerifier_ { using Fr = typename Curve::ScalarField; using GroupElement = typename Curve::Element; using Commitment = typename Curve::AffineElement; @@ -231,7 +231,7 @@ template class ShplonkVerifier_ { scalars.emplace_back(G_commitment_constant); // [G] += G₀⋅[1] = [G] + (∑ⱼ ρʲ ⋅ vⱼ / ( r − xⱼ ))⋅[1] - G_commitment = GroupElement::template batch_mul(commitments, scalars); + G_commitment = GroupElement::batch_mul(commitments, scalars); } else { // [G] = [Q] - ∑ⱼ ρʲ / ( r − xⱼ )⋅[fⱼ] + G₀⋅[1] diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/sumcheck/relation_correctness.test.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/sumcheck/relation_correctness.test.cpp index ea98cc35c46..bd9573754d7 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/sumcheck/relation_correctness.test.cpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/sumcheck/relation_correctness.test.cpp @@ -415,7 +415,7 @@ TEST_F(RelationCorrectnessTests, GoblinUltraRelationCorrectness) // Create a composer and then add an assortment of gates designed to ensure that the constraint(s) represented // by each relation are non-trivially exercised. - auto builder = UltraCircuitBuilder(); + auto builder = GoblinUltraCircuitBuilder(); // Create an assortment of representative gates create_some_add_gates(builder); diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.cpp new file mode 100644 index 00000000000..4fd5679c6fe --- /dev/null +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.cpp @@ -0,0 +1,163 @@ +#include "goblin_ultra_circuit_builder.hpp" +#include +#include +#include + +using namespace barretenberg; + +namespace proof_system { + +template void GoblinUltraCircuitBuilder_::finalize_circuit() +{ + UltraCircuitBuilder_::finalize_circuit(); + + // Set internally the current and previous size of the aggregate op queue transcript + op_queue->set_size_data(); +} + +/** + * @brief Ensure all polynomials have at least one non-zero coefficient to avoid commiting to the zero-polynomial + * + * @param in Structure containing variables and witness selectors + */ +// TODO(#423): This function adds valid (but arbitrary) gates to ensure that the circuit which includes +// them will not result in any zero-polynomials. It also ensures that the first coefficient of the wire +// polynomials is zero, which is required for them to be shiftable. +// TODO(luke): Add ECC op gate to ensure op wires are non-zero? +template void GoblinUltraCircuitBuilder_::add_gates_to_ensure_all_polys_are_non_zero() +{ + UltraCircuitBuilder_::add_gates_to_ensure_all_polys_are_non_zero(); +} + +/** + * @brief Add gates for simple point addition without scalar and compute corresponding op natively + * + * @param point + */ +template +ecc_op_tuple GoblinUltraCircuitBuilder_::queue_ecc_add_accum(const barretenberg::g1::affine_element& point) +{ + // Add raw op to queue + op_queue->add_accumulate(point); + + // Decompose operation inputs into width-four form and add ecc op gates + auto op_tuple = decompose_ecc_operands(add_accum_op_idx, point); + populate_ecc_op_wires(op_tuple); + + return op_tuple; +} + +/** + * @brief Add gates for point mul and add and compute corresponding op natively + * + * @tparam FF + * @param point + * @param scalar + * @return ecc_op_tuple encoding the point and scalar inputs to the mul accum + */ +template +ecc_op_tuple GoblinUltraCircuitBuilder_::queue_ecc_mul_accum(const barretenberg::g1::affine_element& point, + const FF& scalar) +{ + // Add raw op to op queue + op_queue->mul_accumulate(point, scalar); + + // Decompose operation inputs into width-four form and add ecc op gates + auto op_tuple = decompose_ecc_operands(mul_accum_op_idx, point, scalar); + populate_ecc_op_wires(op_tuple); + + return op_tuple; +} + +/** + * @brief Add point equality gates + * + * @return ecc_op_tuple encoding the point to which equality has been asserted + */ +template ecc_op_tuple GoblinUltraCircuitBuilder_::queue_ecc_eq() +{ + // Add raw op to op queue + auto point = op_queue->eq(); + + // Decompose operation inputs into width-four form and add ecc op gates + auto op_tuple = decompose_ecc_operands(equality_op_idx, point); + populate_ecc_op_wires(op_tuple); + + return op_tuple; +} + +/** + * @brief Decompose ecc operands into components, add corresponding variables, return ecc op tuple + * + * @param op_idx Index of op code in variables array + * @param point + * @param scalar + * @return ecc_op_tuple Tuple of indices into variables array used to construct pair of ecc op gates + */ +template +ecc_op_tuple GoblinUltraCircuitBuilder_::decompose_ecc_operands(uint32_t op_idx, const g1::affine_element& point, const FF& scalar) +{ + // Decompose point coordinates (Fq) into hi-lo chunks (Fr) + const size_t CHUNK_SIZE = 2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS; + auto x_256 = uint256_t(point.x); + auto y_256 = uint256_t(point.y); + auto x_lo = FF(x_256.slice(0, CHUNK_SIZE)); + auto x_hi = FF(x_256.slice(CHUNK_SIZE, CHUNK_SIZE * 2)); + auto y_lo = FF(y_256.slice(0, CHUNK_SIZE)); + auto y_hi = FF(y_256.slice(CHUNK_SIZE, CHUNK_SIZE * 2)); + + // Split scalar into 128 bit endomorphism scalars + FF z_1 = 0; + FF z_2 = 0; + auto converted = scalar.from_montgomery_form(); + FF::split_into_endomorphism_scalars(converted, z_1, z_2); + z_1 = z_1.to_montgomery_form(); + z_2 = z_2.to_montgomery_form(); + + // Populate ultra ops in OpQueue with the decomposed operands + op_queue->ultra_ops[0].emplace_back(this->variables[op_idx]); + op_queue->ultra_ops[1].emplace_back(x_lo); + op_queue->ultra_ops[2].emplace_back(x_hi); + op_queue->ultra_ops[3].emplace_back(y_lo); + + op_queue->ultra_ops[0].emplace_back(this->variables[op_idx]); + op_queue->ultra_ops[1].emplace_back(y_hi); + op_queue->ultra_ops[2].emplace_back(z_1); + op_queue->ultra_ops[3].emplace_back(z_2); + + // Add variables for decomposition and get indices needed for op wires + auto x_lo_idx = this->add_variable(x_lo); + auto x_hi_idx = this->add_variable(x_hi); + auto y_lo_idx = this->add_variable(y_lo); + auto y_hi_idx = this->add_variable(y_hi); + auto z_1_idx = this->add_variable(z_1); + auto z_2_idx = this->add_variable(z_2); + + return { op_idx, x_lo_idx, x_hi_idx, y_lo_idx, y_hi_idx, z_1_idx, z_2_idx }; +} + +/** + * @brief Add ecc operation to queue + * + * @param in Variables array indices corresponding to operation inputs + * @note We dont explicitly set values for the selectors here since their values are fully determined by + * num_ecc_op_gates. E.g. in the composer we can reconstruct q_ecc_op as the indicator on the first num_ecc_op_gates + * indices. All other selectors are simply 0 on this domain. + */ +template void GoblinUltraCircuitBuilder_::populate_ecc_op_wires(const ecc_op_tuple& in) +{ + ecc_op_wire_1.emplace_back(in.op); + ecc_op_wire_2.emplace_back(in.x_lo); + ecc_op_wire_3.emplace_back(in.x_hi); + ecc_op_wire_4.emplace_back(in.y_lo); + + ecc_op_wire_1.emplace_back(in.op); // TODO(luke): second op val is sort of a dummy. use "op" again? + ecc_op_wire_2.emplace_back(in.y_hi); + ecc_op_wire_3.emplace_back(in.z_1); + ecc_op_wire_4.emplace_back(in.z_2); + + num_ecc_op_gates += 2; +}; + +template class GoblinUltraCircuitBuilder_; +} // namespace proof_system \ No newline at end of file diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp new file mode 100644 index 00000000000..8952930ccc4 --- /dev/null +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp @@ -0,0 +1,115 @@ +#pragma once +#include "barretenberg/plonk/proof_system/constants.hpp" +#include "barretenberg/plonk/proof_system/types/polynomial_manifest.hpp" +#include "barretenberg/plonk/proof_system/types/prover_settings.hpp" +#include "barretenberg/polynomials/polynomial.hpp" +#include "barretenberg/proof_system/arithmetization/arithmetization.hpp" +#include "barretenberg/proof_system/op_queue/ecc_op_queue.hpp" +#include "barretenberg/proof_system/plookup_tables/plookup_tables.hpp" +#include "barretenberg/proof_system/plookup_tables/types.hpp" +#include "barretenberg/proof_system/types/merkle_hash_type.hpp" +#include "barretenberg/proof_system/types/pedersen_commitment_type.hpp" +#include "ultra_circuit_builder.hpp" +#include + +namespace proof_system { + +using namespace barretenberg; + +template class GoblinUltraCircuitBuilder_ : public UltraCircuitBuilder_ { + public: + static constexpr std::string_view NAME_STRING = "GoblinUltraArithmetization"; + static constexpr CircuitType CIRCUIT_TYPE = CircuitType::ULTRA; + static constexpr size_t DEFAULT_NON_NATIVE_FIELD_LIMB_BITS = + UltraCircuitBuilder_::DEFAULT_NON_NATIVE_FIELD_LIMB_BITS; + + size_t num_ecc_op_gates = 0; // number of ecc op "gates" (rows); these are placed at the start of the circuit + + // Stores record of ecc operations and performs corresponding native operations internally + std::shared_ptr op_queue; + + // Indices for constant variables corresponding to ECCOpQueue op codes + uint32_t null_op_idx; + uint32_t add_accum_op_idx; + uint32_t mul_accum_op_idx; + uint32_t equality_op_idx; + + using WireVector = std::vector>; + + // Wires storing ecc op queue data; values are indices into the variables array + std::array::NUM_WIRES> ecc_op_wires; + + WireVector& ecc_op_wire_1 = std::get<0>(ecc_op_wires); + WireVector& ecc_op_wire_2 = std::get<1>(ecc_op_wires); + WireVector& ecc_op_wire_3 = std::get<2>(ecc_op_wires); + WireVector& ecc_op_wire_4 = std::get<3>(ecc_op_wires); + + ecc_op_tuple queue_ecc_add_accum(const g1::affine_element& point); + ecc_op_tuple queue_ecc_mul_accum(const g1::affine_element& point, const FF& scalar); + ecc_op_tuple queue_ecc_eq(); + + private: + void populate_ecc_op_wires(const ecc_op_tuple& in); + ecc_op_tuple decompose_ecc_operands(uint32_t op, const g1::affine_element& point, const FF& scalar = FF::zero()); + + public: + + GoblinUltraCircuitBuilder_(const size_t size_hint = 0, std::shared_ptr op_queue_in = std::make_shared()) + : UltraCircuitBuilder_(size_hint) + , op_queue(op_queue_in) + { + // Set indices to constants corresponding to Goblin ECC op codes + null_op_idx = this->zero_idx; + add_accum_op_idx = this->put_constant_variable(FF(EccOpCode::ADD_ACCUM)); + mul_accum_op_idx = this->put_constant_variable(FF(EccOpCode::MUL_ACCUM)); + equality_op_idx = this->put_constant_variable(FF(EccOpCode::EQUALITY)); + }; + GoblinUltraCircuitBuilder_(std::shared_ptr op_queue_in) + : GoblinUltraCircuitBuilder_(0, op_queue_in) {} + + void finalize_circuit(); + void add_gates_to_ensure_all_polys_are_non_zero(); + + size_t get_num_constant_gates() const override { return 0; } + + /** + * @brief Get the final number of gates in a circuit, which consists of the sum of: + * 1) Current number number of actual gates + * 2) Number of public inputs, as we'll need to add a gate for each of them + * 3) Number of Rom array-associated gates + * 4) Number of range-list associated gates + * 5) Number of non-native field multiplication gates. + * + * @return size_t + */ + size_t get_num_gates() const override + { + auto num_ultra_gates = UltraCircuitBuilder_::get_num_gates(); + return num_ultra_gates + num_ecc_op_gates; + } + + /**x + * @brief Print the number and composition of gates in the circuit + * + */ + virtual void print_num_gates() const override + { + size_t count = 0; + size_t rangecount = 0; + size_t romcount = 0; + size_t ramcount = 0; + size_t nnfcount = 0; + UltraCircuitBuilder_::get_num_gates_split_into_components(count, rangecount, romcount, ramcount, nnfcount); + + size_t total = count + romcount + ramcount + rangecount + num_ecc_op_gates; + std::cout << "gates = " << total << " (arith " << count << ", rom " << romcount << ", ram " << ramcount + << ", range " << rangecount << ", non native field gates " << nnfcount << ", goblin ecc op gates " + << num_ecc_op_gates << "), pubinp = " << this->public_inputs.size() << std::endl; + } +}; +extern template class GoblinUltraCircuitBuilder_; +// TODO: template plookup to be able to be able to have UltraCircuitBuilder on Grumpkin +// extern template class UltraCircuitBuilder_; +using GoblinUltraCircuitBuilder = GoblinUltraCircuitBuilder_; +// using UltraGrumpkinCircuitBuilder = UltraCircuitBuilder_; +} // namespace proof_system diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.test.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.test.cpp index 5766736d258..7715760c1a5 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.test.cpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.test.cpp @@ -1,5 +1,4 @@ -#include "barretenberg/crypto/generators/generator_data.hpp" -#include "ultra_circuit_builder.hpp" +#include "goblin_ultra_circuit_builder.hpp" #include using namespace barretenberg; @@ -20,7 +19,7 @@ TEST(UltraCircuitBuilder, GoblinSimple) { const size_t CHUNK_SIZE = plonk::NUM_LIMB_BITS_IN_FIELD_SIMULATION * 2; - auto builder = UltraCircuitBuilder(); + auto builder = GoblinUltraCircuitBuilder(); // Compute a simple point accumulation natively auto P1 = g1::affine_element::random_element(); @@ -85,7 +84,7 @@ TEST(UltraCircuitBuilder, GoblinSimple) TEST(UltraCircuitBuilder, GoblinEccOpQueueUltraOps) { // Construct a simple circuit with op gates - auto builder = UltraCircuitBuilder(); + auto builder = GoblinUltraCircuitBuilder(); // Compute a simple point accumulation natively auto P1 = g1::affine_element::random_element(); diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.cpp index 3274c3e6ba8..fc1d1e6ac36 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.cpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.cpp @@ -47,9 +47,6 @@ template void UltraCircuitBuilder_::finalize_circuit() process_RAM_arrays(); process_range_lists(); - // Set internally the current and previous size of the aggregate op queue transcript - op_queue->set_size_data(); - circuit_finalised = true; } } @@ -499,144 +496,6 @@ template uint32_t UltraCircuitBuilder_::put_constant_variable( } } -/** - * ** Goblin Methods ** - */ - -/** - * @brief Add gates for simple point addition without scalar and compute corresponding op natively - * - * @param point - */ -template -ecc_op_tuple UltraCircuitBuilder_::queue_ecc_add_accum(const barretenberg::g1::affine_element& point) -{ - // Add raw op to queue - op_queue->add_accumulate(point); - - // Decompose operation inputs into width-four form and add ecc op gates - auto op_tuple = decompose_ecc_operands(add_accum_op_idx, point); - populate_ecc_op_wires(op_tuple); - - return op_tuple; -} - -/** - * @brief Add gates for point mul and add and compute corresponding op natively - * - * @tparam FF - * @param point - * @param scalar - * @return ecc_op_tuple encoding the point and scalar inputs to the mul accum - */ -template -ecc_op_tuple UltraCircuitBuilder_::queue_ecc_mul_accum(const barretenberg::g1::affine_element& point, - const FF& scalar) -{ - // Add raw op to op queue - op_queue->mul_accumulate(point, scalar); - - // Decompose operation inputs into width-four form and add ecc op gates - auto op_tuple = decompose_ecc_operands(mul_accum_op_idx, point, scalar); - populate_ecc_op_wires(op_tuple); - - return op_tuple; -} - -/** - * @brief Add point equality gates - * - * @return ecc_op_tuple encoding the point to which equality has been asserted - */ -template ecc_op_tuple UltraCircuitBuilder_::queue_ecc_eq() -{ - // Add raw op to op queue - auto point = op_queue->eq(); - - // Decompose operation inputs into width-four form and add ecc op gates - auto op_tuple = decompose_ecc_operands(equality_op_idx, point); - populate_ecc_op_wires(op_tuple); - - return op_tuple; -} - -/** - * @brief Decompose ecc operands into components, add corresponding variables, return ecc op tuple - * - * @param op_idx Index of op code in variables array - * @param point - * @param scalar - * @return ecc_op_tuple Tuple of indices into variables array used to construct pair of ecc op gates - */ -template -ecc_op_tuple UltraCircuitBuilder_::decompose_ecc_operands(uint32_t op_idx, const g1::affine_element& point, const FF& scalar) -{ - // Decompose point coordinates (Fq) into hi-lo chunks (Fr) - const size_t CHUNK_SIZE = 2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS; - auto x_256 = uint256_t(point.x); - auto y_256 = uint256_t(point.y); - auto x_lo = FF(x_256.slice(0, CHUNK_SIZE)); - auto x_hi = FF(x_256.slice(CHUNK_SIZE, CHUNK_SIZE * 2)); - auto y_lo = FF(y_256.slice(0, CHUNK_SIZE)); - auto y_hi = FF(y_256.slice(CHUNK_SIZE, CHUNK_SIZE * 2)); - - // Split scalar into 128 bit endomorphism scalars - FF z_1 = 0; - FF z_2 = 0; - auto converted = scalar.from_montgomery_form(); - FF::split_into_endomorphism_scalars(converted, z_1, z_2); - z_1 = z_1.to_montgomery_form(); - z_2 = z_2.to_montgomery_form(); - - // Populate ultra ops in OpQueue with the decomposed operands - op_queue->ultra_ops[0].emplace_back(this->variables[op_idx]); - op_queue->ultra_ops[1].emplace_back(x_lo); - op_queue->ultra_ops[2].emplace_back(x_hi); - op_queue->ultra_ops[3].emplace_back(y_lo); - - op_queue->ultra_ops[0].emplace_back(this->variables[op_idx]); - op_queue->ultra_ops[1].emplace_back(y_hi); - op_queue->ultra_ops[2].emplace_back(z_1); - op_queue->ultra_ops[3].emplace_back(z_2); - - // Add variables for decomposition and get indices needed for op wires - auto x_lo_idx = this->add_variable(x_lo); - auto x_hi_idx = this->add_variable(x_hi); - auto y_lo_idx = this->add_variable(y_lo); - auto y_hi_idx = this->add_variable(y_hi); - auto z_1_idx = this->add_variable(z_1); - auto z_2_idx = this->add_variable(z_2); - - return { op_idx, x_lo_idx, x_hi_idx, y_lo_idx, y_hi_idx, z_1_idx, z_2_idx }; -} - -/** - * @brief Add ecc operation to queue - * - * @param in Variables array indices corresponding to operation inputs - * @note We dont explicitly set values for the selectors here since their values are fully determined by - * num_ecc_op_gates. E.g. in the composer we can reconstruct q_ecc_op as the indicator on the first num_ecc_op_gates - * indices. All other selectors are simply 0 on this domain. - */ -template void UltraCircuitBuilder_::populate_ecc_op_wires(const ecc_op_tuple& in) -{ - ecc_op_wire_1.emplace_back(in.op); - ecc_op_wire_2.emplace_back(in.x_lo); - ecc_op_wire_3.emplace_back(in.x_hi); - ecc_op_wire_4.emplace_back(in.y_lo); - - ecc_op_wire_1.emplace_back(in.op); // TODO(luke): second op val is sort of a dummy. use "op" again? - ecc_op_wire_2.emplace_back(in.y_hi); - ecc_op_wire_3.emplace_back(in.z_1); - ecc_op_wire_4.emplace_back(in.z_2); - - num_ecc_op_gates += 2; -}; - -/** - * End of Goblin Methods - */ - template plookup::BasicTable& UltraCircuitBuilder_::get_table(const plookup::BasicTableId id) { for (plookup::BasicTable& table : lookup_tables) { diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp index 2a72789ab20..96873b36311 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp @@ -37,11 +37,6 @@ template class UltraCircuitBuilder_ : public CircuitBuilderBase op_queue; - // Indices for constant variables corresponding to ECCOpQueue op codes uint32_t null_op_idx; uint32_t add_accum_op_idx; @@ -545,14 +540,6 @@ template class UltraCircuitBuilder_ : public CircuitBuilderBase(this->wires); WireVector& w_4 = std::get<3>(this->wires); - // Wires storing ecc op queue data; values are indices into the variables array - std::array::NUM_WIRES> ecc_op_wires; - - WireVector& ecc_op_wire_1 = std::get<0>(ecc_op_wires); - WireVector& ecc_op_wire_2 = std::get<1>(ecc_op_wires); - WireVector& ecc_op_wire_3 = std::get<2>(ecc_op_wires); - WireVector& ecc_op_wire_4 = std::get<3>(ecc_op_wires); - SelectorVector& q_m = this->selectors.q_m; SelectorVector& q_c = this->selectors.q_c; SelectorVector& q_1 = this->selectors.q_1; @@ -601,9 +588,8 @@ template class UltraCircuitBuilder_ : public CircuitBuilderBase op_queue_in = std::make_shared()) + UltraCircuitBuilder_(const size_t size_hint = 0) : CircuitBuilderBase>(ultra_selector_names(), size_hint) - , op_queue(op_queue_in) { w_l.reserve(size_hint); w_r.reserve(size_hint); @@ -611,14 +597,7 @@ template class UltraCircuitBuilder_ : public CircuitBuilderBasezero_idx = put_constant_variable(FF::zero()); this->tau.insert({ DUMMY_TAG, DUMMY_TAG }); // TODO(luke): explain this - // Set indices to constants corresponding to Goblin ECC op codes - null_op_idx = this->zero_idx; - add_accum_op_idx = put_constant_variable(FF(EccOpCode::ADD_ACCUM)); - mul_accum_op_idx = put_constant_variable(FF(EccOpCode::MUL_ACCUM)); - equality_op_idx = put_constant_variable(FF(EccOpCode::EQUALITY)); }; - UltraCircuitBuilder_(std::shared_ptr op_queue_in) - : UltraCircuitBuilder_(0, op_queue_in) {} UltraCircuitBuilder_(const UltraCircuitBuilder_& other) = delete; UltraCircuitBuilder_(UltraCircuitBuilder_&& other) : CircuitBuilderBase>(std::move(other)) @@ -717,21 +696,8 @@ template class UltraCircuitBuilder_ : public CircuitBuilderBase class UltraCircuitBuilder_ : public CircuitBuilderBase class UltraCircuitBuilder_ : public CircuitBuilderBase -concept HasPlookup = proof_system::IsAnyOf; +concept HasPlookup = + proof_system::IsAnyOf; + +template +concept IsGoblinBuilder = + proof_system::IsAnyOf; #define INSTANTIATE_STDLIB_METHOD(stdlib_method) \ template stdlib_method(proof_system::StandardCircuitBuilder); \ template stdlib_method(proof_system::TurboCircuitBuilder); \ - template stdlib_method(proof_system::UltraCircuitBuilder); + template stdlib_method(proof_system::UltraCircuitBuilder); \ + template stdlib_method(proof_system::GoblinUltraCircuitBuilder); #define INSTANTIATE_STDLIB_TYPE(stdlib_type) \ template class stdlib_type; \ template class stdlib_type; \ - template class stdlib_type; + template class stdlib_type; \ + template class stdlib_type; #define INSTANTIATE_STDLIB_TYPE_VA(stdlib_type, ...) \ template class stdlib_type; \ template class stdlib_type; \ - template class stdlib_type; + template class stdlib_type; \ + template class stdlib_type; #define INSTANTIATE_STDLIB_BASIC_TYPE(stdlib_type) \ template class stdlib_type; \ @@ -33,9 +42,14 @@ concept HasPlookup = proof_system::IsAnyOf template class stdlib_type; \ template class stdlib_type; -#define INSTANTIATE_STDLIB_ULTRA_METHOD(stdlib_method) template stdlib_method(proof_system::UltraCircuitBuilder); +#define INSTANTIATE_STDLIB_ULTRA_METHOD(stdlib_method) \ + template stdlib_method(proof_system::UltraCircuitBuilder); \ + template stdlib_method(proof_system::GoblinUltraCircuitBuilder); -#define INSTANTIATE_STDLIB_ULTRA_TYPE(stdlib_type) template class stdlib_type; +#define INSTANTIATE_STDLIB_ULTRA_TYPE(stdlib_type) \ + template class stdlib_type; \ + template class stdlib_type; #define INSTANTIATE_STDLIB_ULTRA_TYPE_VA(stdlib_type, ...) \ - template class stdlib_type; + template class stdlib_type; \ + template class stdlib_type; diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/primitives/circuit_builders/circuit_builders_fwd.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/primitives/circuit_builders/circuit_builders_fwd.hpp index 52a83dda9a4..197026406d4 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/primitives/circuit_builders/circuit_builders_fwd.hpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/primitives/circuit_builders/circuit_builders_fwd.hpp @@ -29,22 +29,27 @@ template class TurboCircuitBuilder_; using TurboCircuitBuilder = TurboCircuitBuilder_>; template class UltraCircuitBuilder_; using UltraCircuitBuilder = UltraCircuitBuilder_>; +template class GoblinUltraCircuitBuilder_; +using GoblinUltraCircuitBuilder = GoblinUltraCircuitBuilder_>; } // namespace proof_system #define EXTERN_STDLIB_TYPE(stdlib_type) \ extern template class stdlib_type; \ extern template class stdlib_type; \ - extern template class stdlib_type; + extern template class stdlib_type; \ + extern template class stdlib_type; #define EXTERN_STDLIB_METHOD(stdlib_method) \ extern template stdlib_method(proof_system::StandardCircuitBuilder); \ extern template stdlib_method(proof_system::TurboCircuitBuilder); \ - extern template stdlib_method(proof_system::UltraCircuitBuilder); + extern template stdlib_method(proof_system::UltraCircuitBuilder); \ + extern template stdlib_method(proof_system::GoblinUltraCircuitBuilder); #define EXTERN_STDLIB_TYPE_VA(stdlib_type, ...) \ extern template class stdlib_type; \ extern template class stdlib_type; \ - extern template class stdlib_type; + extern template class stdlib_type; \ + extern template class stdlib_type; #define EXTERN_STDLIB_BASIC_TYPE(stdlib_type) \ extern template class stdlib_type; \ @@ -54,9 +59,14 @@ using UltraCircuitBuilder = UltraCircuitBuilder_; \ extern template class stdlib_type; -#define EXTERN_STDLIB_ULTRA_TYPE(stdlib_type) extern template class stdlib_type; +#define EXTERN_STDLIB_ULTRA_TYPE(stdlib_type) \ + extern template class stdlib_type; \ + extern template class stdlib_type; #define EXTERN_STDLIB_ULTRA_TYPE_VA(stdlib_type, ...) \ - extern template class stdlib_type; + extern template class stdlib_type; \ + extern template class stdlib_type; -#define EXTERN_STDLIB_ULTRA_METHOD(stdlib_method) extern template stdlib_method(proof_system::UltraCircuitBuilder); +#define EXTERN_STDLIB_ULTRA_METHOD(stdlib_method) \ + extern template stdlib_method(proof_system::UltraCircuitBuilder); \ + extern template stdlib_method(proof_system::GoblinUltraCircuitBuilder); diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp index 323b4a2800f..df812044449 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp @@ -8,8 +8,8 @@ namespace proof_system::plonk::stdlib::recursion::honk { -template -UltraRecursiveVerifier_::UltraRecursiveVerifier_(Builder* builder, +template +UltraRecursiveVerifier_::UltraRecursiveVerifier_(Builder* builder, std::shared_ptr verifier_key) : key(verifier_key) , builder(builder) @@ -19,15 +19,15 @@ UltraRecursiveVerifier_::UltraRecursiveVerifier_(Builder* b * @brief This function constructs a recursive verifier circuit for an Ultra Honk proof of a given flavor. * */ -template -std::array UltraRecursiveVerifier_::verify_proof( +template +std::array UltraRecursiveVerifier_::verify_proof( const plonk::proof& proof) { using Sumcheck = ::proof_system::honk::sumcheck::SumcheckVerifier; using Curve = typename Flavor::Curve; - using Gemini = ::proof_system::honk::pcs::gemini::GeminiVerifier_; - using Shplonk = ::proof_system::honk::pcs::shplonk::ShplonkVerifier_; - using KZG = ::proof_system::honk::pcs::kzg::KZG; // note: This can only be KZG + using Gemini = ::proof_system::honk::pcs::gemini::GeminiVerifier_; + using Shplonk = ::proof_system::honk::pcs::shplonk::ShplonkVerifier_; + using KZG = ::proof_system::honk::pcs::kzg::KZG; // note: This can only be KZG using VerifierCommitments = typename Flavor::VerifierCommitments; using CommitmentLabels = typename Flavor::CommitmentLabels; using RelationParams = ::proof_system::RelationParameters; @@ -156,7 +156,7 @@ std::array UltraRecursiveVerifier_(commitments.get_unshifted(), scalars_unshifted); + GroupElement::batch_mul(commitments.get_unshifted(), scalars_unshifted); info("Batch mul (unshifted): num gates = ", builder->get_num_gates() - prev_num_gates, @@ -166,7 +166,7 @@ std::array UltraRecursiveVerifier_get_num_gates(); auto batched_commitment_to_be_shifted = - GroupElement::template batch_mul(commitments.get_to_be_shifted(), scalars_to_be_shifted); + GroupElement::batch_mul(commitments.get_to_be_shifted(), scalars_to_be_shifted); info("Batch mul (to-be-shited): num gates = ", builder->get_num_gates() - prev_num_gates, @@ -209,8 +209,7 @@ std::array UltraRecursiveVerifier_; -template class UltraRecursiveVerifier_; +template class UltraRecursiveVerifier_; +template class UltraRecursiveVerifier_; } // namespace proof_system::plonk::stdlib::recursion::honk diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp index dff4ac7ade1..7ae43c0eb27 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp @@ -3,12 +3,14 @@ #include "barretenberg/honk/flavor/ultra.hpp" #include "barretenberg/honk/flavor/ultra_grumpkin.hpp" #include "barretenberg/honk/flavor/ultra_recursive.hpp" +#include "barretenberg/honk/flavor/goblin_ultra_recursive.hpp" #include "barretenberg/honk/sumcheck/sumcheck.hpp" #include "barretenberg/plonk/proof_system/types/proof.hpp" #include "barretenberg/stdlib/recursion/honk/transcript/transcript.hpp" namespace proof_system::plonk::stdlib::recursion::honk { -template class UltraRecursiveVerifier_ { +template class UltraRecursiveVerifier_ { + public: using FF = typename Flavor::FF; using Commitment = typename Flavor::Commitment; using GroupElement = typename Flavor::GroupElement; @@ -17,7 +19,6 @@ template class UltraRecursiveVerifie using Builder = typename Flavor::CircuitBuilder; using PairingPoints = std::array; - public: explicit UltraRecursiveVerifier_(Builder* builder, std::shared_ptr verifier_key = nullptr); UltraRecursiveVerifier_(UltraRecursiveVerifier_&& other) = delete; UltraRecursiveVerifier_(const UltraRecursiveVerifier_& other) = delete; @@ -37,10 +38,10 @@ template class UltraRecursiveVerifie Transcript transcript; }; -extern template class UltraRecursiveVerifier_; -extern template class UltraRecursiveVerifier_; +extern template class UltraRecursiveVerifier_; +extern template class UltraRecursiveVerifier_; -using UltraRecursiveVerifier = - UltraRecursiveVerifier_; +using UltraRecursiveVerifier = UltraRecursiveVerifier_; +using GoblinUltraRecursiveVerifier = UltraRecursiveVerifier_; } // namespace proof_system::plonk::stdlib::recursion::honk diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/verifier.test.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/verifier.test.cpp index 7bfc45452d1..996d93fc06c 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/verifier.test.cpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/verifier.test.cpp @@ -20,21 +20,19 @@ namespace proof_system::plonk::stdlib::recursion::honk { * @details Construct and check a recursive verifier circuit for an UltraHonk proof using 1) the conventional Ultra * arithmetization, or 2) a Goblin-style Ultra arithmetization * - * @tparam UseGoblinFlag whether or not to use goblin-style arithmetization for group operations + * @tparam Builder */ -template class RecursiveVerifierTest : public testing::Test { - - static constexpr bool goblin_flag = UseGoblinFlag::value; - +template class RecursiveVerifierTest : public testing::Test { using InnerComposer = ::proof_system::honk::UltraComposer; using InnerBuilder = typename InnerComposer::CircuitBuilder; - using OuterBuilder = ::proof_system::UltraCircuitBuilder; using NativeVerifier = ::proof_system::honk::UltraVerifier_<::proof_system::honk::flavor::Ultra>; - // Arithmetization of group operations in recursive verifier circuit (goblin or not) is determined by goblin_flag - using RecursiveVerifier = UltraRecursiveVerifier_<::proof_system::honk::flavor::UltraRecursive, goblin_flag>; - using VerificationKey = ::proof_system::honk::flavor::UltraRecursive::VerificationKey; + // Arithmetization of group operations in recursive verifier circuit is determined by outer builder + + using RecursiveVerifier = VerifierType; + using OuterBuilder = typename RecursiveVerifier::Builder; + using VerificationKey = typename RecursiveVerifier::VerificationKey; using inner_curve = bn254; using inner_scalar_field_ct = inner_curve::ScalarField; @@ -227,11 +225,12 @@ template class RecursiveVerifierTest : public testing:: } }; -// Run the recursive verifier tests twice, once without using a goblin style arithmetization of group operations and -// once with. -using UseGoblinFlag = testing::Types; +// Run the recursive verifier tests with conventional Ultra builder and Goblin builder +using VerifierTypes = testing::Types; +// using VerifierTypes = testing::Types; +// using BuilderTypes = testing::Types; -TYPED_TEST_SUITE(RecursiveVerifierTest, UseGoblinFlag); +TYPED_TEST_SUITE(RecursiveVerifierTest, VerifierTypes); HEAVY_TYPED_TEST(RecursiveVerifierTest, InnerCircuit) {