diff --git a/cpp/src/barretenberg/honk/composer/standard_composer.cpp b/cpp/src/barretenberg/honk/composer/standard_composer.cpp index 3b89a158b3..a189c3fb95 100644 --- a/cpp/src/barretenberg/honk/composer/standard_composer.cpp +++ b/cpp/src/barretenberg/honk/composer/standard_composer.cpp @@ -10,32 +10,6 @@ namespace proof_system::honk { -/** - * Compute proving key base. - * - * 1. Load crs. - * 2. Initialize this->proving_key. - * 3. Create constraint selector polynomials from each of this composer's `selectors` vectors and add them to the - * proving key. - * - * @param minimum_circuit_size Used as the total number of gates when larger than n + count of public inputs. - * @param num_reserved_gates The number of reserved gates. - * @return Pointer to the initialized proving key updated with selector polynomials. - * */ -template -std::shared_ptr StandardComposer_::compute_proving_key_base( - const CircuitBuilder& constructor, const size_t minimum_circuit_size, const size_t num_randomized_gates) -{ - // Initialize proving_key - // TODO(#392)(Kesha): replace composer types. - proving_key = - initialize_proving_key(constructor, crs_factory_.get(), minimum_circuit_size, num_randomized_gates); - // Compute lagrange selectors - construct_selector_polynomials(constructor, proving_key.get()); - - return proving_key; -} - /** * Compute witness polynomials (w_1, w_2, w_3, w_4). * @@ -52,8 +26,7 @@ void StandardComposer_::compute_witness(const CircuitBuilder& circuit_co if (computed_witness) { return; } - auto wire_polynomials = - construct_wire_polynomials_base(circuit_constructor, minimum_circuit_size, NUM_RESERVED_GATES); + auto wire_polynomials = construct_wire_polynomials_base(circuit_constructor, dyadic_circuit_size); proving_key->w_l = wire_polynomials[0]; proving_key->w_r = wire_polynomials[1]; @@ -76,8 +49,12 @@ std::shared_ptr StandardComposer_::compute_ if (proving_key) { return proving_key; } - // Compute q_l, q_r, q_o, etc polynomials - StandardComposer_::compute_proving_key_base(circuit_constructor, /*minimum_circuit_size=*/0, NUM_RESERVED_GATES); + + // Construct a proving key + proving_key = std::make_shared(dyadic_circuit_size, num_public_inputs); + + // Compute lagrange selectors + construct_selector_polynomials(circuit_constructor, proving_key.get()); // Compute sigma polynomials (we should update that late) compute_standard_honk_sigma_permutations(circuit_constructor, proving_key.get()); @@ -139,6 +116,11 @@ StandardVerifier_ StandardComposer_::create_verifier(const Circu template StandardProver_ StandardComposer_::create_prover(const CircuitBuilder& circuit_constructor) { + // Compute some key cicuit size paramaters + num_public_inputs = circuit_constructor.public_inputs.size(); + total_num_gates = circuit_constructor.num_gates + num_public_inputs; + dyadic_circuit_size = circuit_constructor.get_circuit_subgroup_size(total_num_gates); + compute_proving_key(circuit_constructor); compute_witness(circuit_constructor); diff --git a/cpp/src/barretenberg/honk/composer/standard_composer.hpp b/cpp/src/barretenberg/honk/composer/standard_composer.hpp index b12a8bac23..4cc562e0bc 100644 --- a/cpp/src/barretenberg/honk/composer/standard_composer.hpp +++ b/cpp/src/barretenberg/honk/composer/standard_composer.hpp @@ -21,7 +21,6 @@ template class StandardComposer_ { using PCSCommitmentKey = typename PCSParams::CommitmentKey; static constexpr std::string_view NAME_STRING = "StandardHonk"; - static constexpr size_t NUM_RESERVED_GATES = 2; // equal to the number of multilinear evaluations leaked static constexpr size_t NUM_WIRES = CircuitBuilder::NUM_WIRES; std::shared_ptr proving_key; std::shared_ptr verification_key; @@ -32,6 +31,10 @@ template class StandardComposer_ { // The commitment key is passed to the prover but also used herein to compute the verfication key commitments std::shared_ptr commitment_key; + size_t total_num_gates; // total num gates prior to computing dyadic size + size_t dyadic_circuit_size; // final dyadic circuit size + size_t num_public_inputs; + bool computed_witness = false; // TODO(Luke): use make_shared StandardComposer_() @@ -62,11 +65,6 @@ template class StandardComposer_ { StandardProver_ create_prover(const CircuitBuilder& circuit_constructor); - // TODO(#216)(Adrian): Seems error prone to provide the number of randomized gates - std::shared_ptr compute_proving_key_base(const CircuitBuilder& circuit_constructor, - const size_t minimum_circuit_size = 0, - const size_t num_randomized_gates = NUM_RESERVED_GATES); - void compute_witness(const CircuitBuilder& circuit_constructor, const size_t minimum_circuit_size = 0); void compute_commitment_key(size_t circuit_size, std::shared_ptr crs_factory) diff --git a/cpp/src/barretenberg/honk/composer/standard_composer.test.cpp b/cpp/src/barretenberg/honk/composer/standard_composer.test.cpp index c58426f69f..3f3043d09d 100644 --- a/cpp/src/barretenberg/honk/composer/standard_composer.test.cpp +++ b/cpp/src/barretenberg/honk/composer/standard_composer.test.cpp @@ -33,7 +33,9 @@ class StandardHonkComposerTests : public ::testing::Test { TEST_F(StandardHonkComposerTests, SigmaIDCorrectness) { auto test_permutation = [](StandardCircuitBuilder& circuit_constructor, StandardComposer& composer) { - auto proving_key = composer.compute_proving_key(circuit_constructor); + auto prover = composer.create_prover(circuit_constructor); + auto proving_key = prover.key; + const auto n = proving_key->circuit_size; auto public_inputs = circuit_constructor.get_public_inputs(); @@ -71,9 +73,6 @@ TEST_F(StandardHonkComposerTests, SigmaIDCorrectness) left = barretenberg::fr::one(); right = barretenberg::fr::one(); - // Now let's check that witness values correspond to the permutation - composer.compute_witness(circuit_constructor); - auto permutation_polynomials = proving_key->get_sigma_polynomials(); auto id_polynomials = proving_key->get_id_polynomials(); auto wire_polynomials = proving_key->get_wires(); @@ -104,7 +103,7 @@ TEST_F(StandardHonkComposerTests, SigmaIDCorrectness) } // test correctness of the public input delta - auto delta = proof_system::honk::compute_public_input_delta(public_inputs, beta, gamma, n); + auto delta = proof_system::honk::compute_public_input_delta(public_inputs, beta, gamma, n); EXPECT_EQ(left / right, delta); for (size_t i = 0; i < num_public_inputs; ++i) { @@ -164,7 +163,9 @@ TEST_F(StandardHonkComposerTests, LagrangeCorrectness) // Generate proving key auto composer = StandardComposer(); - auto proving_key = composer.compute_proving_key(circuit_constructor); + auto prover = composer.create_prover(circuit_constructor); + auto proving_key = prover.key; + // Generate a random polynomial barretenberg::polynomial random_polynomial = barretenberg::polynomial(proving_key->circuit_size); for (size_t i = 0; i < proving_key->circuit_size; i++) { @@ -225,7 +226,9 @@ TEST_F(StandardHonkComposerTests, AssertEquals) */ auto get_maximum_cycle = [](auto& circuit_constructor, auto& composer) { // Compute the proving key for sigma polynomials - auto proving_key = composer.compute_proving_key(circuit_constructor); + auto prover = composer.create_prover(circuit_constructor); + auto proving_key = prover.key; + auto permutation_length = composer.NUM_WIRES * proving_key->circuit_size; auto sigma_polynomials = proving_key->get_sigma_polynomials(); diff --git a/cpp/src/barretenberg/honk/composer/ultra_composer.cpp b/cpp/src/barretenberg/honk/composer/ultra_composer.cpp index 9a7c54c19e..86e3a6b579 100644 --- a/cpp/src/barretenberg/honk/composer/ultra_composer.cpp +++ b/cpp/src/barretenberg/honk/composer/ultra_composer.cpp @@ -7,61 +7,75 @@ namespace proof_system::honk { /** - * @brief Compute witness polynomials + * @brief Helper method to compute quantities like total number of gates and dyadic circuit size * + * @tparam Flavor + * @param circuit_constructor */ -template void UltraComposer_::compute_witness(CircuitBuilder& circuit_constructor) +template +void UltraComposer_::compute_circuit_size_parameters(CircuitBuilder& circuit_constructor) { - if (computed_witness) { - return; - } - - size_t tables_size = 0; - size_t lookups_size = 0; + // Compute total length of the tables and the number of lookup gates; their sum is the minimum circuit size for (const auto& table : circuit_constructor.lookup_tables) { tables_size += table.size; lookups_size += table.lookup_gates.size(); } - const size_t filled_gates = circuit_constructor.num_gates + circuit_constructor.public_inputs.size(); - const size_t total_num_gates = std::max(filled_gates, tables_size + lookups_size); + const size_t num_gates = circuit_constructor.num_gates; + num_public_inputs = circuit_constructor.public_inputs.size(); + + // minimum circuit size due to the length of lookups plus tables + const size_t minimum_circuit_size_due_to_lookups = tables_size + lookups_size + zero_row_offset; - const size_t subgroup_size = circuit_constructor.get_circuit_subgroup_size(total_num_gates + NUM_RESERVED_GATES); + // number of populated rows in the execution trace + const size_t num_rows_populated_in_execution_trace = + circuit_constructor.num_gates + circuit_constructor.public_inputs.size() + zero_row_offset; + + // The number of gates is max(lookup gates + tables, rows already populated in trace) + 1, where the +1 is due to + // addition of a "zero row" at top of the execution trace to ensure wires and other polys are shiftable. + total_num_gates = std::max(minimum_circuit_size_due_to_lookups, num_rows_populated_in_execution_trace); + + // Next power of 2 + dyadic_circuit_size = circuit_constructor.get_circuit_subgroup_size(total_num_gates); +} + +/** + * @brief Compute witness polynomials + * + */ +template void UltraComposer_::compute_witness(CircuitBuilder& circuit_constructor) +{ + if (computed_witness) { + return; + } - // Pad the wires (pointers to `witness_indices` of the `variables` vector). - // Note: the remaining NUM_RESERVED_GATES indices are padded with zeros within `construct_wire_polynomials_base` - // (called next). - for (size_t i = filled_gates; i < total_num_gates; ++i) { + // At this point, the wires have been populated with as many values as rows in the execution trace. We need to pad + // with zeros up to the full length, i.e. total_num_gates = already populated rows of execution trace + tables_size. + for (size_t i = 0; i < tables_size; ++i) { circuit_constructor.w_l.emplace_back(circuit_constructor.zero_idx); circuit_constructor.w_r.emplace_back(circuit_constructor.zero_idx); circuit_constructor.w_o.emplace_back(circuit_constructor.zero_idx); circuit_constructor.w_4.emplace_back(circuit_constructor.zero_idx); } - // TODO(#340)(luke): within construct_wire_polynomials_base, the 3rd argument is used in the calculation of the - // dyadic circuit size (subgroup_size). Here (and in other split composers) we're passing in NUM_RESERVED_GATES, - // but elsewhere, e.g. directly above, we use NUM_RESERVED_GATES in a similar role. Therefore, these two constants - // must be equal for everything to be consistent. What we should do is compute the dyadic circuit size once and for - // all then pass that around rather than computing in multiple places. - auto wire_polynomials = - construct_wire_polynomials_base(circuit_constructor, total_num_gates, NUM_RESERVED_GATES); + auto wire_polynomials = construct_wire_polynomials_base(circuit_constructor, dyadic_circuit_size); proving_key->w_l = wire_polynomials[0]; proving_key->w_r = wire_polynomials[1]; proving_key->w_o = wire_polynomials[2]; proving_key->w_4 = wire_polynomials[3]; - polynomial s_1(subgroup_size); - polynomial s_2(subgroup_size); - polynomial s_3(subgroup_size); - polynomial s_4(subgroup_size); + polynomial s_1(dyadic_circuit_size); + polynomial s_2(dyadic_circuit_size); + polynomial s_3(dyadic_circuit_size); + polynomial s_4(dyadic_circuit_size); // TODO(luke): The +1 size for z_lookup is not necessary and can lead to confusion. Resolve. - polynomial z_lookup(subgroup_size + 1); // Only instantiated in this function; nothing assigned. + polynomial z_lookup(dyadic_circuit_size + 1); // Only instantiated in this function; nothing assigned. // TODO(kesha): Look at this once we figure out how we do ZK (previously we had roots cut out, so just added // randomness) // The size of empty space in sorted polynomials - size_t count = subgroup_size - tables_size - lookups_size; + size_t count = dyadic_circuit_size - tables_size - lookups_size; ASSERT(count > 0); // We need at least 1 row of zeroes for the permutation argument for (size_t i = 0; i < count; ++i) { s_1[i] = 0; @@ -124,11 +138,10 @@ template void UltraComposer_::compute_witness(Circu // Copy memory read/write record data into proving key. Prover needs to know which gates contain a read/write // 'record' witness on the 4th wire. This wire value can only be fully computed once the first 3 wire polynomials // have been committed to. The 4th wire on these gates will be a random linear combination of the first 3 wires, - // using the plookup challenge `eta`. Because we shift the gates by the number of public inputs, we need to update - // the records with the public_inputs offset - const uint32_t public_inputs_count = static_cast(circuit_constructor.public_inputs.size()); - auto add_public_inputs_offset = [public_inputs_count](uint32_t gate_index) { - return gate_index + public_inputs_count; + // using the plookup challenge `eta`. We need to update the records with an offset Because we shift the gates by + // the number of public inputs plus an additional offset for a zero row. + auto add_public_inputs_offset = [this](uint32_t gate_index) { + return gate_index + num_public_inputs + zero_row_offset; }; proving_key->memory_read_records = std::vector(); proving_key->memory_write_records = std::vector(); @@ -151,6 +164,9 @@ UltraProver_ UltraComposer_::create_prover(CircuitBuilder& circu circuit_constructor.add_gates_to_ensure_all_polys_are_non_zero(); circuit_constructor.finalize_circuit(); + // Compute total number of gates, dyadic circuit size, etc. + compute_circuit_size_parameters(circuit_constructor); + compute_proving_key(circuit_constructor); compute_witness(circuit_constructor); @@ -189,38 +205,20 @@ std::shared_ptr UltraComposer_::compute_pro return proving_key; } - size_t tables_size = 0; - size_t lookups_size = 0; - for (const auto& table : circuit_constructor.lookup_tables) { - tables_size += table.size; - lookups_size += table.lookup_gates.size(); - } - - const size_t minimum_circuit_size = tables_size + lookups_size; - const size_t num_randomized_gates = NUM_RESERVED_GATES; - // Initialize proving_key - // TODO(#392)(Kesha): replace composer types. - proving_key = initialize_proving_key( - circuit_constructor, crs_factory_.get(), minimum_circuit_size, num_randomized_gates); + proving_key = std::make_shared(dyadic_circuit_size, num_public_inputs); construct_selector_polynomials(circuit_constructor, proving_key.get()); - // TODO(#217)(luke): Naively enforcing non-zero selectors for Honk will result in some relations not being - // satisfied. - // enforce_nonzero_polynomial_selectors(circuit_constructor, proving_key.get()); - compute_honk_generalized_sigma_permutations(circuit_constructor, proving_key.get()); compute_first_and_last_lagrange_polynomials(proving_key.get()); - const size_t subgroup_size = proving_key->circuit_size; - - polynomial poly_q_table_column_1(subgroup_size); - polynomial poly_q_table_column_2(subgroup_size); - polynomial poly_q_table_column_3(subgroup_size); - polynomial poly_q_table_column_4(subgroup_size); + polynomial poly_q_table_column_1(dyadic_circuit_size); + polynomial poly_q_table_column_2(dyadic_circuit_size); + polynomial poly_q_table_column_3(dyadic_circuit_size); + polynomial poly_q_table_column_4(dyadic_circuit_size); - size_t offset = subgroup_size - tables_size; + size_t offset = dyadic_circuit_size - tables_size; // Create lookup selector polynomials which interpolate each table column. // Our selector polys always need to interpolate the full subgroup size, so here we offset so as to @@ -251,33 +249,6 @@ std::shared_ptr UltraComposer_::compute_pro // Polynomial memory is zeroed out when constructed with size hint, so we don't have to initialize trailing space - // // In the case of using UltraPlonkComposer for a circuit which does _not_ make use of any lookup tables, all - // four - // // table columns would be all zeros. This would result in these polys' commitments all being the point at - // infinity - // // (which is bad because our point arithmetic assumes we'll never operate on the point at infinity). To avoid - // this, - // // we set the last evaluation of each poly to be nonzero. The last `num_roots_cut_out_of_vanishing_poly = 4` - // // evaluations are ignored by constraint checks; we arbitrarily choose the very-last evaluation to be nonzero. - // See - // // ComposerBase::compute_proving_key_base for further explanation, as a similar trick is done there. We could - // // have chosen `1` for each such evaluation here, but that would have resulted in identical commitments for - // // all four columns. We don't want to have equal commitments, because biggroup operations assume no points are - // // equal, so if we tried to verify an ultra proof in a circuit, the biggroup operations would fail. To combat - // // this, we just choose distinct values: - - // TODO(#217)(luke): Similar to the selectors, enforcing non-zero values by inserting an arbitrary final element - // in the table polys will result in lookup relations not being satisfied. Address this with issue #217. - // size_t num_selectors = circuit_constructor.num_selectors; - // ASSERT(offset == subgroup_size - 1); - // auto unique_last_value = num_selectors + 1; // Note: in compute_proving_key_base, moments earlier, each selector - // // vector was given a unique last value from 1..num_selectors. So we - // // avoid those values and continue the count, to ensure uniqueness. - // poly_q_table_column_1[subgroup_size - 1] = unique_last_value; - // poly_q_table_column_2[subgroup_size - 1] = ++unique_last_value; - // poly_q_table_column_3[subgroup_size - 1] = ++unique_last_value; - // poly_q_table_column_4[subgroup_size - 1] = ++unique_last_value; - proving_key->table_1 = poly_q_table_column_1; proving_key->table_2 = poly_q_table_column_2; proving_key->table_3 = poly_q_table_column_3; diff --git a/cpp/src/barretenberg/honk/composer/ultra_composer.hpp b/cpp/src/barretenberg/honk/composer/ultra_composer.hpp index f72eb22c52..25ed13cc68 100644 --- a/cpp/src/barretenberg/honk/composer/ultra_composer.hpp +++ b/cpp/src/barretenberg/honk/composer/ultra_composer.hpp @@ -21,8 +21,10 @@ template class UltraComposer_ { using PCSCommitmentKey = typename PCSParams::CommitmentKey; using PCSVerificationKey = typename PCSParams::VerificationKey; + // offset due to placing zero wires at the start of execution trace + static constexpr size_t zero_row_offset = Flavor::has_zero_row ? 1 : 0; + static constexpr std::string_view NAME_STRING = "UltraHonk"; - static constexpr size_t NUM_RESERVED_GATES = 4; // equal to the number of multilinear evaluations leaked static constexpr size_t NUM_WIRES = CircuitBuilder::NUM_WIRES; std::shared_ptr proving_key; std::shared_ptr verification_key; @@ -36,6 +38,11 @@ template class UltraComposer_ { std::vector recursive_proof_public_input_indices; bool contains_recursive_proof = false; bool computed_witness = false; + size_t total_num_gates = 0; // num_gates + num_pub_inputs + tables + zero_row_offset (used to compute dyadic size) + size_t dyadic_circuit_size = 0; // final power-of-2 circuit size + size_t lookups_size = 0; // total number of lookup gates + size_t tables_size = 0; // total number of table entries + size_t num_public_inputs = 0; UltraComposer_() : crs_factory_(barretenberg::srs::get_crs_factory()){}; @@ -58,6 +65,8 @@ template class UltraComposer_ { std::shared_ptr compute_proving_key(const CircuitBuilder& circuit_constructor); std::shared_ptr compute_verification_key(const CircuitBuilder& circuit_constructor); + void compute_circuit_size_parameters(CircuitBuilder& circuit_constructor); + void compute_witness(CircuitBuilder& circuit_constructor); UltraProver_ create_prover(CircuitBuilder& circuit_constructor); diff --git a/cpp/src/barretenberg/honk/composer/ultra_composer.test.cpp b/cpp/src/barretenberg/honk/composer/ultra_composer.test.cpp index 07b35aa259..7767113872 100644 --- a/cpp/src/barretenberg/honk/composer/ultra_composer.test.cpp +++ b/cpp/src/barretenberg/honk/composer/ultra_composer.test.cpp @@ -59,14 +59,13 @@ class UltraHonkComposerTests : public ::testing::Test { /** * @brief A quick test to ensure that none of our polynomials are identically zero * - * @note This test assumes that gates have been added by default in the circuit - * constructor to achieve non-zero polynomials + * @note This test assumes that gates have been added by default in the composer + * to achieve non-zero polynomials * */ TEST_F(UltraHonkComposerTests, ANonZeroPolynomialIsAGoodPolynomial) { auto circuit_constructor = UltraCircuitBuilder(); - circuit_constructor.add_gates_to_ensure_all_polys_are_non_zero(); auto composer = UltraComposer(); auto prover = composer.create_prover(circuit_constructor); @@ -85,6 +84,34 @@ TEST_F(UltraHonkComposerTests, ANonZeroPolynomialIsAGoodPolynomial) } } +/** + * @brief Test simple circuit with public inputs + * + */ +TEST_F(UltraHonkComposerTests, PublicInputs) +{ + auto builder = UltraCircuitBuilder(); + size_t num_gates = 10; + + // Add some arbitrary arithmetic gates that utilize public inputs + for (size_t i = 0; i < num_gates; ++i) { + fr a = fr::random_element(); + uint32_t a_idx = builder.add_public_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) }); + } + + auto composer = UltraComposer(); + prove_and_verify(builder, composer, /*expected_result=*/true); +} + TEST_F(UltraHonkComposerTests, XorConstraint) { auto circuit_constructor = UltraCircuitBuilder(); diff --git a/cpp/src/barretenberg/honk/flavor/standard.hpp b/cpp/src/barretenberg/honk/flavor/standard.hpp index fefdb293ba..a0205be2b2 100644 --- a/cpp/src/barretenberg/honk/flavor/standard.hpp +++ b/cpp/src/barretenberg/honk/flavor/standard.hpp @@ -66,6 +66,9 @@ class Standard { using RelationUnivariates = decltype(create_relation_univariates_container()); using RelationValues = decltype(create_relation_values_container()); + // Whether or not the first row of the execution trace is reserved for 0s to enable shifts + static constexpr bool has_zero_row = false; + private: /** * @brief A base class labelling precomputed entities and (ordered) subsets of interest. diff --git a/cpp/src/barretenberg/honk/flavor/standard_grumpkin.hpp b/cpp/src/barretenberg/honk/flavor/standard_grumpkin.hpp index cd75dd4817..87ad545d62 100644 --- a/cpp/src/barretenberg/honk/flavor/standard_grumpkin.hpp +++ b/cpp/src/barretenberg/honk/flavor/standard_grumpkin.hpp @@ -57,8 +57,8 @@ class StandardGrumpkin { using RelationUnivariates = decltype(create_relation_univariates_container()); using RelationValues = decltype(create_relation_values_container()); - // define utilities to extend univarates from RELATION_LENGTH to MAX_RELATION_LENGTH for each Relation - // using BarycentricUtils = decltype(create_barycentric_utils()); + // Whether or not the first row of the execution trace is reserved for 0s to enable shifts + static constexpr bool has_zero_row = false; private: /** diff --git a/cpp/src/barretenberg/honk/flavor/ultra.hpp b/cpp/src/barretenberg/honk/flavor/ultra.hpp index ec2d05b862..31a9af032d 100644 --- a/cpp/src/barretenberg/honk/flavor/ultra.hpp +++ b/cpp/src/barretenberg/honk/flavor/ultra.hpp @@ -71,6 +71,9 @@ class Ultra { using RelationUnivariates = decltype(create_relation_univariates_container()); using RelationValues = decltype(create_relation_values_container()); + // Whether or not the first row of the execution trace is reserved for 0s to enable shifts + static constexpr bool has_zero_row = true; + private: template /** diff --git a/cpp/src/barretenberg/honk/flavor/ultra_grumpkin.hpp b/cpp/src/barretenberg/honk/flavor/ultra_grumpkin.hpp index a8f0f4021e..c5dc82f41a 100644 --- a/cpp/src/barretenberg/honk/flavor/ultra_grumpkin.hpp +++ b/cpp/src/barretenberg/honk/flavor/ultra_grumpkin.hpp @@ -69,6 +69,9 @@ class UltraGrumpkin { using RelationUnivariates = decltype(create_relation_univariates_container()); using RelationValues = decltype(create_relation_values_container()); + // Whether or not the first row of the execution trace is reserved for 0s to enable shifts + static constexpr bool has_zero_row = true; + private: template /** diff --git a/cpp/src/barretenberg/honk/proof_system/prover.cpp b/cpp/src/barretenberg/honk/proof_system/prover.cpp index 9407fe95f6..df49db3111 100644 --- a/cpp/src/barretenberg/honk/proof_system/prover.cpp +++ b/cpp/src/barretenberg/honk/proof_system/prover.cpp @@ -104,7 +104,7 @@ template void StandardProver_::execute_grand_pro // Compute and store parameters required by relations in Sumcheck auto [beta, gamma] = transcript.get_challenges("beta", "gamma"); - auto public_input_delta = compute_public_input_delta(public_inputs, beta, gamma, key->circuit_size); + auto public_input_delta = compute_public_input_delta(public_inputs, beta, gamma, key->circuit_size); relation_parameters = sumcheck::RelationParameters{ .beta = beta, diff --git a/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp b/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp index 09f2a451e7..b382064f3a 100644 --- a/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp +++ b/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp @@ -71,11 +71,14 @@ UltraProver_::UltraProver_(std::shared_ptr prover_polynomials.w_r_shift = key->w_r.shifted(); prover_polynomials.w_o_shift = key->w_o.shifted(); - // Add public inputs to transcript from the second wire polynomial + // Add public inputs to transcript from the second wire polynomial; The PI have been written into the wires at the + // 0th or 1st index depending on whether or not a zero row is utilized. std::span public_wires_source = prover_polynomials.w_r; + const size_t pub_input_offset = Flavor::has_zero_row ? 1 : 0; for (size_t i = 0; i < key->num_public_inputs; ++i) { - public_inputs.emplace_back(public_wires_source[i]); + size_t idx = i + pub_input_offset; + public_inputs.emplace_back(public_wires_source[idx]); } } @@ -156,7 +159,7 @@ template void UltraProver_::execute_grand_product_c // Compute and store parameters required by relations in Sumcheck auto [beta, gamma] = transcript.get_challenges("beta", "gamma"); - auto public_input_delta = compute_public_input_delta(public_inputs, beta, gamma, key->circuit_size); + auto public_input_delta = compute_public_input_delta(public_inputs, beta, gamma, key->circuit_size); auto lookup_grand_product_delta = compute_lookup_grand_product_delta(beta, gamma, key->circuit_size); relation_parameters.beta = beta; diff --git a/cpp/src/barretenberg/honk/proof_system/ultra_verifier.cpp b/cpp/src/barretenberg/honk/proof_system/ultra_verifier.cpp index 4608d9017b..0a149ac791 100644 --- a/cpp/src/barretenberg/honk/proof_system/ultra_verifier.cpp +++ b/cpp/src/barretenberg/honk/proof_system/ultra_verifier.cpp @@ -84,7 +84,7 @@ template bool UltraVerifier_::verify_proof(const plonk // Get permutation challenges auto [beta, gamma] = transcript.get_challenges("beta", "gamma"); - const FF public_input_delta = compute_public_input_delta(public_inputs, beta, gamma, circuit_size); + const FF public_input_delta = compute_public_input_delta(public_inputs, beta, gamma, circuit_size); const FF lookup_grand_product_delta = compute_lookup_grand_product_delta(beta, gamma, circuit_size); relation_parameters.beta = beta; diff --git a/cpp/src/barretenberg/honk/proof_system/verifier.cpp b/cpp/src/barretenberg/honk/proof_system/verifier.cpp index 452e2e0c3a..a109f02ca1 100644 --- a/cpp/src/barretenberg/honk/proof_system/verifier.cpp +++ b/cpp/src/barretenberg/honk/proof_system/verifier.cpp @@ -95,7 +95,7 @@ template bool StandardVerifier_::verify_proof(const pl // Get permutation challenges auto [beta, gamma] = transcript.get_challenges("beta", "gamma"); - const FF public_input_delta = compute_public_input_delta(public_inputs, beta, gamma, circuit_size); + const FF public_input_delta = compute_public_input_delta(public_inputs, beta, gamma, circuit_size); sumcheck::RelationParameters relation_parameters{ .beta = beta, diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp b/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp index 44b30d808b..50065bb606 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp @@ -109,7 +109,7 @@ TEST_F(RelationCorrectnessTests, StandardRelationCorrectness) // Compute public input delta const auto public_inputs = circuit_constructor.get_public_inputs(); auto public_input_delta = - honk::compute_public_input_delta(public_inputs, beta, gamma, prover.key->circuit_size); + honk::compute_public_input_delta(public_inputs, beta, gamma, prover.key->circuit_size); sumcheck::RelationParameters params{ .beta = beta, @@ -304,7 +304,7 @@ TEST_F(RelationCorrectnessTests, UltraRelationCorrectness) // Compute public input delta const auto public_inputs = circuit_constructor.get_public_inputs(); auto public_input_delta = - honk::compute_public_input_delta(public_inputs, beta, gamma, prover.key->circuit_size); + honk::compute_public_input_delta(public_inputs, beta, gamma, prover.key->circuit_size); auto lookup_grand_product_delta = honk::compute_lookup_grand_product_delta(beta, gamma, prover.key->circuit_size); diff --git a/cpp/src/barretenberg/honk/sumcheck/sumcheck.test.cpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck.test.cpp index 4109e6152c..80504cd2a9 100644 --- a/cpp/src/barretenberg/honk/sumcheck/sumcheck.test.cpp +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck.test.cpp @@ -450,7 +450,7 @@ TEST_F(SumcheckTests, RealCircuitStandard) // Compute public input delta const auto public_inputs = circuit_constructor.get_public_inputs(); auto public_input_delta = - honk::compute_public_input_delta(public_inputs, beta, gamma, prover.key->circuit_size); + honk::compute_public_input_delta(public_inputs, beta, gamma, prover.key->circuit_size); sumcheck::RelationParameters relation_parameters{ .beta = beta, @@ -511,8 +511,8 @@ TEST_F(SumcheckTests, RealCircuitUltra) auto circuit_constructor = UltraCircuitBuilder(); fr a = fr::one(); - // Add some basic add gates - uint32_t a_idx = circuit_constructor.add_variable(a); + // Add some basic add gates, with a public input for good measure + uint32_t a_idx = circuit_constructor.add_public_variable(a); fr b = fr::one(); fr c = a + b; fr d = a + c; @@ -632,7 +632,7 @@ TEST_F(SumcheckTests, RealCircuitUltra) // Compute public input delta const auto public_inputs = circuit_constructor.get_public_inputs(); auto public_input_delta = - honk::compute_public_input_delta(public_inputs, beta, gamma, prover.key->circuit_size); + honk::compute_public_input_delta(public_inputs, beta, gamma, prover.key->circuit_size); auto lookup_grand_product_delta = honk::compute_lookup_grand_product_delta(beta, gamma, prover.key->circuit_size); diff --git a/cpp/src/barretenberg/honk/transcript/transcript.test.cpp b/cpp/src/barretenberg/honk/transcript/transcript.test.cpp index 2749bb1011..f3c46576f5 100644 --- a/cpp/src/barretenberg/honk/transcript/transcript.test.cpp +++ b/cpp/src/barretenberg/honk/transcript/transcript.test.cpp @@ -21,12 +21,12 @@ template class TranscriptTests : public testing::Test { /** * @brief Construct a manifest for a standard Honk proof * - * @details This is where we define the "Manifest" for a Standard Honk proof. The tests in this suite are - intented - * to warn the developer if the Prover/Verifier has deviated from this manifest, however, the Transcript class - is + * @details This is where we define the "Manifest" for a Standard Honk proof. The tests in this suite are intented + * to warn the developer if the Prover/Verifier has deviated from this manifest, however, the Transcript class is * not otherwise contrained to follow the manifest. * + * @note Entries in the manifest consist of a name string and a size (bytes), NOT actual data. + * * @return TranscriptManifest */ TranscriptManifest construct_standard_honk_manifest(size_t circuit_size) @@ -40,10 +40,12 @@ template class TranscriptTests : public testing::Test { size_t size_G = 2 * size_FF; size_t size_uni = max_relation_length * size_FF; size_t size_evals = (Flavor::NUM_ALL_ENTITIES)*size_FF; + size_t size_uint32 = 4; + size_t size_uint64 = 8; size_t round = 0; - manifest_expected.add_entry(round, "circuit_size", 4); - manifest_expected.add_entry(round, "public_input_size", 4); + manifest_expected.add_entry(round, "circuit_size", size_uint32); + manifest_expected.add_entry(round, "public_input_size", size_uint32); manifest_expected.add_entry(round, "public_input_0", size_FF); manifest_expected.add_entry(round, "W_1", size_G); manifest_expected.add_entry(round, "W_2", size_G); @@ -87,7 +89,7 @@ template class TranscriptTests : public testing::Test { round++; // TODO(Mara): Make testing more flavor agnostic so we can test this with all flavors if constexpr (IsGrumpkinFlavor) { - manifest_expected.add_entry(round, "IPA:poly_degree", circuit_size); + manifest_expected.add_entry(round, "IPA:poly_degree", size_uint64); manifest_expected.add_challenge(round, "IPA:generator_challenge"); for (size_t i = 0; i < log_n; i++) { diff --git a/cpp/src/barretenberg/honk/utils/grand_product_delta.hpp b/cpp/src/barretenberg/honk/utils/grand_product_delta.hpp index 674ab15c2a..1860026639 100644 --- a/cpp/src/barretenberg/honk/utils/grand_product_delta.hpp +++ b/cpp/src/barretenberg/honk/utils/grand_product_delta.hpp @@ -13,12 +13,13 @@ namespace proof_system::honk { * @param domain_size Total number of rows required for the circuit (power of 2) * @return Field Public input Δ */ -template -Field compute_public_input_delta(std::span public_inputs, - const Field& beta, - const Field& gamma, - const size_t domain_size) +template +typename Flavor::FF compute_public_input_delta(std::span public_inputs, + const typename Flavor::FF& beta, + const typename Flavor::FF& gamma, + const size_t domain_size) { + using Field = typename Flavor::FF; Field numerator = Field::one(); Field denominator = Field::one(); @@ -41,8 +42,11 @@ Field compute_public_input_delta(std::span public_inputs, // denominator_acc = γ - β⋅(1+i) = γ - β - β⋅i // at the end of the loop, add and subtract β to each term respectively to // set the expected value for the start of iteration i+1. - Field numerator_acc = gamma + (beta * Field(domain_size)); - Field denominator_acc = gamma - beta; + // Note: If a zero row is included at the start of the execution + // trace, the indices of the PI are offset by 1. + const size_t offset = Flavor::has_zero_row ? 1 : 0; + Field numerator_acc = gamma + (beta * Field(domain_size + offset)); + Field denominator_acc = gamma - beta * Field(1 + offset); for (const auto& x_i : public_inputs) { numerator *= (numerator_acc + x_i); // γ + xᵢ + β(n+i) diff --git a/cpp/src/barretenberg/plonk/composer/composer_lib.hpp b/cpp/src/barretenberg/plonk/composer/composer_lib.hpp index 21d32b8ad8..d24ca45279 100644 --- a/cpp/src/barretenberg/plonk/composer/composer_lib.hpp +++ b/cpp/src/barretenberg/plonk/composer/composer_lib.hpp @@ -9,6 +9,57 @@ struct SelectorProperties { bool requires_lagrange_base_polynomial = false; }; +/** + * @brief Initilalize proving key and load the crs + * + * @param circuit_constructor Object containing the circuit + * @param crs_factory Produces the prover's reference string + * @param minimum_circuit_size The minimum size of polynomials without randomized elements + * @param num_randomized_gates Number of gates with randomized witnesses + * @param circuit_type This is passed in the case of Plonk since we use flavor-independent proving and verification keys + * in that case. + * @return std::shared_ptr + */ +std::shared_ptr initialize_proving_key(const auto& circuit_constructor, + barretenberg::srs::factories::CrsFactory* crs_factory, + const size_t minimum_circuit_size, + const size_t num_randomized_gates, + CircuitType circuit_type) +{ + const size_t num_gates = circuit_constructor.num_gates; + + const size_t num_public_inputs = circuit_constructor.public_inputs.size(); + const size_t num_constraints = num_gates + num_public_inputs; + const size_t total_num_constraints = std::max(minimum_circuit_size, num_constraints); + const size_t subgroup_size = + circuit_constructor.get_circuit_subgroup_size(total_num_constraints + num_randomized_gates); // next power of 2 + + auto crs = crs_factory->get_prover_crs(subgroup_size + 1); + + // Differentiate between Honk and Plonk here since Plonk pkey requires crs whereas Honk pkey does not + auto proving_key = std::make_shared(subgroup_size, num_public_inputs, crs, circuit_type); + + return proving_key; +} + +/** + * @brief Fill the last index of each selector polynomial in lagrange form with a non-zero value + * + * @tparam Flavor + * @param circuit_constructor The object holding the circuit + * @param key Pointer to the proving key + */ +void enforce_nonzero_selector_polynomials(const auto& circuit_constructor, auto* proving_key) +{ + for (size_t idx = 0; idx < circuit_constructor.num_selectors; ++idx) { + auto current_selector = + proving_key->polynomial_store.get(circuit_constructor.selector_names_[idx] + "_lagrange"); + current_selector[current_selector.size() - 1] = idx + 1; + proving_key->polynomial_store.put(circuit_constructor.selector_names_[idx] + "_lagrange", + std::move(current_selector)); + } +} + /** * @brief Retrieve lagrange forms of selector polynomials and compute monomial and coset-monomial forms and put into * cache diff --git a/cpp/src/barretenberg/plonk/composer/standard_composer.cpp b/cpp/src/barretenberg/plonk/composer/standard_composer.cpp index c14107dfcc..97439bf798 100644 --- a/cpp/src/barretenberg/plonk/composer/standard_composer.cpp +++ b/cpp/src/barretenberg/plonk/composer/standard_composer.cpp @@ -30,8 +30,14 @@ void StandardComposer::compute_witness(const CircuitBuilder& circuit_constructor if (computed_witness) { return; } - auto wire_polynomial_evaluations = - construct_wire_polynomials_base(circuit_constructor, minimum_circuit_size, NUM_RESERVED_GATES); + const size_t num_gates = circuit_constructor.num_gates; + const size_t num_public_inputs = circuit_constructor.public_inputs.size(); + + const size_t num_constraints = std::max(minimum_circuit_size, num_gates + num_public_inputs); + + const size_t subgroup_size = circuit_constructor.get_circuit_subgroup_size(num_constraints + NUM_RESERVED_GATES); + + auto wire_polynomial_evaluations = construct_wire_polynomials_base(circuit_constructor, subgroup_size); for (size_t j = 0; j < program_width; ++j) { std::string index = std::to_string(j + 1); @@ -61,12 +67,12 @@ std::shared_ptr StandardComposer::compute_proving_key(const const size_t num_randomized_gates = NUM_RESERVED_GATES; // Initialize circuit_proving_key // TODO(#392)(Kesha): replace composer types. - circuit_proving_key = proof_system::initialize_proving_key( + circuit_proving_key = initialize_proving_key( circuit_constructor, crs_factory_.get(), minimum_circuit_size, num_randomized_gates, CircuitType::STANDARD); // Compute lagrange selectors construct_selector_polynomials(circuit_constructor, circuit_proving_key.get()); // Make all selectors nonzero - enforce_nonzero_selector_polynomials(circuit_constructor, circuit_proving_key.get()); + enforce_nonzero_selector_polynomials(circuit_constructor, circuit_proving_key.get()); // Compute selectors in monomial form compute_monomial_and_coset_selector_forms(circuit_proving_key.get(), standard_selector_properties()); diff --git a/cpp/src/barretenberg/plonk/composer/turbo_composer.cpp b/cpp/src/barretenberg/plonk/composer/turbo_composer.cpp index b4c741d9fa..9c6937adb8 100644 --- a/cpp/src/barretenberg/plonk/composer/turbo_composer.cpp +++ b/cpp/src/barretenberg/plonk/composer/turbo_composer.cpp @@ -36,12 +36,12 @@ std::shared_ptr TurboComposer::compute_proving_key(const Cir const size_t num_randomized_gates = NUM_RESERVED_GATES; // Initialize circuit_proving_key // TODO(#392)(Kesha): replace composer types. - circuit_proving_key = initialize_proving_key( + circuit_proving_key = initialize_proving_key( circuit_constructor, crs_factory_.get(), minimum_circuit_size, num_randomized_gates, CircuitType::TURBO); construct_selector_polynomials(circuit_constructor, circuit_proving_key.get()); - enforce_nonzero_selector_polynomials(circuit_constructor, circuit_proving_key.get()); + enforce_nonzero_selector_polynomials(circuit_constructor, circuit_proving_key.get()); compute_monomial_and_coset_selector_forms(circuit_proving_key.get(), turbo_selector_properties()); @@ -94,8 +94,13 @@ void TurboComposer::compute_witness(const CircuitBuilder& circuit_constructor, c if (computed_witness) { return; } - auto wire_polynomial_evaluations = - construct_wire_polynomials_base(circuit_constructor, minimum_circuit_size, NUM_RESERVED_GATES); + const size_t num_gates = circuit_constructor.num_gates; + const size_t num_public_inputs = circuit_constructor.public_inputs.size(); + + const size_t num_constraints = std::max(minimum_circuit_size, num_gates + num_public_inputs); + const size_t subgroup_size = circuit_constructor.get_circuit_subgroup_size(num_constraints + NUM_RESERVED_GATES); + + auto wire_polynomial_evaluations = construct_wire_polynomials_base(circuit_constructor, subgroup_size); for (size_t j = 0; j < program_width; ++j) { std::string index = std::to_string(j + 1); diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp index 244bf770a2..7b0ded945c 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp @@ -1,4 +1,5 @@ #include "ultra_composer.hpp" +#include "barretenberg/plonk/composer/composer_lib.hpp" #include "barretenberg/plonk/proof_system/commitment_scheme/kate_commitment_scheme.hpp" #include "barretenberg/plonk/proof_system/types/program_settings.hpp" #include "barretenberg/plonk/proof_system/types/prover_settings.hpp" @@ -48,10 +49,7 @@ void UltraComposer::compute_witness(CircuitBuilder& circuit_constructor) circuit_constructor.w_4.emplace_back(circuit_constructor.zero_idx); } - // TODO(luke): subgroup size was already computed above but compute_witness_base computes it again. If we pass in - // NUM_RESERVED_GATES (as in the other split composers) the resulting sizes can differ. Reconcile this. - auto wire_polynomial_evaluations = - construct_wire_polynomials_base(circuit_constructor, total_num_gates, NUM_RESERVED_GATES); + auto wire_polynomial_evaluations = construct_wire_polynomials_base(circuit_constructor, subgroup_size); for (size_t j = 0; j < program_width; ++j) { std::string index = std::to_string(j + 1); @@ -379,12 +377,12 @@ std::shared_ptr UltraComposer::compute_proving_key(CircuitBuilder& const size_t num_randomized_gates = NUM_RESERVED_GATES; // Initialize circuit_proving_key // TODO(#392)(Kesha): replace composer types. - circuit_proving_key = initialize_proving_key( + circuit_proving_key = initialize_proving_key( circuit_constructor, crs_factory_.get(), minimum_circuit_size, num_randomized_gates, CircuitType::ULTRA); construct_selector_polynomials(circuit_constructor, circuit_proving_key.get()); - enforce_nonzero_selector_polynomials(circuit_constructor, circuit_proving_key.get()); + enforce_nonzero_selector_polynomials(circuit_constructor, circuit_proving_key.get()); compute_monomial_and_coset_selector_forms(circuit_proving_key.get(), ultra_selector_properties()); diff --git a/cpp/src/barretenberg/plonk/flavor/flavor.hpp b/cpp/src/barretenberg/plonk/flavor/flavor.hpp index b50fccf33d..95fdc13528 100644 --- a/cpp/src/barretenberg/plonk/flavor/flavor.hpp +++ b/cpp/src/barretenberg/plonk/flavor/flavor.hpp @@ -11,6 +11,8 @@ class Standard { using CircuitBuilder = proof_system::StandardCircuitBuilder; using ProvingKey = plonk::proving_key; static constexpr size_t NUM_WIRES = CircuitBuilder::NUM_WIRES; + // Whether or not the first row of the execution trace is reserved for 0s to enable shifts + static constexpr bool has_zero_row = false; }; class Turbo { @@ -18,6 +20,8 @@ class Turbo { using CircuitBuilder = proof_system::TurboCircuitBuilder; using ProvingKey = plonk::proving_key; static constexpr size_t NUM_WIRES = CircuitBuilder::NUM_WIRES; + // Whether or not the first row of the execution trace is reserved for 0s to enable shifts + static constexpr bool has_zero_row = false; }; class Ultra { @@ -25,6 +29,8 @@ class Ultra { using CircuitBuilder = proof_system::UltraCircuitBuilder; using ProvingKey = plonk::proving_key; static constexpr size_t NUM_WIRES = CircuitBuilder::NUM_WIRES; + // Whether or not the first row of the execution trace is reserved for 0s to enable shifts + static constexpr bool has_zero_row = false; /** * @brief Create a manifest object diff --git a/cpp/src/barretenberg/proof_system/composer/composer_helper_lib.hpp b/cpp/src/barretenberg/proof_system/composer/composer_helper_lib.hpp deleted file mode 100644 index 9544179766..0000000000 --- a/cpp/src/barretenberg/proof_system/composer/composer_helper_lib.hpp +++ /dev/null @@ -1,179 +0,0 @@ -#pragma once -#include "barretenberg/plonk/proof_system/proving_key/proving_key.hpp" -#include "barretenberg/proof_system/flavor/flavor.hpp" -#include "barretenberg/srs/factories/crs_factory.hpp" -#include - -namespace proof_system { - -/** - * @brief Initilalize proving key and load the crs - * - * @tparam Flavor - * @param circuit_constructor Object containing the circuit - * @param crs_factory Produces the prover's reference string - * @param minimum_circuit_size The minimum size of polynomials without randomized elements - * @param num_randomized_gates Number of gates with randomized witnesses - * @param circuit_type This is passed in the case of Plonk since we use flavor-independent proving and verification keys - * in that case. - * @return std::shared_ptr - */ -template -std::shared_ptr initialize_proving_key( - const typename Flavor::CircuitBuilder& circuit_constructor, - barretenberg::srs::factories::CrsFactory* crs_factory, - const size_t minimum_circuit_size, - const size_t num_randomized_gates, - CircuitType circuit_type = CircuitType::UNDEFINED) -{ - const size_t num_gates = circuit_constructor.num_gates; - std::span public_inputs = circuit_constructor.public_inputs; - - const size_t num_public_inputs = public_inputs.size(); - const size_t num_constraints = num_gates + num_public_inputs; - const size_t total_num_constraints = std::max(minimum_circuit_size, num_constraints); - const size_t subgroup_size = - circuit_constructor.get_circuit_subgroup_size(total_num_constraints + num_randomized_gates); // next power of 2 - - // Differentiate between Honk and Plonk here since Plonk pkey requires crs whereas Honk pkey does not - std::shared_ptr proving_key; - if constexpr (IsHonkFlavor) { - proving_key = std::make_shared(subgroup_size, num_public_inputs); - } else if constexpr (IsPlonkFlavor) { - auto crs = crs_factory->get_prover_crs(subgroup_size + 1); - proving_key = - std::make_shared(subgroup_size, num_public_inputs, crs, circuit_type); - } - - return proving_key; -} - -/** - * @brief Construct selector polynomials from ciruit selector information and put into polynomial cache - * - * @tparam Flavor - * @param circuit_constructor The object holding the circuit - * @param key Pointer to the proving key - */ -template -void construct_selector_polynomials(const typename Flavor::CircuitBuilder& circuit_constructor, - typename Flavor::ProvingKey* proving_key) -{ - const size_t num_public_inputs = circuit_constructor.public_inputs.size(); - // TODO(#398): Loose coupling here! Would rather build up pk from arithmetization - size_t selector_idx = 0; // TODO(#391) zip - for (auto& selector_values : circuit_constructor.selectors) { - ASSERT(proving_key->circuit_size >= selector_values.size()); - - // Copy the selector values for all gates, keeping the rows at which we store public inputs as 0. - // Initializing the polynomials in this way automatically applies 0-padding to the selectors. - barretenberg::polynomial selector_poly_lagrange(proving_key->circuit_size); - for (size_t i = 0; i < selector_values.size(); ++i) { - selector_poly_lagrange[num_public_inputs + i] = selector_values[i]; - } - if constexpr (IsHonkFlavor) { - // TODO(#398): Loose coupling here of arithmetization and flavor. - proving_key->_precomputed_polynomials[selector_idx] = selector_poly_lagrange; - } else if constexpr (IsPlonkFlavor) { - // TODO(Cody): Loose coupling here of selector_names and selector_properties. - proving_key->polynomial_store.put(circuit_constructor.selector_names_[selector_idx] + "_lagrange", - std::move(selector_poly_lagrange)); - } - ++selector_idx; - } -} - -/** - * @brief Fill the last index of each selector polynomial in lagrange form with a non-zero value - * - * @tparam Flavor - * @param circuit_constructor The object holding the circuit - * @param key Pointer to the proving key - * - * @warning We should ensure that this does not clash with any other values we want to place at the end of of the - * witness vectors. In later iterations of the Sumcheck, we will be able to efficiently cancel out any checks in the - * last 2^k rows, so any randomness or unique values should be placed there. -@adr1anh - */ -template -void enforce_nonzero_selector_polynomials(const typename Flavor::CircuitBuilder& circuit_constructor, - typename Flavor::ProvingKey* proving_key) -{ - if constexpr (IsHonkFlavor) { - size_t idx = 1; - for (auto selector : proving_key->get_selectors()) { - selector[selector.size() - 1] = idx; - ++idx; - } - } else if constexpr (IsPlonkFlavor) { - for (size_t idx = 0; idx < circuit_constructor.num_selectors; ++idx) { - auto current_selector = - proving_key->polynomial_store.get(circuit_constructor.selector_names_[idx] + "_lagrange"); - current_selector[current_selector.size() - 1] = idx + 1; - proving_key->polynomial_store.put(circuit_constructor.selector_names_[idx] + "_lagrange", - std::move(current_selector)); - } - } -} - -/** - * @brief Construct the witness polynomials from the witness vectors in the circuit constructor. - * - * @details The first two witness polynomials begin with the public input values. - * - * - * @tparam Flavor provides the circuit constructor type and the number of wires. - * @param circuit_constructor - * @param minimum_circuit_size - * @param number_of_randomized_gates - * - * @return std::vector - * */ -template -std::vector construct_wire_polynomials_base( - const typename Flavor::CircuitBuilder& circuit_constructor, - const size_t minimum_circuit_size, - const size_t number_of_randomized_gates) -{ - const size_t num_gates = circuit_constructor.num_gates; - std::span public_inputs = circuit_constructor.public_inputs; - const size_t num_public_inputs = public_inputs.size(); - - const size_t num_constraints = std::max(minimum_circuit_size, num_gates + num_public_inputs); - // TODO(#216)(Adrian): Not a fan of specifying NUM_RESERVED_GATES everywhere, - // Each flavor of Honk should have a "fixed" number of random places to add randomness to. - // It should be taken care of in as few places possible. - const size_t subgroup_size = - circuit_constructor.get_circuit_subgroup_size(num_constraints + number_of_randomized_gates); - - // construct a view over all the wire's variable indices - // w[j][i] is the index of the variable in the j-th wire, at gate i - // Each array should be of size `num_gates` - - std::vector wire_polynomials; - // Note: randomness is added to 3 of the last 4 positions in plonk/proof_system/prover/prover.cpp - // StandardProverBase::execute_preamble_round(). - size_t wire_idx = 0; // TODO(#391) zip - for (auto& wire : circuit_constructor.wires) { - // Initialize the polynomial with all the actual copies variable values - // Expect all values to be set to 0 initially - barretenberg::polynomial w_lagrange(subgroup_size); - - // Place all public inputs at the start of the first two wires. - // All selectors at these indices are set to 0, so these values are not constrained at all. - if (wire_idx < 2) { - for (size_t i = 0; i < num_public_inputs; ++i) { - w_lagrange[i] = circuit_constructor.get_variable(public_inputs[i]); - } - ++wire_idx; - } - - // Assign the variable values (which are pointed-to by the `w_` wire_polynomials) to the wire witness - // polynomials `poly_w_`, shifted to make room for the public inputs at the beginning. - for (size_t i = 0; i < num_gates; ++i) { - w_lagrange[num_public_inputs + i] = circuit_constructor.get_variable(wire[i]); - } - wire_polynomials.push_back(std::move(w_lagrange)); - } - return wire_polynomials; -} -} // namespace proof_system diff --git a/cpp/src/barretenberg/proof_system/composer/composer_lib.hpp b/cpp/src/barretenberg/proof_system/composer/composer_lib.hpp index 9544179766..e1802456d0 100644 --- a/cpp/src/barretenberg/proof_system/composer/composer_lib.hpp +++ b/cpp/src/barretenberg/proof_system/composer/composer_lib.hpp @@ -6,48 +6,6 @@ namespace proof_system { -/** - * @brief Initilalize proving key and load the crs - * - * @tparam Flavor - * @param circuit_constructor Object containing the circuit - * @param crs_factory Produces the prover's reference string - * @param minimum_circuit_size The minimum size of polynomials without randomized elements - * @param num_randomized_gates Number of gates with randomized witnesses - * @param circuit_type This is passed in the case of Plonk since we use flavor-independent proving and verification keys - * in that case. - * @return std::shared_ptr - */ -template -std::shared_ptr initialize_proving_key( - const typename Flavor::CircuitBuilder& circuit_constructor, - barretenberg::srs::factories::CrsFactory* crs_factory, - const size_t minimum_circuit_size, - const size_t num_randomized_gates, - CircuitType circuit_type = CircuitType::UNDEFINED) -{ - const size_t num_gates = circuit_constructor.num_gates; - std::span public_inputs = circuit_constructor.public_inputs; - - const size_t num_public_inputs = public_inputs.size(); - const size_t num_constraints = num_gates + num_public_inputs; - const size_t total_num_constraints = std::max(minimum_circuit_size, num_constraints); - const size_t subgroup_size = - circuit_constructor.get_circuit_subgroup_size(total_num_constraints + num_randomized_gates); // next power of 2 - - // Differentiate between Honk and Plonk here since Plonk pkey requires crs whereas Honk pkey does not - std::shared_ptr proving_key; - if constexpr (IsHonkFlavor) { - proving_key = std::make_shared(subgroup_size, num_public_inputs); - } else if constexpr (IsPlonkFlavor) { - auto crs = crs_factory->get_prover_crs(subgroup_size + 1); - proving_key = - std::make_shared(subgroup_size, num_public_inputs, crs, circuit_type); - } - - return proving_key; -} - /** * @brief Construct selector polynomials from ciruit selector information and put into polynomial cache * @@ -59,7 +17,9 @@ template void construct_selector_polynomials(const typename Flavor::CircuitBuilder& circuit_constructor, typename Flavor::ProvingKey* proving_key) { - const size_t num_public_inputs = circuit_constructor.public_inputs.size(); + // Offset for starting to write selectors is zero row offset + num public inputs + const size_t zero_row_offset = Flavor::has_zero_row ? 1 : 0; + const size_t gate_offset = zero_row_offset + circuit_constructor.public_inputs.size(); // TODO(#398): Loose coupling here! Would rather build up pk from arithmetization size_t selector_idx = 0; // TODO(#391) zip for (auto& selector_values : circuit_constructor.selectors) { @@ -69,7 +29,7 @@ void construct_selector_polynomials(const typename Flavor::CircuitBuilder& circu // Initializing the polynomials in this way automatically applies 0-padding to the selectors. barretenberg::polynomial selector_poly_lagrange(proving_key->circuit_size); for (size_t i = 0; i < selector_values.size(); ++i) { - selector_poly_lagrange[num_public_inputs + i] = selector_values[i]; + selector_poly_lagrange[i + gate_offset] = selector_values[i]; } if constexpr (IsHonkFlavor) { // TODO(#398): Loose coupling here of arithmetization and flavor. @@ -83,38 +43,6 @@ void construct_selector_polynomials(const typename Flavor::CircuitBuilder& circu } } -/** - * @brief Fill the last index of each selector polynomial in lagrange form with a non-zero value - * - * @tparam Flavor - * @param circuit_constructor The object holding the circuit - * @param key Pointer to the proving key - * - * @warning We should ensure that this does not clash with any other values we want to place at the end of of the - * witness vectors. In later iterations of the Sumcheck, we will be able to efficiently cancel out any checks in the - * last 2^k rows, so any randomness or unique values should be placed there. -@adr1anh - */ -template -void enforce_nonzero_selector_polynomials(const typename Flavor::CircuitBuilder& circuit_constructor, - typename Flavor::ProvingKey* proving_key) -{ - if constexpr (IsHonkFlavor) { - size_t idx = 1; - for (auto selector : proving_key->get_selectors()) { - selector[selector.size() - 1] = idx; - ++idx; - } - } else if constexpr (IsPlonkFlavor) { - for (size_t idx = 0; idx < circuit_constructor.num_selectors; ++idx) { - auto current_selector = - proving_key->polynomial_store.get(circuit_constructor.selector_names_[idx] + "_lagrange"); - current_selector[current_selector.size() - 1] = idx + 1; - proving_key->polynomial_store.put(circuit_constructor.selector_names_[idx] + "_lagrange", - std::move(current_selector)); - } - } -} - /** * @brief Construct the witness polynomials from the witness vectors in the circuit constructor. * @@ -123,32 +51,18 @@ void enforce_nonzero_selector_polynomials(const typename Flavor::CircuitBuilder& * * @tparam Flavor provides the circuit constructor type and the number of wires. * @param circuit_constructor - * @param minimum_circuit_size - * @param number_of_randomized_gates + * @param dyadic_circuit_size Power of 2 circuit size * * @return std::vector * */ template std::vector construct_wire_polynomials_base( - const typename Flavor::CircuitBuilder& circuit_constructor, - const size_t minimum_circuit_size, - const size_t number_of_randomized_gates) + const typename Flavor::CircuitBuilder& circuit_constructor, const size_t dyadic_circuit_size) { - const size_t num_gates = circuit_constructor.num_gates; + const size_t zero_row_offset = Flavor::has_zero_row ? 1 : 0; std::span public_inputs = circuit_constructor.public_inputs; const size_t num_public_inputs = public_inputs.size(); - const size_t num_constraints = std::max(minimum_circuit_size, num_gates + num_public_inputs); - // TODO(#216)(Adrian): Not a fan of specifying NUM_RESERVED_GATES everywhere, - // Each flavor of Honk should have a "fixed" number of random places to add randomness to. - // It should be taken care of in as few places possible. - const size_t subgroup_size = - circuit_constructor.get_circuit_subgroup_size(num_constraints + number_of_randomized_gates); - - // construct a view over all the wire's variable indices - // w[j][i] is the index of the variable in the j-th wire, at gate i - // Each array should be of size `num_gates` - std::vector wire_polynomials; // Note: randomness is added to 3 of the last 4 positions in plonk/proof_system/prover/prover.cpp // StandardProverBase::execute_preamble_round(). @@ -156,21 +70,23 @@ std::vector construct_wire_polynomials_base( for (auto& wire : circuit_constructor.wires) { // Initialize the polynomial with all the actual copies variable values // Expect all values to be set to 0 initially - barretenberg::polynomial w_lagrange(subgroup_size); + barretenberg::polynomial w_lagrange(dyadic_circuit_size); - // Place all public inputs at the start of the first two wires. + // Place all public inputs at the start of the first two wires, possibly offset by a zero row. // All selectors at these indices are set to 0, so these values are not constrained at all. + const size_t pub_input_offset = zero_row_offset; // offset at which to start writing pub inputs if (wire_idx < 2) { for (size_t i = 0; i < num_public_inputs; ++i) { - w_lagrange[i] = circuit_constructor.get_variable(public_inputs[i]); + w_lagrange[i + pub_input_offset] = circuit_constructor.get_variable(public_inputs[i]); } ++wire_idx; } // Assign the variable values (which are pointed-to by the `w_` wire_polynomials) to the wire witness - // polynomials `poly_w_`, shifted to make room for the public inputs at the beginning. - for (size_t i = 0; i < num_gates; ++i) { - w_lagrange[num_public_inputs + i] = circuit_constructor.get_variable(wire[i]); + // polynomials `poly_w_`, shifted to make room for public inputs and the specified offset (possibly 0). + const size_t gate_offset = num_public_inputs + pub_input_offset; // offset at which to start writing gates + for (size_t i = 0; i < circuit_constructor.num_gates; ++i) { + w_lagrange[i + gate_offset] = circuit_constructor.get_variable(wire[i]); } wire_polynomials.push_back(std::move(w_lagrange)); } diff --git a/cpp/src/barretenberg/proof_system/composer/composer_lib.test.cpp b/cpp/src/barretenberg/proof_system/composer/composer_lib.test.cpp index 45d1a81844..0df8d50666 100644 --- a/cpp/src/barretenberg/proof_system/composer/composer_lib.test.cpp +++ b/cpp/src/barretenberg/proof_system/composer/composer_lib.test.cpp @@ -20,22 +20,6 @@ class ComposerLibTests : public ::testing::Test { }(); }; -TEST_F(ComposerLibTests, InitializeProvingKey) -{ - static_assert(IsHonkFlavor); - - EXPECT_EQ(circuit_constructor.get_circuit_subgroup_size(7), 8); - - barretenberg::srs::factories::CrsFactory crs_factory; - - auto pk = initialize_proving_key(circuit_constructor, - &crs_factory, - /*minimum_circuit_size=*/0, - /*num_randomized_gates=*/2); - EXPECT_EQ(pk->circuit_size, 8); - EXPECT_EQ(pk->num_public_inputs, 0); -} - TEST_F(ComposerLibTests, ConstructSelectors) { circuit_constructor.q_m = { 1, 2, 3, 4 }; @@ -72,24 +56,6 @@ TEST_F(ComposerLibTests, ConstructSelectors) EXPECT_EQ(proving_key.q_c[3], 20); } -TEST_F(ComposerLibTests, EnforceNonzeroSelectors) -{ - circuit_constructor.q_m = { 0, 0, 0, 0 }; - circuit_constructor.q_1 = { 0, 0, 0, 0 }; - circuit_constructor.q_2 = { 0, 0, 0, 0 }; - circuit_constructor.q_3 = { 0, 0, 0, 0 }; - circuit_constructor.q_c = { 0, 0, 0, 0 }; - - construct_selector_polynomials(circuit_constructor, &proving_key); - enforce_nonzero_selector_polynomials(circuit_constructor, &proving_key); - - EXPECT_EQ(proving_key.q_m[3], 1); - EXPECT_EQ(proving_key.q_l[3], 2); - EXPECT_EQ(proving_key.q_r[3], 3); - EXPECT_EQ(proving_key.q_o[3], 4); - EXPECT_EQ(proving_key.q_c[3], 5); -} - TEST_F(ComposerLibTests, ConstructWitnessPolynomialsBase) { circuit_constructor.add_public_variable(1024); @@ -128,7 +94,10 @@ TEST_F(ComposerLibTests, ConstructWitnessPolynomialsBase) */ - auto wires = construct_wire_polynomials_base(circuit_constructor, 1, 2); + const size_t dyadic_circuit_size = circuit_constructor.get_circuit_subgroup_size( + circuit_constructor.num_gates + circuit_constructor.public_inputs.size()); + + auto wires = construct_wire_polynomials_base(circuit_constructor, dyadic_circuit_size); auto& w_l = wires[0]; auto& w_r = wires[1]; auto& w_o = wires[2]; diff --git a/cpp/src/barretenberg/proof_system/composer/permutation_lib.hpp b/cpp/src/barretenberg/proof_system/composer/permutation_lib.hpp index ab63a82834..1c7c1eeda4 100644 --- a/cpp/src/barretenberg/proof_system/composer/permutation_lib.hpp +++ b/cpp/src/barretenberg/proof_system/composer/permutation_lib.hpp @@ -60,8 +60,8 @@ using CyclicPermutation = std::vector; namespace { /** - * Compute all CyclicPermutations of the circuit. Each CyclicPermutation represents the indices of the values in the - * witness wires that must have the same value. + * @brief Compute all CyclicPermutations of the circuit. Each CyclicPermutation represents the indices of the values in + * the witness wires that must have the same value. * * @tparam program_width Program width * */ @@ -73,6 +73,10 @@ std::vector compute_wire_copy_cycles(const typename Flavor::C std::span public_inputs = circuit_constructor.public_inputs; const size_t num_public_inputs = public_inputs.size(); + // Define offsets for placement of public inputs and gates in execution trace + const size_t pub_inputs_offset = Flavor::has_zero_row ? 1 : 0; + const size_t gates_offset = num_public_inputs + pub_inputs_offset; + // Each variable represents one cycle const size_t number_of_cycles = circuit_constructor.variables.size(); std::vector copy_cycles(number_of_cycles); @@ -81,6 +85,17 @@ std::vector compute_wire_copy_cycles(const typename Flavor::C // Represents the index of a variable in circuit_constructor.variables std::span real_variable_index = circuit_constructor.real_variable_index; + // For some flavors, we need to ensure the value in the 0th index of each wire is 0 to allow for left-shift by 1. To + // do this, we add the wires of the first gate in the execution trace to the "zero index" copy cycle. + if (Flavor::has_zero_row) { + for (size_t wire_idx = 0; wire_idx < Flavor::NUM_WIRES; ++wire_idx) { + const auto wire_index = static_cast(wire_idx); + const uint32_t gate_index = 0; // place zeros at 0th index + const uint32_t zero_idx = circuit_constructor.zero_idx; // index of constant zero in variables + copy_cycles[zero_idx].emplace_back(cycle_node{ wire_index, gate_index }); + } + } + // We use the permutation argument to enforce the public input variables to be equal to values provided by the // verifier. The convension we use is to place the public input values as the first rows of witness vectors. // More specifically, we set the LEFT and RIGHT wires to be the public inputs and set the other elements of the row @@ -95,14 +110,14 @@ std::vector compute_wire_copy_cycles(const typename Flavor::C // for all i s.t. row i defines a public input. for (size_t i = 0; i < num_public_inputs; ++i) { const uint32_t public_input_index = real_variable_index[public_inputs[i]]; - const auto gate_index = static_cast(i); + const auto gate_index = static_cast(i + pub_inputs_offset); // These two nodes must be in adjacent locations in the cycle for correct handling of public inputs copy_cycles[public_input_index].emplace_back(cycle_node{ 0, gate_index }); copy_cycles[public_input_index].emplace_back(cycle_node{ 1, gate_index }); } // Iterate over all variables of the "real" gates, and add a corresponding node to the cycle for that variable - for (size_t gate_idx = 0; gate_idx < num_gates; ++gate_idx) { + for (size_t i = 0; i < num_gates; ++i) { size_t wire_idx = 0; for (auto& wire : circuit_constructor.wires) { // We are looking at the j-th wire in the i-th row. @@ -110,10 +125,10 @@ std::vector compute_wire_copy_cycles(const typename Flavor::C // of the `constructor.variables` vector. // Therefore, we add (i,j) to the cycle at index `var_index` to indicate that w^j_i should have the values // constructor.variables[var_index]. - const uint32_t var_index = circuit_constructor.real_variable_index[wire[gate_idx]]; + const uint32_t var_index = circuit_constructor.real_variable_index[wire[i]]; const auto wire_index = static_cast(wire_idx); - const auto shifted_gate_idx = static_cast(gate_idx + num_public_inputs); - copy_cycles[var_index].emplace_back(cycle_node{ wire_index, shifted_gate_idx }); + const auto gate_idx = static_cast(i + gates_offset); + copy_cycles[var_index].emplace_back(cycle_node{ wire_index, gate_idx }); ++wire_idx; } } @@ -206,13 +221,16 @@ PermutationMapping compute_permutation_mapping( } // Add information about public inputs to the computation - const uint32_t num_public_inputs = static_cast(circuit_constructor.public_inputs.size()); + const auto num_public_inputs = static_cast(circuit_constructor.public_inputs.size()); + // The public inputs are placed at the top of the execution trace, potentially offset by a zero row. + const size_t zero_row_offset = Flavor::has_zero_row ? 1 : 0; for (size_t i = 0; i < num_public_inputs; ++i) { - mapping.sigmas[0][i].row_index = static_cast(i); - mapping.sigmas[0][i].column_index = 0; - mapping.sigmas[0][i].is_public_input = true; - if (mapping.sigmas[0][i].is_tag) { + size_t idx = i + zero_row_offset; + mapping.sigmas[0][idx].row_index = static_cast(idx); + mapping.sigmas[0][idx].column_index = 0; + mapping.sigmas[0][idx].is_public_input = true; + if (mapping.sigmas[0][idx].is_tag) { std::cerr << "MAPPING IS BOTH A TAG AND A PUBLIC INPUT" << std::endl; } } diff --git a/cpp/src/barretenberg/proof_system/composer/permutation_lib.test.cpp b/cpp/src/barretenberg/proof_system/composer/permutation_lib.test.cpp index 80aa86f13b..ee9125606a 100644 --- a/cpp/src/barretenberg/proof_system/composer/permutation_lib.test.cpp +++ b/cpp/src/barretenberg/proof_system/composer/permutation_lib.test.cpp @@ -12,6 +12,7 @@ class PermutationHelperTests : public ::testing::Test { protected: using Flavor = honk::flavor::Standard; using FF = typename Flavor::FF; + using ProvingKey = Flavor::ProvingKey; Flavor::CircuitBuilder circuit_constructor; barretenberg::srs::factories::CrsFactory crs_factory = barretenberg::srs::factories::CrsFactory(); std::shared_ptr proving_key; @@ -54,7 +55,10 @@ class PermutationHelperTests : public ::testing::Test { */ - proving_key = initialize_proving_key(circuit_constructor, &crs_factory, 0, 2); + size_t num_public_inputs = circuit_constructor.public_inputs.size(); + size_t dyadic_circuit_size = + circuit_constructor.get_circuit_subgroup_size(circuit_constructor.num_gates + num_public_inputs); + proving_key = std::make_shared(dyadic_circuit_size, num_public_inputs); // construct_selector_polynomials(circuit_constructor, proving_key.get()); }