diff --git a/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/plonk.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/plonk.bench.cpp index 64cfe4f92f2..d1a5e501cb9 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/plonk.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/plonk.bench.cpp @@ -32,21 +32,6 @@ plonk::Prover provers[NUM_CIRCUITS]; plonk::Verifier verifiers[NUM_CIRCUITS]; plonk::proof proofs[NUM_CIRCUITS]; -void construct_witnesses_bench(State& state) noexcept -{ - for (auto _ : state) { - state.PauseTiming(); - auto builder = Builder(static_cast(state.range(0))); - generate_test_plonk_circuit(builder, static_cast(state.range(0))); - auto composer = Composer(); - composer.compute_proving_key(builder); - state.ResumeTiming(); - - composer.compute_witness(builder); - } -} -BENCHMARK(construct_witnesses_bench)->RangeMultiplier(2)->Range(START, MAX_GATES); - void construct_proving_keys_bench(State& state) noexcept { for (auto _ : state) { diff --git a/barretenberg/cpp/src/barretenberg/common/ref_array.hpp b/barretenberg/cpp/src/barretenberg/common/ref_array.hpp index a9dcb7c3462..3c2fb017dce 100644 --- a/barretenberg/cpp/src/barretenberg/common/ref_array.hpp +++ b/barretenberg/cpp/src/barretenberg/common/ref_array.hpp @@ -38,8 +38,18 @@ template class RefArray { T& operator[](std::size_t idx) const { + // GCC has a bug where it has trouble analyzing zip_view + // this is likely due to this bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104165 + // We disable this - if GCC was right, we would have caught this at runtime +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif ASSERT(idx < N); return *storage[idx]; +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif } /** diff --git a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp index d1968234cbc..20008d3f67c 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp @@ -115,6 +115,7 @@ class ProvingKey_ : public PrecomputedPolynomials, public WitnessPolynomials { auto get_all() { return concatenate(get_precomputed_polynomials(), get_witness_polynomials()); } auto get_witness_polynomials() { return WitnessPolynomials::get_all(); } auto get_precomputed_polynomials() { return PrecomputedPolynomials::get_all(); } + auto get_selectors() { return PrecomputedPolynomials::get_selectors(); } ProvingKey_() = default; ProvingKey_(const size_t circuit_size, const size_t num_public_inputs) { @@ -285,6 +286,9 @@ namespace bb { template concept IsPlonkFlavor = IsAnyOf; +template +concept IsUltraPlonkFlavor = IsAnyOf; + template concept IsHonkFlavor = IsAnyOf; diff --git a/barretenberg/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split.cpp b/barretenberg/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split.cpp index 73cae41dbee..da086414e95 100644 --- a/barretenberg/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split.cpp +++ b/barretenberg/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split.cpp @@ -62,7 +62,7 @@ Prover new_join_split_prover(join_split_tx const& tx, bool mock) info("public inputs: ", builder.public_inputs.size()); - Composer composer(proving_key, nullptr); + Composer composer; if (!mock) { info("composer gates: ", builder.get_num_gates()); return composer.create_prover(builder); diff --git a/barretenberg/cpp/src/barretenberg/plonk/composer/composer_lib.hpp b/barretenberg/cpp/src/barretenberg/plonk/composer/composer_lib.hpp index e34e8ba8654..0ca1c00e747 100644 --- a/barretenberg/cpp/src/barretenberg/plonk/composer/composer_lib.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk/composer/composer_lib.hpp @@ -10,39 +10,6 @@ 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, - bb::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 * diff --git a/barretenberg/cpp/src/barretenberg/plonk/composer/standard_composer.cpp b/barretenberg/cpp/src/barretenberg/plonk/composer/standard_composer.cpp index 16aab4bce43..42c713ca291 100644 --- a/barretenberg/cpp/src/barretenberg/plonk/composer/standard_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/plonk/composer/standard_composer.cpp @@ -14,39 +14,6 @@ namespace bb::plonk { -/** - * Compute witness polynomials (w_1, w_2, w_3, w_4). - * - * @details Fills 3 or 4 witness polynomials w_1, w_2, w_3, w_4 with the values of in-circuit variables. The beginning - * of w_1, w_2 polynomials is filled with public_input values. - * @return Witness with computed witness polynomials. - * - * @tparam Program settings needed to establish if w_4 is being used. - * */ -void StandardComposer::compute_witness(const CircuitBuilder& circuit_constructor, const size_t minimum_circuit_size) -{ - - if (computed_witness) { - return; - } - 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); - circuit_proving_key->polynomial_store.put("w_" + index + "_lagrange", - std::move(wire_polynomial_evaluations[j])); - } - computed_witness = true; -} - /** * Compute proving key * @@ -63,27 +30,30 @@ std::shared_ptr StandardComposer::compute_proving_key(const if (circuit_proving_key) { return circuit_proving_key; } - const size_t minimum_circuit_size = 0; - 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_constructor, crs_factory_.get(), minimum_circuit_size, num_randomized_gates, CircuitType::STANDARD); - // Compute lagrange selectors - construct_selector_polynomials(circuit_constructor, circuit_proving_key.get()); + + const size_t total_num_gates = + circuit_constructor.num_gates + circuit_constructor.public_inputs.size() + NUM_RESERVED_GATES; + const size_t subgroup_size = circuit_constructor.get_circuit_subgroup_size(total_num_gates); // next power of 2 + + auto crs = srs::get_crs_factory()->get_prover_crs(subgroup_size + 1); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/392): Composer type + circuit_proving_key = std::make_shared( + subgroup_size, circuit_constructor.public_inputs.size(), crs, CircuitType::STANDARD); + + // Construct and add to proving key the wire, selector and copy constraint polynomials + Trace::generate(circuit_constructor, circuit_proving_key); + // Make all selectors nonzero 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()); - // Compute sigma polynomials (we should update that late) - compute_standard_plonk_sigma_permutations(circuit_constructor, circuit_proving_key.get()); - circuit_proving_key->recursive_proof_public_input_indices = std::vector(circuit_constructor.recursive_proof_public_input_indices.begin(), circuit_constructor.recursive_proof_public_input_indices.end()); - // What does this line do exactly? + circuit_proving_key->contains_recursive_proof = circuit_constructor.contains_recursive_proof; + return circuit_proving_key; } @@ -144,12 +114,8 @@ plonk::Verifier StandardComposer::create_verifier(const CircuitBuilder& circuit_ * */ plonk::Prover StandardComposer::create_prover(const CircuitBuilder& circuit_constructor) { - // Compute q_l, etc. and sigma polynomials. compute_proving_key(circuit_constructor); - // Compute witness polynomials. - compute_witness(circuit_constructor); - plonk::Prover output_state(circuit_proving_key, create_manifest(circuit_constructor.public_inputs.size())); std::unique_ptr> permutation_widget = diff --git a/barretenberg/cpp/src/barretenberg/plonk/composer/standard_composer.hpp b/barretenberg/cpp/src/barretenberg/plonk/composer/standard_composer.hpp index 3961a59bc60..84b6632fa77 100644 --- a/barretenberg/cpp/src/barretenberg/plonk/composer/standard_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk/composer/standard_composer.hpp @@ -7,6 +7,7 @@ #include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" #include "barretenberg/plonk/proof_system/verifier/verifier.hpp" #include "barretenberg/proof_system/circuit_builder/standard_circuit_builder.hpp" +#include "barretenberg/proof_system/execution_trace/execution_trace.hpp" #include "barretenberg/srs/factories/file_crs_factory.hpp" #include @@ -16,6 +17,7 @@ class StandardComposer { using Flavor = plonk::flavor::Standard; using CircuitBuilder = StandardCircuitBuilder; + using Trace = ExecutionTrace_; static constexpr std::string_view NAME_STRING = "StandardPlonk"; static constexpr size_t NUM_RESERVED_GATES = 4; // equal to the number of evaluations leaked @@ -60,7 +62,6 @@ class StandardComposer { plonk::Verifier create_verifier(const CircuitBuilder& circuit_constructor); plonk::Prover create_prover(const CircuitBuilder& circuit_constructor); - void compute_witness(const CircuitBuilder& circuit_constructor, const size_t minimum_circuit_size = 0); /** * Create a manifest, which specifies proof rounds, elements and who supplies them. * diff --git a/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp b/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp index ad3aa5fd260..f42c1780807 100644 --- a/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp @@ -13,275 +13,78 @@ namespace bb::plonk { -/** - * @brief Computes `this.witness`, which is basiclly a set of polynomials mapped-to by strings. - * - * Note: this doesn't actually compute the _entire_ witness. Things missing: randomness for blinding both the wires and - * sorted `s` poly, lookup rows of the wire witnesses, the values of `z_lookup`, `z`. These are all calculated - * elsewhere. - */ - -void UltraComposer::compute_witness(CircuitBuilder& circuit_constructor) +void UltraComposer::construct_sorted_polynomials(CircuitBuilder& circuit, size_t subgroup_size) { - if (computed_witness) { - return; - } - - 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 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 subgroup_size = circuit_constructor.get_circuit_subgroup_size(total_num_gates + NUM_RESERVED_GATES); - - // Pad the wires (pointers to `witness_indices` of the `variables` vector). - // Note: the remaining NUM_RESERVED_GATES indices are padded with zeros within `compute_witness_base` (called - // next). - for (size_t i = filled_gates; i < total_num_gates; ++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); - } - - 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); - circuit_proving_key->polynomial_store.put("w_" + index + "_lagrange", - std::move(wire_polynomial_evaluations[j])); - } - - polynomial s_1(subgroup_size); - polynomial s_2(subgroup_size); - polynomial s_3(subgroup_size); - polynomial s_4(subgroup_size); - polynomial z_lookup(subgroup_size + 1); // Only instantiated in this function; nothing assigned. - - // Save space for adding random scalars in the s polynomial later. - // The subtracted 1 allows us to insert a `1` at the end, to ensure the evaluations (and hence coefficients) aren't - // all 0. - // See ComposerBase::compute_proving_key_base for further explanation, as a similar trick is done there. - size_t count = subgroup_size - tables_size - lookups_size - s_randomness - 1; - for (size_t i = 0; i < count; ++i) { - s_1[i] = 0; - s_2[i] = 0; - s_3[i] = 0; - s_4[i] = 0; - } - - for (auto& table : circuit_constructor.lookup_tables) { - const fr table_index(table.table_index); - auto& lookup_gates = table.lookup_gates; - for (size_t i = 0; i < table.size; ++i) { - if (table.use_twin_keys) { - lookup_gates.push_back({ - { - table.column_1[i].from_montgomery_form().data[0], - table.column_2[i].from_montgomery_form().data[0], - }, - { - table.column_3[i], - 0, - }, - }); - } else { - lookup_gates.push_back({ - { - table.column_1[i].from_montgomery_form().data[0], - 0, - }, - { - table.column_2[i], - table.column_3[i], - }, - }); - } - } - -#ifdef NO_TBB - std::sort(lookup_gates.begin(), lookup_gates.end()); -#else - std::sort(std::execution::par_unseq, lookup_gates.begin(), lookup_gates.end()); -#endif - - for (const auto& entry : lookup_gates) { - const auto components = entry.to_sorted_list_components(table.use_twin_keys); - s_1[count] = components[0]; - s_2[count] = components[1]; - s_3[count] = components[2]; - s_4[count] = table_index; - ++count; - } - } - - // Initialize the `s_randomness` positions in the s polynomials with 0. - // These will be the positions where we will be adding random scalars to add zero knowledge - // to plookup (search for `Blinding` in plonk/proof_system/widgets/random_widgets/plookup_widget_impl.hpp - // ProverPlookupWidget::compute_sorted_list_polynomial()) - for (size_t i = 0; i < s_randomness; ++i) { - s_1[count] = 0; - s_2[count] = 0; - s_3[count] = 0; - s_4[count] = 0; - ++count; - } - - circuit_proving_key->polynomial_store.put("s_1_lagrange", std::move(s_1)); - circuit_proving_key->polynomial_store.put("s_2_lagrange", std::move(s_2)); - circuit_proving_key->polynomial_store.put("s_3_lagrange", std::move(s_3)); - circuit_proving_key->polynomial_store.put("s_4_lagrange", std::move(s_4)); - - // 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; - }; - circuit_proving_key->memory_read_records = std::vector(); - circuit_proving_key->memory_write_records = std::vector(); - - std::transform(circuit_constructor.memory_read_records.begin(), - circuit_constructor.memory_read_records.end(), - std::back_inserter(circuit_proving_key->memory_read_records), - add_public_inputs_offset); - std::transform(circuit_constructor.memory_write_records.begin(), - circuit_constructor.memory_write_records.end(), - std::back_inserter(circuit_proving_key->memory_write_records), - add_public_inputs_offset); - - computed_witness = true; + // Save space in the sorted list polynomials for randomness (zk) plus one additional spot used to ensure the polys + // aren't identically 0. + size_t additional_offset = s_randomness + 1; + auto sorted_polynomials = construct_sorted_list_polynomials(circuit, subgroup_size, additional_offset); + + circuit_proving_key->polynomial_store.put("s_1_lagrange", std::move(sorted_polynomials[0])); + circuit_proving_key->polynomial_store.put("s_2_lagrange", std::move(sorted_polynomials[1])); + circuit_proving_key->polynomial_store.put("s_3_lagrange", std::move(sorted_polynomials[2])); + circuit_proving_key->polynomial_store.put("s_4_lagrange", std::move(sorted_polynomials[3])); } -UltraProver UltraComposer::create_prover(CircuitBuilder& circuit_constructor) +/** + * @brief Compute proving key and construct an Ultra Prover + */ +UltraProver UltraComposer::create_prover(CircuitBuilder& circuit) { - circuit_constructor.finalize_circuit(); - - compute_proving_key(circuit_constructor); - compute_witness(circuit_constructor); - - UltraProver output_state(circuit_proving_key, create_manifest(circuit_constructor.public_inputs.size())); - - auto permutation_widget = std::make_unique>(circuit_proving_key.get()); - auto plookup_widget = std::make_unique>(circuit_proving_key.get()); - auto arithmetic_widget = std::make_unique>(circuit_proving_key.get()); - auto sort_widget = std::make_unique>(circuit_proving_key.get()); - auto elliptic_widget = std::make_unique>(circuit_proving_key.get()); - auto auxiliary_widget = std::make_unique>(circuit_proving_key.get()); - - output_state.random_widgets.emplace_back(std::move(permutation_widget)); - output_state.random_widgets.emplace_back(std::move(plookup_widget)); - - output_state.transition_widgets.emplace_back(std::move(arithmetic_widget)); - output_state.transition_widgets.emplace_back(std::move(sort_widget)); - output_state.transition_widgets.emplace_back(std::move(elliptic_widget)); - output_state.transition_widgets.emplace_back(std::move(auxiliary_widget)); + compute_proving_key(circuit); - std::unique_ptr> kate_commitment_scheme = - std::make_unique>(); - - output_state.commitment_scheme = std::move(kate_commitment_scheme); - - return output_state; + return construct_prover(circuit); } /** - * @brief Uses slightly different settings from the UltraProver. + * @brief Compute proving key and construct an UltraToStandardProver Prover */ UltraToStandardProver UltraComposer::create_ultra_to_standard_prover(CircuitBuilder& circuit_constructor) { - circuit_constructor.finalize_circuit(); - compute_proving_key(circuit_constructor); - compute_witness(circuit_constructor); - - UltraToStandardProver output_state(circuit_proving_key, create_manifest(circuit_constructor.public_inputs.size())); - - std::unique_ptr> permutation_widget = - std::make_unique>(circuit_proving_key.get()); - - std::unique_ptr> plookup_widget = - std::make_unique>(circuit_proving_key.get()); - - std::unique_ptr> arithmetic_widget = - std::make_unique>(circuit_proving_key.get()); - std::unique_ptr> sort_widget = - std::make_unique>(circuit_proving_key.get()); - - std::unique_ptr> elliptic_widget = - std::make_unique>(circuit_proving_key.get()); - - std::unique_ptr> auxiliary_widget = - std::make_unique>(circuit_proving_key.get()); - - output_state.random_widgets.emplace_back(std::move(permutation_widget)); - output_state.random_widgets.emplace_back(std::move(plookup_widget)); - - output_state.transition_widgets.emplace_back(std::move(arithmetic_widget)); - output_state.transition_widgets.emplace_back(std::move(sort_widget)); - output_state.transition_widgets.emplace_back(std::move(elliptic_widget)); - output_state.transition_widgets.emplace_back(std::move(auxiliary_widget)); - - std::unique_ptr> kate_commitment_scheme = - std::make_unique>(); - - output_state.commitment_scheme = std::move(kate_commitment_scheme); - - return output_state; + return construct_prover(circuit_constructor); } /** - * @brief Uses slightly different settings from the UltraProver. + * @brief Compute proving key and construct an UltraWithKeccakProver Prover */ UltraWithKeccakProver UltraComposer::create_ultra_with_keccak_prover(CircuitBuilder& circuit_constructor) { - circuit_constructor.finalize_circuit(); compute_proving_key(circuit_constructor); - compute_witness(circuit_constructor); - UltraWithKeccakProver output_state(circuit_proving_key, create_manifest(circuit_constructor.public_inputs.size())); - - std::unique_ptr> permutation_widget = - std::make_unique>(circuit_proving_key.get()); - - std::unique_ptr> plookup_widget = - std::make_unique>(circuit_proving_key.get()); - - std::unique_ptr> arithmetic_widget = - std::make_unique>(circuit_proving_key.get()); - - std::unique_ptr> sort_widget = - std::make_unique>(circuit_proving_key.get()); - - std::unique_ptr> elliptic_widget = - std::make_unique>(circuit_proving_key.get()); + return construct_prover(circuit_constructor); +} - std::unique_ptr> auxiliary_widget = - std::make_unique>(circuit_proving_key.get()); +/** + * @brief Construct a Prover of given settings and populate it with the appropriate widgets + * + * @tparam settings + * @param circuit_constructor + * @return ProverBase + */ +template ProverBase UltraComposer::construct_prover(CircuitBuilder& circuit_constructor) +{ + ProverBase prover{ circuit_proving_key, create_manifest(circuit_constructor.public_inputs.size()) }; - output_state.random_widgets.emplace_back(std::move(permutation_widget)); - output_state.random_widgets.emplace_back(std::move(plookup_widget)); + auto permutation_widget = std::make_unique>(circuit_proving_key.get()); + auto plookup_widget = std::make_unique>(circuit_proving_key.get()); + auto arithmetic_widget = std::make_unique>(circuit_proving_key.get()); + auto sort_widget = std::make_unique>(circuit_proving_key.get()); + auto elliptic_widget = std::make_unique>(circuit_proving_key.get()); + auto auxiliary_widget = std::make_unique>(circuit_proving_key.get()); - output_state.transition_widgets.emplace_back(std::move(arithmetic_widget)); - output_state.transition_widgets.emplace_back(std::move(sort_widget)); - output_state.transition_widgets.emplace_back(std::move(elliptic_widget)); - output_state.transition_widgets.emplace_back(std::move(auxiliary_widget)); + prover.random_widgets.emplace_back(std::move(permutation_widget)); + prover.random_widgets.emplace_back(std::move(plookup_widget)); - std::unique_ptr> kate_commitment_scheme = - std::make_unique>(); + prover.transition_widgets.emplace_back(std::move(arithmetic_widget)); + prover.transition_widgets.emplace_back(std::move(sort_widget)); + prover.transition_widgets.emplace_back(std::move(elliptic_widget)); + prover.transition_widgets.emplace_back(std::move(auxiliary_widget)); - output_state.commitment_scheme = std::move(kate_commitment_scheme); + prover.commitment_scheme = std::make_unique>(); - return output_state; + return prover; } /** @@ -298,9 +101,7 @@ plonk::UltraVerifier UltraComposer::create_verifier(CircuitBuilder& circuit_cons plonk::UltraVerifier output_state(circuit_verification_key, create_manifest(circuit_constructor.public_inputs.size())); - auto kate_commitment_scheme = std::make_unique>(); - - output_state.commitment_scheme = std::move(kate_commitment_scheme); + output_state.commitment_scheme = std::make_unique>(); return output_state; } @@ -318,10 +119,7 @@ UltraToStandardVerifier UltraComposer::create_ultra_to_standard_verifier(Circuit UltraToStandardVerifier output_state(circuit_verification_key, create_manifest(circuit_constructor.public_inputs.size())); - std::unique_ptr> kate_commitment_scheme = - std::make_unique>(); - - output_state.commitment_scheme = std::move(kate_commitment_scheme); + output_state.commitment_scheme = std::make_unique>(); return output_state; } @@ -339,132 +137,58 @@ UltraWithKeccakVerifier UltraComposer::create_ultra_with_keccak_verifier(Circuit UltraWithKeccakVerifier output_state(circuit_verification_key, create_manifest(circuit_constructor.public_inputs.size())); - std::unique_ptr> kate_commitment_scheme = - std::make_unique>(); - - output_state.commitment_scheme = std::move(kate_commitment_scheme); + output_state.commitment_scheme = std::make_unique>(); return output_state; } -std::shared_ptr UltraComposer::compute_proving_key(CircuitBuilder& circuit_constructor) +size_t UltraComposer::compute_dyadic_circuit_size(CircuitBuilder& circuit) +{ + const size_t filled_gates = circuit.num_gates + circuit.public_inputs.size(); + const size_t size_required_for_lookups = circuit.get_tables_size() + circuit.get_lookups_size(); + const size_t total_num_gates = std::max(filled_gates, size_required_for_lookups); + return circuit.get_circuit_subgroup_size(total_num_gates + NUM_RESERVED_GATES); +} + +std::shared_ptr UltraComposer::compute_proving_key(CircuitBuilder& circuit) { if (circuit_proving_key) { return circuit_proving_key; } - circuit_constructor.finalize_circuit(); + circuit.finalize_circuit(); - 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 subgroup_size = compute_dyadic_circuit_size(circuit); - const size_t minimum_circuit_size = tables_size + lookups_size; - const size_t num_randomized_gates = NUM_RESERVED_GATES; - auto crs_factory = srs::get_crs_factory(); - // Initialize circuit_proving_key - // TODO(#392)(Kesha): replace composer types. - circuit_proving_key = initialize_proving_key( - circuit_constructor, crs_factory.get(), minimum_circuit_size, num_randomized_gates, CircuitType::ULTRA); + auto crs = srs::get_crs_factory()->get_prover_crs(subgroup_size + 1); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/392): Composer type + circuit_proving_key = + std::make_shared(subgroup_size, circuit.public_inputs.size(), crs, CircuitType::ULTRA); - construct_selector_polynomials(circuit_constructor, circuit_proving_key.get()); + // Construct and add to proving key the wire, selector and copy constraint polynomials + Trace::generate(circuit, circuit_proving_key); - enforce_nonzero_selector_polynomials(circuit_constructor, circuit_proving_key.get()); + enforce_nonzero_selector_polynomials(circuit, circuit_proving_key.get()); compute_monomial_and_coset_selector_forms(circuit_proving_key.get(), ultra_selector_properties()); - compute_plonk_generalized_sigma_permutations(circuit_constructor, circuit_proving_key.get()); - - const size_t subgroup_size = circuit_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); - - size_t offset = subgroup_size - tables_size - s_randomness - 1; - - // 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 - // put the table column's values at the end. (The first gates are for non-lookup constraints). - // [0, ..., 0, ...table, 0, 0, 0, x] - // ^^^^^^^^^ ^^^^^^^^ ^^^^^^^ ^nonzero to ensure uniqueness and to avoid infinity commitments - // | table randomness - // ignored, as used for regular constraints and padding to the next power of 2. - - for (size_t i = 0; i < offset; ++i) { - poly_q_table_column_1[i] = 0; - poly_q_table_column_2[i] = 0; - poly_q_table_column_3[i] = 0; - poly_q_table_column_4[i] = 0; - } - - for (const auto& table : circuit_constructor.lookup_tables) { - const fr table_index(table.table_index); - - for (size_t i = 0; i < table.size; ++i) { - poly_q_table_column_1[offset] = table.column_1[i]; - poly_q_table_column_2[offset] = table.column_2[i]; - poly_q_table_column_3[offset] = table.column_3[i]; - poly_q_table_column_4[offset] = table_index; - ++offset; - } - } - - // Initialize the last `s_randomness` positions in table polynomials with 0. We don't need to actually randomize - // the table polynomials. - for (size_t i = 0; i < s_randomness; ++i) { - poly_q_table_column_1[offset] = 0; - poly_q_table_column_2[offset] = 0; - poly_q_table_column_3[offset] = 0; - poly_q_table_column_4[offset] = 0; - ++offset; - } - - // // 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: - ASSERT(offset == subgroup_size - 1); - auto unique_last_value = - get_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; - - add_table_column_selector_poly_to_proving_key(poly_q_table_column_1, "table_value_1"); - add_table_column_selector_poly_to_proving_key(poly_q_table_column_2, "table_value_2"); - add_table_column_selector_poly_to_proving_key(poly_q_table_column_3, "table_value_3"); - add_table_column_selector_poly_to_proving_key(poly_q_table_column_4, "table_value_4"); + construct_table_polynomials(circuit, subgroup_size); // Instantiate z_lookup and s polynomials in the proving key (no values assigned yet). // Note: might be better to add these polys to cache only after they've been computed, as is convention - // TODO(luke): Don't put empty polynomials in the store, just add these where they're computed polynomial z_lookup_fft(subgroup_size * 4); polynomial s_fft(subgroup_size * 4); circuit_proving_key->polynomial_store.put("z_lookup_fft", std::move(z_lookup_fft)); circuit_proving_key->polynomial_store.put("s_fft", std::move(s_fft)); - circuit_proving_key->recursive_proof_public_input_indices = - std::vector(circuit_constructor.recursive_proof_public_input_indices.begin(), - circuit_constructor.recursive_proof_public_input_indices.end()); + circuit_proving_key->recursive_proof_public_input_indices = std::vector( + circuit.recursive_proof_public_input_indices.begin(), circuit.recursive_proof_public_input_indices.end()); + + circuit_proving_key->contains_recursive_proof = circuit.contains_recursive_proof; - circuit_proving_key->contains_recursive_proof = circuit_constructor.contains_recursive_proof; + construct_sorted_polynomials(circuit, subgroup_size); + + populate_memory_read_write_records(circuit, circuit_proving_key); return circuit_proving_key; } @@ -481,12 +205,11 @@ std::shared_ptr UltraComposer::compute_verification_key return circuit_verification_key; } - auto crs_factory = srs::get_crs_factory(); - if (!circuit_proving_key) { compute_proving_key(circuit_constructor); } - circuit_verification_key = compute_verification_key_common(circuit_proving_key, crs_factory->get_verifier_crs()); + circuit_verification_key = + compute_verification_key_common(circuit_proving_key, srs::get_crs_factory()->get_verifier_crs()); circuit_verification_key->circuit_type = CircuitType::ULTRA; @@ -518,4 +241,37 @@ void UltraComposer::add_table_column_selector_poly_to_proving_key(polynomial& se circuit_proving_key->polynomial_store.put(tag + "_fft", std::move(selector_poly_coset_form)); } +void UltraComposer::construct_table_polynomials(CircuitBuilder& circuit, size_t subgroup_size) +{ + size_t additional_offset = s_randomness + 1; + auto table_polynomials = construct_lookup_table_polynomials(circuit, subgroup_size, additional_offset); + + // // 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: + // ASSERT(offset == subgroup_size - 1); + auto unique_last_value = + get_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. + table_polynomials[0][subgroup_size - 1] = unique_last_value; + table_polynomials[1][subgroup_size - 1] = ++unique_last_value; + table_polynomials[2][subgroup_size - 1] = ++unique_last_value; + table_polynomials[3][subgroup_size - 1] = ++unique_last_value; + + add_table_column_selector_poly_to_proving_key(table_polynomials[0], "table_value_1"); + add_table_column_selector_poly_to_proving_key(table_polynomials[1], "table_value_2"); + add_table_column_selector_poly_to_proving_key(table_polynomials[2], "table_value_3"); + add_table_column_selector_poly_to_proving_key(table_polynomials[3], "table_value_4"); +} } // namespace bb::plonk diff --git a/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp b/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp index e95888e9f52..d6f01e6f43e 100644 --- a/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp @@ -7,6 +7,7 @@ #include "barretenberg/plonk/proof_system/verifier/verifier.hpp" #include "barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp" #include "barretenberg/proof_system/composer/composer_lib.hpp" +#include "barretenberg/proof_system/execution_trace/execution_trace.hpp" #include "barretenberg/srs/factories/file_crs_factory.hpp" #include @@ -18,6 +19,7 @@ class UltraComposer { using Flavor = flavor::Ultra; using CircuitBuilder = UltraCircuitBuilder; using Curve = Flavor::Curve; + using Trace = ExecutionTrace_; static constexpr std::string_view NAME_STRING = "UltraPlonk"; static constexpr CircuitType type = CircuitType::ULTRA; @@ -71,8 +73,6 @@ class UltraComposer { std::shared_ptr compute_proving_key(CircuitBuilder& circuit_constructor); std::shared_ptr compute_verification_key(CircuitBuilder& circuit_constructor); - void compute_witness(CircuitBuilder& circuit_constructor); - UltraProver create_prover(CircuitBuilder& circuit_constructor); UltraVerifier create_verifier(CircuitBuilder& circuit_constructor); @@ -84,6 +84,8 @@ class UltraComposer { void add_table_column_selector_poly_to_proving_key(polynomial& small, const std::string& tag); + size_t compute_dyadic_circuit_size(CircuitBuilder& circuit_constructor); + /** * @brief Create a manifest object * @@ -95,6 +97,26 @@ class UltraComposer { { return Flavor::create_manifest(num_public_inputs); } -}; + private: + /** + * @brief Construct a prover given a proving key and populate it with the appropriate widgets + */ + template ProverBase construct_prover(CircuitBuilder& circuit_constructor); + + /** + * @brief Construct sorted concatenated table-lookup polynomials for lookup argument + */ + void construct_sorted_polynomials(CircuitBuilder& circuit_constructor, size_t subgroup_size); + + /** + * @brief Populate proving key with memory read/write records + */ + void populate_memory_records(CircuitBuilder& circuit_constructor); + + /** + * @brief Construct polynomials containing concatenation of the lookup tables + */ + void construct_table_polynomials(CircuitBuilder& circuit_constructor, size_t subgroup_size); +}; } // namespace bb::plonk diff --git a/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp b/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp index 0fd5c17b8ac..683ed6ba372 100644 --- a/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp @@ -32,8 +32,9 @@ template class ultra_plonk_composer : public ::testing::Test { public: static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } - void prove_and_verify(UltraCircuitBuilder& builder, UltraComposer& composer, bool expected_result) + void prove_and_verify(UltraCircuitBuilder& builder, bool expected_result) { + auto composer = UltraComposer(); if constexpr (T::use_keccak) { auto prover = composer.create_ultra_with_keccak_prover(builder); auto verifier = composer.create_ultra_with_keccak_verifier(builder); @@ -64,7 +65,6 @@ TYPED_TEST_SUITE(ultra_plonk_composer, BooleanTypes); TYPED_TEST(ultra_plonk_composer, create_gates_from_plookup_accumulators) { auto circuit_builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); fr input_value = fr::random_element(); const fr input_lo = static_cast(input_value).slice(0, plookup::fixed_base::table::BITS_PER_LO_SCALAR); @@ -119,13 +119,12 @@ TYPED_TEST(ultra_plonk_composer, create_gates_from_plookup_accumulators) } } - TestFixture::prove_and_verify(circuit_builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true); } TYPED_TEST(ultra_plonk_composer, test_no_lookup_proof) { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); for (size_t i = 0; i < 16; ++i) { for (size_t j = 0; j < 16; ++j) { @@ -141,7 +140,7 @@ TYPED_TEST(ultra_plonk_composer, test_no_lookup_proof) } } - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } TYPED_TEST(ultra_plonk_composer, test_elliptic_gate) @@ -149,7 +148,6 @@ TYPED_TEST(ultra_plonk_composer, test_elliptic_gate) typedef grumpkin::g1::affine_element affine_element; typedef grumpkin::g1::element element; auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); affine_element p1 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 0); @@ -176,13 +174,13 @@ TYPED_TEST(ultra_plonk_composer, test_elliptic_gate) y3 = builder.add_variable(p3.y); builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, -1 }); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } TYPED_TEST(ultra_plonk_composer, non_trivial_tag_permutation) { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + fr a = fr::random_element(); fr b = -a; @@ -207,13 +205,13 @@ TYPED_TEST(ultra_plonk_composer, non_trivial_tag_permutation) // fr::zero() }); builder.create_add_gate({ a_idx, b_idx, builder.zero_idx, fr::one(), fr::neg_one(), // fr::zero(), fr::zero() }); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } TYPED_TEST(ultra_plonk_composer, non_trivial_tag_permutation_and_cycles) { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + fr a = fr::random_element(); fr c = -a; @@ -247,14 +245,14 @@ TYPED_TEST(ultra_plonk_composer, non_trivial_tag_permutation_and_cycles) // fr::zero() }); builder.create_add_gate({ a_idx, b_idx, builder.zero_idx, fr::one(), fr::neg_one(), // fr::zero(), fr::zero() }); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } TYPED_TEST(ultra_plonk_composer, bad_tag_permutation) { { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + fr a = fr::random_element(); fr b = -a; @@ -274,12 +272,12 @@ TYPED_TEST(ultra_plonk_composer, bad_tag_permutation) builder.assign_tag(c_idx, 2); builder.assign_tag(d_idx, 2); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/false); + TestFixture::prove_and_verify(builder, /*expected_result=*/false); } // Same as above but without tag creation to check reason of failure is really tag mismatch { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + fr a = fr::random_element(); fr b = -a; @@ -291,17 +289,14 @@ TYPED_TEST(ultra_plonk_composer, bad_tag_permutation) builder.create_add_gate({ a_idx, b_idx, builder.zero_idx, 1, 1, 0, 0 }); builder.create_add_gate({ c_idx, d_idx, builder.zero_idx, 1, 1, 0, -1 }); - auto prover = composer.create_prover(builder); - auto verifier = composer.create_verifier(builder); - - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } } TYPED_TEST(ultra_plonk_composer, sort_widget) { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + fr a = fr::one(); fr b = fr(2); fr c = fr(3); @@ -313,7 +308,7 @@ TYPED_TEST(ultra_plonk_composer, sort_widget) auto d_idx = builder.add_variable(d); builder.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } TYPED_TEST(ultra_plonk_composer, sort_with_edges_gate) @@ -329,7 +324,7 @@ TYPED_TEST(ultra_plonk_composer, sort_with_edges_gate) { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + auto a_idx = builder.add_variable(a); auto b_idx = builder.add_variable(b); auto c_idx = builder.add_variable(c); @@ -340,12 +335,12 @@ TYPED_TEST(ultra_plonk_composer, sort_with_edges_gate) auto h_idx = builder.add_variable(h); builder.create_sort_constraint_with_edges({ a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, h); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + auto a_idx = builder.add_variable(a); auto b_idx = builder.add_variable(b); auto c_idx = builder.add_variable(c); @@ -355,17 +350,12 @@ TYPED_TEST(ultra_plonk_composer, sort_with_edges_gate) auto g_idx = builder.add_variable(g); auto h_idx = builder.add_variable(h); builder.create_sort_constraint_with_edges({ a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, g); - auto prover = composer.create_prover(builder); - auto verifier = composer.create_verifier(builder); - - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); - EXPECT_EQ(result, false); + TestFixture::prove_and_verify(builder, /*expected_result=*/false); } { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + auto a_idx = builder.add_variable(a); auto b_idx = builder.add_variable(b); auto c_idx = builder.add_variable(c); @@ -376,11 +366,11 @@ TYPED_TEST(ultra_plonk_composer, sort_with_edges_gate) auto h_idx = builder.add_variable(h); builder.create_sort_constraint_with_edges({ a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, b, h); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/false); + TestFixture::prove_and_verify(builder, /*expected_result=*/false); } { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + auto a_idx = builder.add_variable(a); auto c_idx = builder.add_variable(c); auto d_idx = builder.add_variable(d); @@ -391,26 +381,26 @@ TYPED_TEST(ultra_plonk_composer, sort_with_edges_gate) auto b2_idx = builder.add_variable(fr(15)); builder.create_sort_constraint_with_edges({ a_idx, b2_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, b, h); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/false); + TestFixture::prove_and_verify(builder, /*expected_result=*/false); } { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + auto idx = add_variables(builder, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25, 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 }); builder.create_sort_constraint_with_edges(idx, 1, 45); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + auto idx = add_variables(builder, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25, 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 }); builder.create_sort_constraint_with_edges(idx, 1, 29); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/false); + TestFixture::prove_and_verify(builder, /*expected_result=*/false); } } @@ -418,24 +408,19 @@ TYPED_TEST(ultra_plonk_composer, range_constraint) { { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + auto indices = add_variables(builder, { 1, 2, 3, 4, 5, 6, 7, 8 }); for (size_t i = 0; i < indices.size(); i++) { builder.create_new_range_constraint(indices[i], 8); } // auto ind = {a_idx,b_idx,c_idx,d_idx,e_idx,f_idx,g_idx,h_idx}; builder.create_sort_constraint(indices); - auto prover = composer.create_prover(builder); - auto verifier = composer.create_verifier(builder); - - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + auto indices = add_variables(builder, { 3 }); for (size_t i = 0; i < indices.size(); i++) { builder.create_new_range_constraint(indices[i], 3); @@ -443,22 +428,22 @@ TYPED_TEST(ultra_plonk_composer, range_constraint) // auto ind = {a_idx,b_idx,c_idx,d_idx,e_idx,f_idx,g_idx,h_idx}; builder.create_dummy_constraints(indices); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + auto indices = add_variables(builder, { 1, 2, 3, 4, 5, 6, 8, 25 }); for (size_t i = 0; i < indices.size(); i++) { builder.create_new_range_constraint(indices[i], 8); } builder.create_sort_constraint(indices); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/false); + TestFixture::prove_and_verify(builder, /*expected_result=*/false); } { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + auto indices = add_variables(builder, { 1, 2, 3, 4, 5, 6, 10, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 19, 51 }); for (size_t i = 0; i < indices.size(); i++) { @@ -466,28 +451,23 @@ TYPED_TEST(ultra_plonk_composer, range_constraint) } builder.create_dummy_constraints(indices); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + auto indices = add_variables(builder, { 1, 2, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 }); for (size_t i = 0; i < indices.size(); i++) { builder.create_new_range_constraint(indices[i], 79); } builder.create_dummy_constraints(indices); - auto prover = composer.create_prover(builder); - auto verifier = composer.create_verifier(builder); - - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); - EXPECT_EQ(result, false); + TestFixture::prove_and_verify(builder, /*expected_result=*/false); } { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + auto indices = add_variables(builder, { 1, 0, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 }); for (size_t i = 0; i < indices.size(); i++) { @@ -495,14 +475,14 @@ TYPED_TEST(ultra_plonk_composer, range_constraint) } builder.create_dummy_constraints(indices); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/false); + TestFixture::prove_and_verify(builder, /*expected_result=*/false); } } TYPED_TEST(ultra_plonk_composer, range_with_gates) { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + auto idx = add_variables(builder, { 1, 2, 3, 4, 5, 6, 7, 8 }); for (size_t i = 0; i < idx.size(); i++) { builder.create_new_range_constraint(idx[i], 8); @@ -513,13 +493,13 @@ TYPED_TEST(ultra_plonk_composer, range_with_gates) builder.create_add_gate({ idx[4], idx[5], builder.zero_idx, fr::one(), fr::one(), fr::zero(), -11 }); builder.create_add_gate({ idx[6], idx[7], builder.zero_idx, fr::one(), fr::one(), fr::zero(), -15 }); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } TYPED_TEST(ultra_plonk_composer, range_with_gates_where_range_is_not_a_power_of_two) { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + auto idx = add_variables(builder, { 1, 2, 3, 4, 5, 6, 7, 8 }); for (size_t i = 0; i < idx.size(); i++) { builder.create_new_range_constraint(idx[i], 12); @@ -530,7 +510,7 @@ TYPED_TEST(ultra_plonk_composer, range_with_gates_where_range_is_not_a_power_of_ builder.create_add_gate({ idx[4], idx[5], builder.zero_idx, fr::one(), fr::one(), fr::zero(), -11 }); builder.create_add_gate({ idx[6], idx[7], builder.zero_idx, fr::one(), fr::one(), fr::zero(), -15 }); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } TYPED_TEST(ultra_plonk_composer, sort_widget_complex) @@ -538,37 +518,32 @@ TYPED_TEST(ultra_plonk_composer, sort_widget_complex) { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + std::vector a = { 1, 3, 4, 7, 7, 8, 11, 14, 15, 15, 18, 19, 21, 21, 24, 25, 26, 27, 30, 32 }; std::vector ind; for (size_t i = 0; i < a.size(); i++) ind.emplace_back(builder.add_variable(a[i])); builder.create_sort_constraint(ind); - auto prover = composer.create_prover(builder); - auto verifier = composer.create_verifier(builder); - - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + std::vector a = { 1, 3, 4, 7, 7, 8, 16, 14, 15, 15, 18, 19, 21, 21, 24, 25, 26, 27, 30, 32 }; std::vector ind; for (size_t i = 0; i < a.size(); i++) ind.emplace_back(builder.add_variable(a[i])); builder.create_sort_constraint(ind); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/false); + TestFixture::prove_and_verify(builder, /*expected_result=*/false); } } TYPED_TEST(ultra_plonk_composer, sort_widget_neg) { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + fr a = fr::one(); fr b = fr(2); fr c = fr(3); @@ -580,13 +555,13 @@ TYPED_TEST(ultra_plonk_composer, sort_widget_neg) auto d_idx = builder.add_variable(d); builder.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/false); + TestFixture::prove_and_verify(builder, /*expected_result=*/false); } TYPED_TEST(ultra_plonk_composer, composed_range_constraint) { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + auto c = fr::random_element(); auto d = uint256_t(c).slice(0, 133); auto e = fr(d); @@ -594,13 +569,12 @@ TYPED_TEST(ultra_plonk_composer, composed_range_constraint) builder.create_add_gate({ a_idx, builder.zero_idx, builder.zero_idx, 1, 0, 0, -fr(e) }); builder.decompose_into_default_range(a_idx, 134); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } TYPED_TEST(ultra_plonk_composer, non_native_field_multiplication) { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); fq a = fq::random_element(); fq b = fq::random_element(); @@ -650,13 +624,12 @@ TYPED_TEST(ultra_plonk_composer, non_native_field_multiplication) const auto [lo_1_idx, hi_1_idx] = builder.evaluate_non_native_field_multiplication(inputs); builder.range_constrain_two_limbs(lo_1_idx, hi_1_idx, 70, 70); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } TYPED_TEST(ultra_plonk_composer, rom) { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); uint32_t rom_values[8]{ builder.add_variable(fr::random_element()), builder.add_variable(fr::random_element()), @@ -691,13 +664,12 @@ TYPED_TEST(ultra_plonk_composer, rom) 0, }); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } TYPED_TEST(ultra_plonk_composer, ram) { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); uint32_t ram_values[8]{ builder.add_variable(fr::random_element()), builder.add_variable(fr::random_element()), @@ -755,13 +727,12 @@ TYPED_TEST(ultra_plonk_composer, ram) }, false); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } TYPED_TEST(ultra_plonk_composer, range_checks_on_duplicates) { auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); uint32_t a = builder.add_variable(100); uint32_t b = builder.add_variable(100); @@ -791,7 +762,7 @@ TYPED_TEST(ultra_plonk_composer, range_checks_on_duplicates) }, false); - TestFixture::prove_and_verify(builder, composer, /*expected_result=*/true); + TestFixture::prove_and_verify(builder, /*expected_result=*/true); } // Ensure copy constraints added on variables smaller than 2^14, which have been previously @@ -800,8 +771,9 @@ TYPED_TEST(ultra_plonk_composer, range_checks_on_duplicates) // before range constraints are applied to it. TEST(ultra_plonk_composer, range_constraint_small_variable) { + bb::srs::init_crs_factory("../srs_db/ignition"); auto builder = UltraCircuitBuilder(); - auto composer = UltraComposer(); + uint16_t mask = (1 << 8) - 1; int a = engine.get_random_uint16() & mask; uint32_t a_idx = builder.add_variable(fr(a)); @@ -814,6 +786,7 @@ TEST(ultra_plonk_composer, range_constraint_small_variable) builder.create_range_constraint(c_idx, 8, "bad range"); builder.assert_equal(a_idx, c_idx); + UltraComposer composer; auto prover = composer.create_prover(builder); auto proof = prover.construct_proof(); auto verifier = composer.create_verifier(builder); diff --git a/barretenberg/cpp/src/barretenberg/proof_system/arithmetization/arithmetization.hpp b/barretenberg/cpp/src/barretenberg/proof_system/arithmetization/arithmetization.hpp index a5110aefa35..9001beef281 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/arithmetization/arithmetization.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/arithmetization/arithmetization.hpp @@ -40,7 +40,7 @@ template class StandardArith { using FF = FF_; using SelectorType = std::vector>; - std::vector selectors; + std::array selectors; SelectorType& q_m() { return selectors[0]; }; SelectorType& q_1() { return selectors[1]; }; @@ -54,11 +54,7 @@ template class StandardArith { const SelectorType& q_3() const { return selectors[3]; }; const SelectorType& q_c() const { return selectors[4]; }; - StandardArith() - : selectors(NUM_SELECTORS) - {} - - const auto& get() const { return selectors; }; + auto& get() { return selectors; }; void reserve(size_t size_hint) { @@ -106,7 +102,7 @@ template class UltraArith { const SelectorType& q_aux() const { return selectors[9]; }; const SelectorType& q_lookup_type() const { return selectors[10]; }; - const auto& get() const { return selectors; }; + auto& get() { return selectors; }; void reserve(size_t size_hint) { @@ -168,7 +164,7 @@ template class UltraHonkArith { const SelectorType& q_poseidon2_external() const { return this->selectors[12]; }; const SelectorType& q_poseidon2_internal() const { return this->selectors[13]; }; - const auto& get() const { return selectors; }; + auto& get() { return selectors; }; void reserve(size_t size_hint) { diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.cpp index e4181fecc6b..be0aa89d1b1 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.cpp @@ -211,11 +211,17 @@ template void GoblinUltraCircuitBuilder_::populate_ecc_op_wire 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); + for (auto& selector : ecc_op_block.selectors.get()) { + selector.emplace_back(0); + } ecc_op_wire_1().emplace_back(this->zero_idx); 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); + for (auto& selector : ecc_op_block.selectors.get()) { + selector.emplace_back(0); + } num_ecc_op_gates += 2; }; diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp index a338b88549e..22e707f5b35 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp @@ -1,5 +1,6 @@ #pragma once #include "barretenberg/proof_system/arithmetization/arithmetization.hpp" +#include "barretenberg/proof_system/execution_trace/execution_trace.hpp" #include "barretenberg/proof_system/op_queue/ecc_op_queue.hpp" #include "ultra_circuit_builder.hpp" @@ -9,6 +10,9 @@ using namespace bb; template class GoblinUltraCircuitBuilder_ : public UltraCircuitBuilder_> { public: + using Arithmetization = UltraHonkArith; + using TraceBlock = ExecutionTraceBlock; + 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 = @@ -28,18 +32,18 @@ template class GoblinUltraCircuitBuilder_ : public UltraCircuitBui using WireVector = std::vector>; using SelectorVector = std::vector>; - // Wires storing ecc op queue data; values are indices into the variables array - std::array::NUM_WIRES> ecc_op_wires; + // Execution trace block for goblin ecc op gates + TraceBlock ecc_op_block; - WireVector& ecc_op_wire_1() { return std::get<0>(ecc_op_wires); }; - WireVector& ecc_op_wire_2() { return std::get<1>(ecc_op_wires); }; - WireVector& ecc_op_wire_3() { return std::get<2>(ecc_op_wires); }; - WireVector& ecc_op_wire_4() { return std::get<3>(ecc_op_wires); }; + WireVector& ecc_op_wire_1() { return std::get<0>(ecc_op_block.wires); }; + WireVector& ecc_op_wire_2() { return std::get<1>(ecc_op_block.wires); }; + WireVector& ecc_op_wire_3() { return std::get<2>(ecc_op_block.wires); }; + WireVector& ecc_op_wire_4() { return std::get<3>(ecc_op_block.wires); }; - const WireVector& ecc_op_wire_1() const { return std::get<0>(ecc_op_wires); }; - const WireVector& ecc_op_wire_2() const { return std::get<1>(ecc_op_wires); }; - const WireVector& ecc_op_wire_3() const { return std::get<2>(ecc_op_wires); }; - const WireVector& ecc_op_wire_4() const { return std::get<3>(ecc_op_wires); }; + const WireVector& ecc_op_wire_1() const { return std::get<0>(ecc_op_block.wires); }; + const WireVector& ecc_op_wire_2() const { return std::get<1>(ecc_op_block.wires); }; + const WireVector& ecc_op_wire_3() const { return std::get<2>(ecc_op_block.wires); }; + const WireVector& ecc_op_wire_4() const { return std::get<3>(ecc_op_block.wires); }; SelectorVector& q_busread() { return this->selectors.q_busread(); }; SelectorVector& q_poseidon2_external() { return this->selectors.q_poseidon2_external(); }; diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.test.cpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.test.cpp index 4e2b8566715..766686501ae 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.test.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.test.cpp @@ -150,7 +150,7 @@ TEST(GoblinUltraCircuitBuilder, GoblinEccOpQueueUltraOps) auto ultra_ops = builder.op_queue->get_aggregate_transcript(); for (size_t i = 1; i < 4; ++i) { for (size_t j = 0; j < builder.num_ecc_op_gates; ++j) { - auto op_wire_val = builder.variables[builder.ecc_op_wires[i][j]]; + auto op_wire_val = builder.variables[builder.ecc_op_block.wires[i][j]]; auto ultra_op_val = ultra_ops[i][j]; ASSERT_EQ(op_wire_val, ultra_op_val); } diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/standard_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/standard_circuit_builder.hpp index a8c1fa20436..77c3e87e94a 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/standard_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/standard_circuit_builder.hpp @@ -12,6 +12,7 @@ namespace bb { template class StandardCircuitBuilder_ : public CircuitBuilderBase { public: using Arithmetization = StandardArith; + using Selectors = Arithmetization; static constexpr size_t NUM_WIRES = Arithmetization::NUM_WIRES; // Keeping NUM_WIRES, at least temporarily, for backward compatibility static constexpr size_t program_width = Arithmetization::NUM_WIRES; diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp index 78242840d9c..d9e533c15c1 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp @@ -1,7 +1,4 @@ #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/op_queue/ecc_op_queue.hpp" #include "barretenberg/proof_system/plookup_tables/plookup_tables.hpp" @@ -30,6 +27,7 @@ using namespace bb; template class UltraCircuitBuilder_ : public CircuitBuilderBase { public: + using Selectors = Arithmetization; using FF = typename Arithmetization::FF; static constexpr size_t NUM_WIRES = Arithmetization::NUM_WIRES; // Keeping NUM_WIRES, at least temporarily, for backward compatibility @@ -946,6 +944,32 @@ class UltraCircuitBuilder_ : public CircuitBuilderBasepublic_inputs.size(); return std::max(minimum_circuit_size, num_filled_gates) + NUM_RESERVED_GATES; } diff --git a/barretenberg/cpp/src/barretenberg/proof_system/composer/composer_lib.hpp b/barretenberg/cpp/src/barretenberg/proof_system/composer/composer_lib.hpp index 2312f3983a8..ebeff300a37 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/composer/composer_lib.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/composer/composer_lib.hpp @@ -7,142 +7,148 @@ namespace bb { /** - * @brief Construct selector polynomials from circuit selector information and put into polynomial cache + * @brief Copy memory read/write record data into proving key + * @details 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 * * @tparam Flavor - * @param circuit_constructor The object holding the circuit - * @param key Pointer to the proving key + * @param circuit + * @param proving_key */ template -void construct_selector_polynomials(const typename Flavor::CircuitBuilder& circuit_constructor, - typename Flavor::ProvingKey* proving_key) +void populate_memory_read_write_records(const typename Flavor::CircuitBuilder& circuit, + const std::shared_ptr& proving_key) { - // 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; - size_t gate_offset = zero_row_offset + circuit_constructor.public_inputs.size(); - - // If Goblin, (1) update the conventional gate offset to account for ecc op gates at the top of the execution trace, - // and (2) construct ecc op gate selector polynomial. This selector is handled separately from the others since it - // is computable based simply on num_ecc_op_gates and thus is not constructed explicitly in the builder. - // Note 1: All other selectors will be automatically and correctly initialized to 0 on this domain. - // Note 2: If applicable, the ecc op gates are shifted down by 1 to account for a zero row. + // Determine offset of conventional gates in execution trace + auto offset = static_cast(circuit.public_inputs.size()); + if (Flavor::has_zero_row) { + offset += 1; + } if constexpr (IsGoblinFlavor) { - const size_t num_ecc_op_gates = circuit_constructor.num_ecc_op_gates; - gate_offset += num_ecc_op_gates; - const size_t op_gate_offset = zero_row_offset; - // The op gate selector is simply the indicator on the domain [offset, num_ecc_op_gates + offset - 1] - bb::polynomial ecc_op_selector(proving_key->circuit_size); - for (size_t i = 0; i < num_ecc_op_gates; ++i) { - ecc_op_selector[i + op_gate_offset] = 1; - } - proving_key->lagrange_ecc_op = ecc_op_selector.share(); + offset += static_cast(circuit.num_ecc_op_gates); } + auto add_public_inputs_offset = [offset](uint32_t gate_index) { return gate_index + offset; }; + proving_key->memory_read_records = std::vector(); + proving_key->memory_write_records = std::vector(); + + std::transform(circuit.memory_read_records.begin(), + circuit.memory_read_records.end(), + std::back_inserter(proving_key->memory_read_records), + add_public_inputs_offset); + std::transform(circuit.memory_write_records.begin(), + circuit.memory_write_records.end(), + std::back_inserter(proving_key->memory_write_records), + add_public_inputs_offset); +} - // TODO(#398): Loose coupling here! Would rather build up pk from arithmetization - if constexpr (IsHonkFlavor) { - for (auto [poly, selector_values] : zip_view(ZipAllowDifferentSizes::FLAG, - proving_key->get_precomputed_polynomials(), - circuit_constructor.selectors.get())) { - 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. - typename Flavor::Polynomial selector_poly_lagrange(proving_key->circuit_size); - for (size_t i = 0; i < selector_values.size(); ++i) { - selector_poly_lagrange[i + gate_offset] = selector_values[i]; - } - poly = selector_poly_lagrange.share(); - } - } else if constexpr (IsPlonkFlavor) { - size_t selector_idx = 0; - for (auto& selector_values : circuit_constructor.selectors.get()) { - ASSERT(proving_key->circuit_size >= selector_values.size()); +template +std::array construct_lookup_table_polynomials( + const typename Flavor::CircuitBuilder& circuit, size_t dyadic_circuit_size, size_t additional_offset = 0) +{ + using Polynomial = typename Flavor::Polynomial; + std::array table_polynomials; + for (auto& poly : table_polynomials) { + poly = Polynomial(dyadic_circuit_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. - typename Flavor::Polynomial selector_poly_lagrange(proving_key->circuit_size); - for (size_t i = 0; i < selector_values.size(); ++i) { - selector_poly_lagrange[i + gate_offset] = selector_values[i]; - } - // 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; + // 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 + // put the table column's values at the end. (The first gates are for non-lookup constraints). + // [0, ..., 0, ...table, 0, 0, 0, x] + // ^^^^^^^^^ ^^^^^^^^ ^^^^^^^ ^nonzero to ensure uniqueness and to avoid infinity commitments + // | table randomness + // ignored, as used for regular constraints and padding to the next power of 2. + size_t offset = dyadic_circuit_size - circuit.get_tables_size() - additional_offset; + + for (const auto& table : circuit.lookup_tables) { + const fr table_index(table.table_index); + + for (size_t i = 0; i < table.size; ++i) { + table_polynomials[0][offset] = table.column_1[i]; + table_polynomials[1][offset] = table.column_2[i]; + table_polynomials[2][offset] = table.column_3[i]; + table_polynomials[3][offset] = table_index; + ++offset; } } + return table_polynomials; } /** - * @brief Construct the witness polynomials from the witness vectors in the circuit constructor. + * @brief Construct polynomials containing the sorted concatenation of the lookups and the lookup tables * - * @details We can think of the entries in the wire polynomials as reflecting the structure of the circuit execution - * trace. The execution trace is broken up into several distinct blocks, depending on Flavor. For example, for Goblin - * Ultra Honk, the order is: leading zero row, ECC op gates, public inputs, conventional wires. The actual values used - * to populate the wire polynomials are determined by corresponding arrays of indices into the variables vector of the - * circuit builder, and their location in the polynomials is determined by applying the appropriate "offset" for the - * corresponding block. - * - * @tparam Flavor provides the circuit constructor type and the number of wires. - * @param circuit_constructor - * @param dyadic_circuit_size Power of 2 circuit size - * - * @return std::vector - * */ + * @tparam Flavor + * @param circuit + * @param dyadic_circuit_size + * @param additional_offset Additional space needed in polynomials to add randomness for zk (Plonk only) + * @return std::array + */ template -std::vector construct_wire_polynomials_base( - const typename Flavor::CircuitBuilder& circuit_constructor, const size_t dyadic_circuit_size) +std::array construct_sorted_list_polynomials(typename Flavor::CircuitBuilder& circuit, + const size_t dyadic_circuit_size, + size_t additional_offset = 0) { - // Determine size of each block of data in the wire polynomials - const size_t num_zero_rows = Flavor::has_zero_row ? 1 : 0; - 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(); - size_t num_ecc_op_gates = 0; - if constexpr (IsGoblinFlavor) { - num_ecc_op_gates = circuit_constructor.num_ecc_op_gates; + using Polynomial = typename Flavor::Polynomial; + std::array sorted_polynomials; + // Initialise the sorted concatenated list polynomials for the lookup argument + for (auto& s_i : sorted_polynomials) { + s_i = Polynomial(dyadic_circuit_size); } - // Define offsets at which to start writing different blocks of data - size_t op_gate_offset = num_zero_rows; - size_t pub_input_offset = num_zero_rows + num_ecc_op_gates; - size_t gate_offset = num_zero_rows + num_ecc_op_gates + num_public_inputs; - - std::vector wire_polynomials; - - // Populate the wire polynomials with values from ecc op gates, public inputs and conventional wires - for (size_t wire_idx = 0; wire_idx < Flavor::NUM_WIRES; ++wire_idx) { - - // Expect all values to be set to 0 initially - typename Flavor::Polynomial w_lagrange(dyadic_circuit_size); - - // Insert leading zero row into wire poly (for clarity; not stricly necessary due to zero-initialization) - for (size_t i = 0; i < num_zero_rows; ++i) { - w_lagrange[i] = circuit_constructor.get_variable(circuit_constructor.zero_idx); - } - - // Insert ECC op wire values into wire polynomial - if constexpr (IsGoblinFlavor) { - for (size_t i = 0; i < num_ecc_op_gates; ++i) { - auto& op_wire = circuit_constructor.ecc_op_wires[wire_idx]; - w_lagrange[i + op_gate_offset] = circuit_constructor.get_variable(op_wire[i]); - } - } - - // Insert public inputs (first two wire polynomials only) - if (wire_idx < 2) { - for (size_t i = 0; i < num_public_inputs; ++i) { - w_lagrange[i + pub_input_offset] = circuit_constructor.get_variable(public_inputs[i]); + // The sorted list polynomials have (tables_size + lookups_size) populated entries. We define the index below so + // that these entries are written into the last indices of the polynomials. The values on the first + // dyadic_circuit_size - (tables_size + lookups_size) indices are automatically initialized to zero via the + // polynomial constructor. + size_t s_index = dyadic_circuit_size - (circuit.get_tables_size() + circuit.get_lookups_size()) - additional_offset; + ASSERT(s_index > 0); // We need at least 1 row of zeroes for the permutation argument + + for (auto& table : circuit.lookup_tables) { + const fr table_index(table.table_index); + auto& lookup_gates = table.lookup_gates; + for (size_t i = 0; i < table.size; ++i) { + if (table.use_twin_keys) { + lookup_gates.push_back({ + { + table.column_1[i].from_montgomery_form().data[0], + table.column_2[i].from_montgomery_form().data[0], + }, + { + table.column_3[i], + 0, + }, + }); + } else { + lookup_gates.push_back({ + { + table.column_1[i].from_montgomery_form().data[0], + 0, + }, + { + table.column_2[i], + table.column_3[i], + }, + }); } } - // Insert conventional gate wire values into the wire polynomial - for (size_t i = 0; i < num_gates; ++i) { - auto& wire = circuit_constructor.wires[wire_idx]; - w_lagrange[i + gate_offset] = circuit_constructor.get_variable(wire[i]); +#ifdef NO_TBB + std::sort(lookup_gates.begin(), lookup_gates.end()); +#else + std::sort(std::execution::par_unseq, lookup_gates.begin(), lookup_gates.end()); +#endif + + for (const auto& entry : lookup_gates) { + const auto components = entry.to_sorted_list_components(table.use_twin_keys); + sorted_polynomials[0][s_index] = components[0]; + sorted_polynomials[1][s_index] = components[1]; + sorted_polynomials[2][s_index] = components[2]; + sorted_polynomials[3][s_index] = table_index; + ++s_index; } - - wire_polynomials.push_back(std::move(w_lagrange)); } - return wire_polynomials; + return sorted_polynomials; } + } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/proof_system/composer/composer_lib.test.cpp b/barretenberg/cpp/src/barretenberg/proof_system/composer/composer_lib.test.cpp index 9e45324e0d3..9f780a49327 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/composer/composer_lib.test.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/composer/composer_lib.test.cpp @@ -18,44 +18,4 @@ class ComposerLibTests : public ::testing::Test { auto crs = crs_factory.get_prover_crs(4); return Flavor::ProvingKey(/*circuit_size=*/8, /*num_public_inputs=*/0); }(); -}; - -TEST_F(ComposerLibTests, ConstructSelectors) -{ - circuit_constructor.q_m() = { 1, 2, 3, 4 }; - circuit_constructor.q_1() = { 5, 6, 7, 8 }; - circuit_constructor.q_2() = { 9, 10, 11, 12 }; - circuit_constructor.q_3() = { 13, 14, 15, 16 }; - circuit_constructor.q_c() = { 17, 18, 19, 20 }; - - construct_selector_polynomials(circuit_constructor, &proving_key); - size_t offset = 0; - if (Flavor::has_zero_row) { - offset += 1; - } - - EXPECT_EQ(proving_key.q_m[0 + offset], 1); - EXPECT_EQ(proving_key.q_m[1 + offset], 2); - EXPECT_EQ(proving_key.q_m[2 + offset], 3); - EXPECT_EQ(proving_key.q_m[3 + offset], 4); - - EXPECT_EQ(proving_key.q_l[0 + offset], 5); - EXPECT_EQ(proving_key.q_l[1 + offset], 6); - EXPECT_EQ(proving_key.q_l[2 + offset], 7); - EXPECT_EQ(proving_key.q_l[3 + offset], 8); - - EXPECT_EQ(proving_key.q_r[0 + offset], 9); - EXPECT_EQ(proving_key.q_r[1 + offset], 10); - EXPECT_EQ(proving_key.q_r[2 + offset], 11); - EXPECT_EQ(proving_key.q_r[3 + offset], 12); - - EXPECT_EQ(proving_key.q_o[0 + offset], 13); - EXPECT_EQ(proving_key.q_o[1 + offset], 14); - EXPECT_EQ(proving_key.q_o[2 + offset], 15); - EXPECT_EQ(proving_key.q_o[3 + offset], 16); - - EXPECT_EQ(proving_key.q_c[0 + offset], 17); - EXPECT_EQ(proving_key.q_c[1 + offset], 18); - EXPECT_EQ(proving_key.q_c[2 + offset], 19); - EXPECT_EQ(proving_key.q_c[3 + offset], 20); -} +}; \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/proof_system/composer/permutation_lib.hpp b/barretenberg/cpp/src/barretenberg/proof_system/composer/permutation_lib.hpp index d36be83dfa1..99740c61205 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/composer/permutation_lib.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/composer/permutation_lib.hpp @@ -52,116 +52,37 @@ struct permutation_subgroup_element { bool is_tag = false; }; -template struct PermutationMapping { +template struct PermutationMapping { using Mapping = std::array, NUM_WIRES>; Mapping sigmas; Mapping ids; -}; - -using CyclicPermutation = std::vector; - -namespace { - -/** - * @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. using Curve = curve::BN254; - using FF = Curve::ScalarField; - using Polynomial = bb::Polynomial; - * - * @tparam program_width Program width - * - * */ -template -std::vector compute_wire_copy_cycles(const typename Flavor::CircuitBuilder& circuit_constructor) -{ - // Reference circuit constructor members - 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(); - - // Each variable represents one cycle - const size_t number_of_cycles = circuit_constructor.variables.size(); - std::vector copy_cycles(number_of_cycles); - copy_cycles.reserve(num_gates * 3); - - // 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 constexpr (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 }); - } - } - - // Define offsets for placement of public inputs and gates in execution trace - const size_t num_zero_rows = Flavor::has_zero_row ? 1 : 0; - size_t pub_inputs_offset = num_zero_rows; - size_t gates_offset = num_public_inputs + num_zero_rows; - // If Goblin, adjust offsets to account for ecc op gates and update copy cycles to include these gates - if constexpr (IsGoblinFlavor) { - // Set ecc op gate offset and update offsets for PI and conventional gates - const size_t op_gates_offset = num_zero_rows; - const size_t num_ecc_op_gates = circuit_constructor.num_ecc_op_gates; - pub_inputs_offset += num_ecc_op_gates; - gates_offset += num_ecc_op_gates; - - const auto& op_wires = circuit_constructor.ecc_op_wires; - // Iterate over all variables of the ecc op gates, and add a corresponding node to the cycle for that variable - for (size_t i = 0; i < num_ecc_op_gates; ++i) { - for (size_t op_wire_idx = 0; op_wire_idx < Flavor::NUM_WIRES; ++op_wire_idx) { - const uint32_t var_index = circuit_constructor.real_variable_index[op_wires[op_wire_idx][i]]; - const auto wire_index = static_cast(op_wire_idx); - const auto gate_idx = static_cast(i + op_gates_offset); - copy_cycles[var_index].emplace_back(cycle_node{ wire_index, gate_idx }); + /** + * @brief Construct a permutation mapping default initialized so every element is in a cycle by itself + * + */ + PermutationMapping(size_t circuit_size) + { + for (uint8_t col_idx = 0; col_idx < NUM_WIRES; ++col_idx) { + sigmas[col_idx].reserve(circuit_size); + if constexpr (generalized) { + ids[col_idx].reserve(circuit_size); + } + // Initialize every element to point to itself + for (uint32_t row_idx = 0; row_idx < circuit_size; ++row_idx) { + permutation_subgroup_element self{ row_idx, col_idx }; + sigmas[col_idx].emplace_back(self); + if constexpr (generalized) { + ids[col_idx].emplace_back(self); + } } } } +}; - // 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 - // to 0. All selectors are zero at these rows, so they are fully unconstrained. The "real" gates that follow can use - // references to these variables. - // - // The copy cycle for the i-th public variable looks like - // (i) -> (n+i) -> (i') -> ... -> (i'') - // (Using the convention that W^L_i = W_i and W^R_i = W_{n+i}, W^O_i = W_{2n+i}) - // - // This loop initializes the i-th cycle with (i) -> (n+i), meaning that we always expect W^L_i = W^R_i, - // 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 + 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 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. - // The value in this position should be equal to the value of the element at index `var_index` - // 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[i]]; - const auto wire_index = static_cast(wire_idx); - const auto gate_idx = static_cast(i + gates_offset); - copy_cycles[var_index].emplace_back(cycle_node{ wire_index, gate_idx }); - ++wire_idx; - } - } - return copy_cycles; -} +using CyclicPermutation = std::vector; +namespace { /** * @brief Compute the traditional or generalized permutation mapping * @@ -170,41 +91,19 @@ std::vector compute_wire_copy_cycles(const typename Flavor::C * * @tparam program_width The number of wires * @tparam generalized (bool) Triggers use of gen perm tags and computation of id mappings when true - * @tparam CircuitBuilder The class that holds basic circuitl ogic * @param circuit_constructor Circuit-containing object - * @param key Pointer to the proving key + * @param proving_key Pointer to the proving key * @return PermutationMapping sigma mapping (and id mapping if generalized == true) */ template -PermutationMapping compute_permutation_mapping( - const typename Flavor::CircuitBuilder& circuit_constructor, typename Flavor::ProvingKey* proving_key) +PermutationMapping compute_permutation_mapping( + const typename Flavor::CircuitBuilder& circuit_constructor, + typename Flavor::ProvingKey* proving_key, + std::vector wire_copy_cycles) { - // Compute wire copy cycles (cycles of permutations) - auto wire_copy_cycles = compute_wire_copy_cycles(circuit_constructor); - - PermutationMapping mapping; // Initialize the table of permutations so that every element points to itself - // TODO(https://github.com/AztecProtocol/barretenberg/issues/391) zip - for (size_t i = 0; i < Flavor::NUM_WIRES; ++i) { - mapping.sigmas[i].reserve(proving_key->circuit_size); - if constexpr (generalized) { - mapping.ids[i].reserve(proving_key->circuit_size); - } - - for (size_t j = 0; j < proving_key->circuit_size; ++j) { - mapping.sigmas[i].emplace_back(permutation_subgroup_element{ .row_index = static_cast(j), - .column_index = static_cast(i), - .is_public_input = false, - .is_tag = false }); - if constexpr (generalized) { - mapping.ids[i].emplace_back(permutation_subgroup_element{ .row_index = static_cast(j), - .column_index = static_cast(i), - .is_public_input = false, - .is_tag = false }); - } - } - } + PermutationMapping mapping{ proving_key->circuit_size }; // Represents the index of a variable in circuit_constructor.variables (needed only for generalized) std::span real_variable_tags = circuit_constructor.real_variable_tags; @@ -251,6 +150,8 @@ PermutationMapping compute_permutation_mapping( // Add information about public inputs to the computation const auto num_public_inputs = static_cast(circuit_constructor.public_inputs.size()); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/862): this is brittle. depends on when PI are placed. + // how can we make this more robust? // The public inputs are placed at the top of the execution trace, potentially offset by a zero row. const size_t num_zero_rows = Flavor::has_zero_row ? 1 : 0; size_t pub_input_offset = num_zero_rows; @@ -446,26 +347,6 @@ void compute_monomial_and_coset_fft_polynomials_from_lagrange(std::string label, } } -/** - * @brief Compute sigma permutation polynomials for standard plonk and put them in the polynomial cache - * - * @tparam program_width Number of wires - * @tparam CircuitBuilder Class holding the circuit - * @param circuit_constructor An object holdingt he circuit - * @param key Pointer to a proving key - */ -template -void compute_standard_plonk_sigma_permutations(const typename Flavor::CircuitBuilder& circuit_constructor, - typename Flavor::ProvingKey* key) -{ - // Compute the permutation table specifying which element becomes which - auto mapping = compute_permutation_mapping(circuit_constructor, key); - // Compute Plonk-style sigma polynomials from the mapping - compute_plonk_permutation_lagrange_polynomials_from_mapping("sigma", mapping.sigmas, key); - // Compute their monomial and coset versions - compute_monomial_and_coset_fft_polynomials_from_lagrange("sigma", key); -} - /** * @brief Compute Lagrange Polynomials L_0 and L_{n-1} and put them in the polynomial cache * @@ -484,48 +365,36 @@ template inline void compute_first_and_last_lagrange_polynomia } /** - * @brief Compute generalized permutation sigmas and ids for ultra plonk + * @brief Compute Plonk or Honk style generalized permutation sigmas and ids and add to proving_key * - * @tparam program_width - * @tparam CircuitBuilder - * @param circuit_constructor - * @param key - * @return std::array, program_width> - */ -template -void compute_plonk_generalized_sigma_permutations(const typename Flavor::CircuitBuilder& circuit_constructor, - typename Flavor::ProvingKey* key) -{ - auto mapping = compute_permutation_mapping(circuit_constructor, key); - - // Compute Plonk-style sigma and ID polynomials from the corresponding mappings - compute_plonk_permutation_lagrange_polynomials_from_mapping("sigma", mapping.sigmas, key); - compute_plonk_permutation_lagrange_polynomials_from_mapping("id", mapping.ids, key); - // Compute the monomial and coset-ffts for sigmas and IDs - compute_monomial_and_coset_fft_polynomials_from_lagrange("sigma", key); - compute_monomial_and_coset_fft_polynomials_from_lagrange("id", key); -} - -/** - * @brief Compute generalized permutation sigmas and ids for ultra plonk - * - * @tparam program_width - * @tparam CircuitBuilder - * @param circuit_constructor + * @param circuit * @param proving_key - * @return std::array, program_width> + * @param copy_cycles pre-computed sets of wire addresses whose values should be copy constrained + * */ template -void compute_honk_generalized_sigma_permutations(const typename Flavor::CircuitBuilder& circuit_constructor, - typename Flavor::ProvingKey* proving_key) +void compute_permutation_argument_polynomials(const typename Flavor::CircuitBuilder& circuit, + typename Flavor::ProvingKey* key, + std::vector copy_cycles) { - auto mapping = compute_permutation_mapping(circuit_constructor, proving_key); + constexpr bool generalized = IsUltraPlonkFlavor || IsUltraFlavor; + auto mapping = compute_permutation_mapping(circuit, key, copy_cycles); - // Compute Honk-style sigma and ID polynomials from the corresponding mappings - compute_honk_style_permutation_lagrange_polynomials_from_mapping( - proving_key->get_sigma_polynomials(), mapping.sigmas, proving_key); - compute_honk_style_permutation_lagrange_polynomials_from_mapping( - proving_key->get_id_polynomials(), mapping.ids, proving_key); + if constexpr (IsPlonkFlavor) { // any Plonk flavor + // Compute Plonk-style sigma and ID polynomials in lagrange, monomial, and coset-fft forms + compute_plonk_permutation_lagrange_polynomials_from_mapping("sigma", mapping.sigmas, key); + compute_monomial_and_coset_fft_polynomials_from_lagrange("sigma", key); + if constexpr (generalized) { + compute_plonk_permutation_lagrange_polynomials_from_mapping("id", mapping.ids, key); + compute_monomial_and_coset_fft_polynomials_from_lagrange("id", key); + } + } else if constexpr (IsUltraFlavor) { // any UltraHonk flavor + // Compute Honk-style sigma and ID polynomials from the corresponding mappings + compute_honk_style_permutation_lagrange_polynomials_from_mapping( + key->get_sigma_polynomials(), mapping.sigmas, key); + compute_honk_style_permutation_lagrange_polynomials_from_mapping( + key->get_id_polynomials(), mapping.ids, key); + } } } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/proof_system/composer/permutation_lib.test.cpp b/barretenberg/cpp/src/barretenberg/proof_system/composer/permutation_lib.test.cpp index bde1e500a41..a563ca3d534 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/composer/permutation_lib.test.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/composer/permutation_lib.test.cpp @@ -64,22 +64,17 @@ class PermutationHelperTests : public ::testing::Test { } }; -TEST_F(PermutationHelperTests, ComputeWireCopyCycles) -{ - // TODO(#425) Flesh out these tests - compute_wire_copy_cycles(circuit_constructor); -} - TEST_F(PermutationHelperTests, ComputePermutationMapping) { // TODO(#425) Flesh out these tests - compute_permutation_mapping(circuit_constructor, proving_key.get()); + compute_permutation_mapping(circuit_constructor, proving_key.get(), {}); } TEST_F(PermutationHelperTests, ComputeHonkStyleSigmaLagrangePolynomialsFromMapping) { // TODO(#425) Flesh out these tests - auto mapping = compute_permutation_mapping(circuit_constructor, proving_key.get()); + auto mapping = + compute_permutation_mapping(circuit_constructor, proving_key.get(), {}); compute_honk_style_permutation_lagrange_polynomials_from_mapping( proving_key->get_sigma_polynomials(), mapping.sigmas, proving_key.get()); } diff --git a/barretenberg/cpp/src/barretenberg/proof_system/execution_trace/execution_trace.cpp b/barretenberg/cpp/src/barretenberg/proof_system/execution_trace/execution_trace.cpp new file mode 100644 index 00000000000..26532627539 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/proof_system/execution_trace/execution_trace.cpp @@ -0,0 +1,147 @@ +#include "execution_trace.hpp" +#include "barretenberg/flavor/goblin_ultra.hpp" +#include "barretenberg/flavor/plonk_flavors.hpp" +#include "barretenberg/flavor/ultra.hpp" +#include "barretenberg/plonk/proof_system/proving_key/proving_key.hpp" +namespace bb { + +template +void ExecutionTrace_::generate(const Builder& builder, + const std::shared_ptr& proving_key) +{ + // Construct wire polynomials, selector polynomials, and copy cycles from raw circuit data + auto trace_data = construct_trace_data(builder, proving_key->circuit_size); + + add_wires_and_selectors_to_proving_key(trace_data, builder, proving_key); + + // Compute the permutation argument polynomials (sigma/id) and add them to proving key + compute_permutation_argument_polynomials(builder, proving_key.get(), trace_data.copy_cycles); +} + +template +void ExecutionTrace_::add_wires_and_selectors_to_proving_key( + TraceData& trace_data, const Builder& builder, const std::shared_ptr& proving_key) +{ + if constexpr (IsHonkFlavor) { + for (auto [pkey_wire, trace_wire] : zip_view(proving_key->get_wires(), trace_data.wires)) { + pkey_wire = std::move(trace_wire); + } + for (auto [pkey_selector, trace_selector] : zip_view(proving_key->get_selectors(), trace_data.selectors)) { + pkey_selector = std::move(trace_selector); + } + } else if constexpr (IsPlonkFlavor) { + for (size_t idx = 0; idx < trace_data.wires.size(); ++idx) { + std::string wire_tag = "w_" + std::to_string(idx + 1) + "_lagrange"; + proving_key->polynomial_store.put(wire_tag, std::move(trace_data.wires[idx])); + } + for (size_t idx = 0; idx < trace_data.selectors.size(); ++idx) { + proving_key->polynomial_store.put(builder.selector_names[idx] + "_lagrange", + std::move(trace_data.selectors[idx])); + } + } +} + +template +typename ExecutionTrace_::TraceData ExecutionTrace_::construct_trace_data(const Builder& builder, + size_t dyadic_circuit_size) +{ + TraceData trace_data{ dyadic_circuit_size, builder }; + + // TODO(https://github.com/AztecProtocol/barretenberg/issues/862): Eventually trace_blocks will be constructed + // directly in the builder, i.e. the gate addition methods will directly populate the wire/selectors in the + // appropriate block. In the mean time we do some inefficient copying etc to construct it here post facto. + auto trace_blocks = create_execution_trace_blocks(builder); + + uint32_t offset = 0; // Track offset at which to place each block in the trace polynomials + // For each block in the trace, populate wire polys, copy cycles and selector polys + for (auto& block : trace_blocks) { + auto block_size = static_cast(block.wires[0].size()); + info("block size = ", block_size); + + // Update wire polynomials and copy cycles + // NB: The order of row/column loops is arbitrary but needs to be row/column to match old copy_cycle code + for (uint32_t block_row_idx = 0; block_row_idx < block_size; ++block_row_idx) { + for (uint32_t wire_idx = 0; wire_idx < NUM_WIRES; ++wire_idx) { + uint32_t var_idx = block.wires[wire_idx][block_row_idx]; // an index into the variables array + uint32_t real_var_idx = builder.real_variable_index[var_idx]; + uint32_t trace_row_idx = block_row_idx + offset; + // Insert the real witness values from this block into the wire polys at the correct offset + trace_data.wires[wire_idx][trace_row_idx] = builder.get_variable(var_idx); + // Add the address of the witness value to its corresponding copy cycle + // NB: Not adding cycles for wires 3 and 4 here is only needed in order to maintain consistency with old + // version. We can remove this special case and the result is simply that all the zeros in wires 3 and 4 + // over the PI range are copy constrained together, but this changes sigma/id which changes the vkey. + if (!(block.is_public_input && wire_idx > 1)) { + trace_data.copy_cycles[real_var_idx].emplace_back(cycle_node{ wire_idx, trace_row_idx }); + } + } + } + + // Insert the selector values for this block into the selector polynomials at the correct offset + // TODO(https://github.com/AztecProtocol/barretenberg/issues/398): implicit arithmetization/flavor consistency + for (auto [selector_poly, selector] : zip_view(trace_data.selectors, block.selectors.get())) { + for (size_t row_idx = 0; row_idx < block_size; ++row_idx) { + size_t trace_row_idx = row_idx + offset; + selector_poly[trace_row_idx] = selector[row_idx]; + } + } + + offset += block_size; + } + return trace_data; +} + +template +std::vector::TraceBlock> ExecutionTrace_::create_execution_trace_blocks( + const Builder& builder) +{ + std::vector trace_blocks; + + // Make a block for the zero row + if constexpr (Flavor::has_zero_row) { + TraceBlock zero_block; + for (auto& wire : zero_block.wires) { + wire.emplace_back(builder.zero_idx); + } + for (auto& selector : zero_block.selectors.get()) { + selector.emplace_back(0); + } + trace_blocks.emplace_back(zero_block); + } + + // Make a block for the ecc op wires + if constexpr (IsGoblinFlavor) { + trace_blocks.emplace_back(builder.ecc_op_block); + } + + // Make a block for the public inputs + TraceBlock public_block; + for (auto& idx : builder.public_inputs) { + for (size_t wire_idx = 0; wire_idx < NUM_WIRES; ++wire_idx) { + if (wire_idx < 2) { // first two wires get a copy of the public inputs + public_block.wires[wire_idx].emplace_back(idx); + } else { // the remaining wires get zeros + public_block.wires[wire_idx].emplace_back(builder.zero_idx); + } + } + for (auto& selector : public_block.selectors.get()) { + selector.emplace_back(0); + } + } + + public_block.is_public_input = true; + trace_blocks.emplace_back(public_block); + + // Make a block for the basic wires and selectors + TraceBlock conventional_block{ builder.wires, builder.selectors }; + trace_blocks.emplace_back(conventional_block); + + return trace_blocks; +} + +template class ExecutionTrace_; +template class ExecutionTrace_; +template class ExecutionTrace_; +template class ExecutionTrace_; + +} // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/proof_system/execution_trace/execution_trace.hpp b/barretenberg/cpp/src/barretenberg/proof_system/execution_trace/execution_trace.hpp new file mode 100644 index 00000000000..a79877761de --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/proof_system/execution_trace/execution_trace.hpp @@ -0,0 +1,90 @@ +#pragma once +#include "barretenberg/flavor/flavor.hpp" +#include "barretenberg/proof_system/composer/permutation_lib.hpp" +#include "barretenberg/srs/global_crs.hpp" + +namespace bb { + +/** + * @brief The wires and selectors used to define a block in the execution trace + * + * @tparam Arithmetization The set of selectors corresponding to the arithmetization + */ +template struct ExecutionTraceBlock { + // WORKTODO: Zac - make this less terrible + using Wires = std::array>, Arithmetization::NUM_WIRES>; + Wires wires; + Arithmetization selectors; + bool is_public_input = false; +}; + +template class ExecutionTrace_ { + using Builder = typename Flavor::CircuitBuilder; + using Polynomial = typename Flavor::Polynomial; + using FF = typename Flavor::FF; + using TraceBlock = ExecutionTraceBlock; + using Wires = std::array>, Builder::NUM_WIRES>; + using Selectors = typename Builder::Selectors; + using ProvingKey = typename Flavor::ProvingKey; + + public: + static constexpr size_t NUM_WIRES = Builder::NUM_WIRES; + + struct TraceData { + std::array wires; + std::array selectors; + // A vector of sets (vectors) of addresses into the wire polynomials whose values are copy constrained + std::vector copy_cycles; + + TraceData(size_t dyadic_circuit_size, const Builder& builder) + { + // Initializate the wire and selector polynomials + for (auto& wire : wires) { + wire = Polynomial(dyadic_circuit_size); + } + for (auto& selector : selectors) { + selector = Polynomial(dyadic_circuit_size); + } + copy_cycles.resize(builder.variables.size()); + } + }; + + /** + * @brief Given a circuit, populate a proving key with wire polys, selector polys, and sigma/id polys + * + * @param builder + */ + static void generate(const Builder& builder, const std::shared_ptr&); + + private: + /** + * @brief Add the wire and selector polynomials from the trace data to a honk or plonk proving key + * + * @param trace_data + * @param builder + * @param proving_key + */ + static void add_wires_and_selectors_to_proving_key(TraceData& trace_data, + const Builder& builder, + const std::shared_ptr& proving_key); + + /** + * @brief Construct wire polynomials, selector polynomials and copy cycles from raw circuit data + * + * @param builder + * @param dyadic_circuit_size + * @return TraceData + */ + static TraceData construct_trace_data(const Builder& builder, size_t dyadic_circuit_size); + + /** + * @brief Temporary helper method to construct execution trace blocks from existing builder structures + * @details Eventually the builder will construct blocks directly + * + * @param builder + * @return std::vector + */ + static std::vector create_execution_trace_blocks(const Builder& builder); +}; + +} // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/srs/scalar_multiplication.test.cpp b/barretenberg/cpp/src/barretenberg/srs/scalar_multiplication.test.cpp index c43bb64a60d..d66641157a5 100644 --- a/barretenberg/cpp/src/barretenberg/srs/scalar_multiplication.test.cpp +++ b/barretenberg/cpp/src/barretenberg/srs/scalar_multiplication.test.cpp @@ -576,14 +576,6 @@ TYPED_TEST(ScalarMultiplicationTests, EndomorphismSplit) Fr* k2_t = (Fr*)&scalar.data[2]; Fr::split_into_endomorphism_scalars(scalar, *k1_t, *k2_t); - // The compiler really doesn't like what we're doing here, - // and disabling the array-bounds error project-wide seems unsafe. - // The large macro blocks are here to warn that we should be careful when - // aliasing the arguments to split_into_endomorphism_scalars -#if !defined(__clang__) && defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" -#endif Fr k1{ (*k1_t).data[0], (*k1_t).data[1], 0, 0 }; Fr k2{ (*k2_t).data[0], (*k2_t).data[1], 0, 0 }; #if !defined(__clang__) && defined(__GNUC__) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/pedersen/pedersen.bench.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/pedersen/pedersen.bench.cpp index 6258d855e94..e37bc05a9ef 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/pedersen/pedersen.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/pedersen/pedersen.bench.cpp @@ -116,35 +116,6 @@ void native_pedersen_hash_pair_bench(State& state) noexcept } BENCHMARK(native_pedersen_hash_pair_bench)->Unit(benchmark::kMillisecond)->MinTime(3); -void construct_pedersen_witnesses_bench(State& state) noexcept -{ - bb::srs::init_crs_factory(BARRETENBERG_SRS_PATH); - - for (auto _ : state) { - state.PauseTiming(); - auto builder = Builder(static_cast(state.range(0))); - generate_test_pedersen_hash_circuit(builder, static_cast(state.range(0))); - std::cout << "builder gates = " << builder.get_num_gates() << std::endl; - - auto composer = Composer(); - composer.compute_proving_key(builder); - state.ResumeTiming(); - - composer.compute_witness(builder); - } -} -BENCHMARK(construct_pedersen_witnesses_bench) - ->Arg(num_hashes[0]) - ->Arg(num_hashes[1]) - ->Arg(num_hashes[2]) - ->Arg(num_hashes[3]) - ->Arg(num_hashes[4]) - ->Arg(num_hashes[5]) - ->Arg(num_hashes[6]) - ->Arg(num_hashes[7]) - ->Arg(num_hashes[8]) - ->Arg(num_hashes[9]); - void construct_pedersen_proving_keys_bench(State& state) noexcept { for (auto _ : state) { diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.cpp b/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.cpp index 5479420510d..1f2d09bb086 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.cpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.cpp @@ -12,138 +12,24 @@ namespace bb { * @tparam Flavor * @param circuit */ -template void ProverInstance_::compute_circuit_size_parameters(Circuit& circuit) +template size_t ProverInstance_::compute_dyadic_size(Circuit& circuit) { - // Compute total length of the tables and the number of lookup gates; their sum is the minimum circuit size - for (const auto& table : circuit.lookup_tables) { - tables_size += table.size; - lookups_size += table.lookup_gates.size(); - } - - // Get num conventional gates, num public inputs and num Goblin style ECC op gates - const size_t num_gates = circuit.num_gates; - num_public_inputs = circuit.public_inputs.size(); - num_ecc_op_gates = 0; - if constexpr (IsGoblinFlavor) { - num_ecc_op_gates = circuit.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; - - // number of populated rows in the execution trace - size_t num_rows_populated_in_execution_trace = num_zero_rows + num_ecc_op_gates + num_public_inputs + num_gates; - - // 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.get_circuit_subgroup_size(total_num_gates); -} - -/** - * @brief Compute witness polynomials - * - */ -template void ProverInstance_::compute_witness(Circuit& circuit) -{ - if (computed_witness) { - return; - } - - // Construct the conventional wire polynomials - auto wire_polynomials = construct_wire_polynomials_base(circuit, dyadic_circuit_size); + // minimum circuit size due to lookup argument + const size_t min_size_due_to_lookups = circuit.get_tables_size() + circuit.get_lookups_size(); - proving_key->w_l = wire_polynomials[0].share(); - proving_key->w_r = wire_polynomials[1].share(); - proving_key->w_o = wire_polynomials[2].share(); - proving_key->w_4 = wire_polynomials[3].share(); - - // If Goblin, construct the ECC op queue wire and databus polynomials + // minumum size of execution trace due to everything else + size_t min_size_of_execution_trace = circuit.public_inputs.size() + circuit.num_gates; if constexpr (IsGoblinFlavor) { - construct_ecc_op_wire_polynomials(wire_polynomials); - construct_databus_polynomials(circuit); - } - - // Initialise the sorted concatenated list polynomials for the lookup argument - for (auto& s_i : sorted_polynomials) { - s_i = Polynomial(dyadic_circuit_size); - } - - // The sorted list polynomials have (tables_size + lookups_size) populated entries. We define the index below so - // that these entries are written into the last indices of the polynomials. The values on the first - // dyadic_circuit_size - (tables_size + lookups_size) indices are automatically initialized to zero via the - // polynomial constructor. - size_t s_index = dyadic_circuit_size - tables_size - lookups_size; - ASSERT(s_index > 0); // We need at least 1 row of zeroes for the permutation argument - - for (auto& table : circuit.lookup_tables) { - const fr table_index(table.table_index); - auto& lookup_gates = table.lookup_gates; - for (size_t i = 0; i < table.size; ++i) { - if (table.use_twin_keys) { - lookup_gates.push_back({ - { - table.column_1[i].from_montgomery_form().data[0], - table.column_2[i].from_montgomery_form().data[0], - }, - { - table.column_3[i], - 0, - }, - }); - } else { - lookup_gates.push_back({ - { - table.column_1[i].from_montgomery_form().data[0], - 0, - }, - { - table.column_2[i], - table.column_3[i], - }, - }); - } - } - -#ifdef NO_TBB - std::sort(lookup_gates.begin(), lookup_gates.end()); -#else - std::sort(std::execution::par_unseq, lookup_gates.begin(), lookup_gates.end()); -#endif - - for (const auto& entry : lookup_gates) { - const auto components = entry.to_sorted_list_components(table.use_twin_keys); - sorted_polynomials[0][s_index] = components[0]; - sorted_polynomials[1][s_index] = components[1]; - sorted_polynomials[2][s_index] = components[2]; - sorted_polynomials[3][s_index] = table_index; - ++s_index; - } + min_size_of_execution_trace += circuit.num_ecc_op_gates; } - // 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`. We need to update the records with an offset Because we - // shift the gates to account for everything that comes before them in the execution trace, e.g. public inputs, - // a zero row, etc. - size_t offset = num_ecc_op_gates + num_public_inputs + num_zero_rows; - auto add_public_inputs_offset = [offset](uint32_t gate_index) { return gate_index + offset; }; - proving_key->memory_read_records = std::vector(); - proving_key->memory_write_records = std::vector(); - - std::transform(circuit.memory_read_records.begin(), - circuit.memory_read_records.end(), - std::back_inserter(proving_key->memory_read_records), - add_public_inputs_offset); - std::transform(circuit.memory_write_records.begin(), - circuit.memory_write_records.end(), - std::back_inserter(proving_key->memory_write_records), - add_public_inputs_offset); + // The number of gates is the maxmimum required by the lookup argument or everything else, plus an optional zero row + // to allow for shifts. + size_t num_zero_rows = Flavor::has_zero_row ? 1 : 0; + size_t total_num_gates = num_zero_rows + std::max(min_size_due_to_lookups, min_size_of_execution_trace); - computed_witness = true; + // Next power of 2 (dyadic circuit size) + return circuit.get_circuit_subgroup_size(total_num_gates); } /** @@ -154,28 +40,34 @@ template void ProverInstance_::compute_witness(Circuit& c * @tparam Flavor * @param wire_polynomials */ -template void ProverInstance_::construct_ecc_op_wire_polynomials(auto& wire_polynomials) +template +void ProverInstance_::construct_ecc_op_wire_polynomials(Circuit& circuit) + requires IsGoblinFlavor { - std::array op_wire_polynomials; + std::array op_wire_polynomials; for (auto& poly : op_wire_polynomials) { - poly = static_cast(dyadic_circuit_size); + poly = Polynomial{ dyadic_circuit_size }; } + Polynomial ecc_op_selector{ dyadic_circuit_size }; // The ECC op wires are constructed to contain the op data on the appropriate range and to vanish everywhere else. // The op data is assumed to have already been stored at the correct location in the convetional wires so the data // can simply be copied over directly. const size_t op_wire_offset = Flavor::has_zero_row ? 1 : 0; - for (size_t poly_idx = 0; poly_idx < Flavor::NUM_WIRES; ++poly_idx) { - for (size_t i = 0; i < num_ecc_op_gates; ++i) { + for (auto [ecc_op_wire, wire] : zip_view(op_wire_polynomials, proving_key->get_wires())) { + for (size_t i = 0; i < circuit.num_ecc_op_gates; ++i) { size_t idx = i + op_wire_offset; - op_wire_polynomials[poly_idx][idx] = wire_polynomials[poly_idx][idx]; + ecc_op_wire[idx] = wire[idx]; + ecc_op_selector[idx] = 1; } } + proving_key->num_ecc_op_gates = circuit.num_ecc_op_gates; proving_key->ecc_op_wire_1 = op_wire_polynomials[0].share(); proving_key->ecc_op_wire_2 = op_wire_polynomials[1].share(); proving_key->ecc_op_wire_3 = op_wire_polynomials[2].share(); proving_key->ecc_op_wire_4 = op_wire_polynomials[3].share(); + proving_key->lagrange_ecc_op = ecc_op_selector.share(); } /** @@ -189,8 +81,9 @@ template void ProverInstance_::construct_databus_polynomials(Circuit& circuit) requires IsGoblinFlavor { - polynomial public_calldata(dyadic_circuit_size); - polynomial calldata_read_counts(dyadic_circuit_size); + Polynomial public_calldata{ dyadic_circuit_size }; + Polynomial calldata_read_counts{ dyadic_circuit_size }; + Polynomial databus_id{ dyadic_circuit_size }; // Note: We do not utilize a zero row for databus columns for (size_t idx = 0; idx < circuit.public_calldata.size(); ++idx) { @@ -199,85 +92,24 @@ void ProverInstance_::construct_databus_polynomials(Circuit& circuit) calldata_read_counts[idx] = circuit.calldata_read_counts[idx]; } + // Compute a simple identity polynomial for use in the databus lookup argument + for (size_t i = 0; i < databus_id.size(); ++i) { + databus_id[i] = i; + } + proving_key->calldata = public_calldata.share(); proving_key->calldata_read_counts = calldata_read_counts.share(); + proving_key->databus_id = databus_id.share(); } template -std::shared_ptr ProverInstance_::compute_proving_key(Circuit& circuit) +void ProverInstance_::construct_table_polynomials(Circuit& circuit, size_t dyadic_circuit_size) { - if (proving_key) { - return proving_key; - } - - // Compute lagrange selectors - - proving_key = std::make_shared(dyadic_circuit_size, num_public_inputs); - - construct_selector_polynomials(circuit, proving_key.get()); - - compute_honk_generalized_sigma_permutations(circuit, proving_key.get()); - - compute_first_and_last_lagrange_polynomials(proving_key.get()); - - 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 = 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 - // put the table column's values at the end. (The first gates are for non-lookup constraints). - // [0, ..., 0, ...table, 0, 0, 0, x] - // ^^^^^^^^^ ^^^^^^^^ ^^^^^^^ ^nonzero to ensure uniqueness and to avoid infinity commitments - // | table randomness - // ignored, as used for regular constraints and padding to the next power of 2. - - for (size_t i = 0; i < offset; ++i) { - poly_q_table_column_1[i] = 0; - poly_q_table_column_2[i] = 0; - poly_q_table_column_3[i] = 0; - poly_q_table_column_4[i] = 0; - } - - for (const auto& table : circuit.lookup_tables) { - const fr table_index(table.table_index); - - for (size_t i = 0; i < table.size; ++i) { - poly_q_table_column_1[offset] = table.column_1[i]; - poly_q_table_column_2[offset] = table.column_2[i]; - poly_q_table_column_3[offset] = table.column_3[i]; - poly_q_table_column_4[offset] = table_index; - ++offset; - } - } - - // Polynomial memory is zeroed out when constructed with size hint, so we don't have to initialize trailing - // space - - proving_key->table_1 = poly_q_table_column_1.share(); - proving_key->table_2 = poly_q_table_column_2.share(); - proving_key->table_3 = poly_q_table_column_3.share(); - proving_key->table_4 = poly_q_table_column_4.share(); - - proving_key->recursive_proof_public_input_indices = - std::vector(recursive_proof_public_input_indices.begin(), recursive_proof_public_input_indices.end()); - - proving_key->contains_recursive_proof = contains_recursive_proof; - - if constexpr (IsGoblinFlavor) { - proving_key->num_ecc_op_gates = num_ecc_op_gates; - // Construct simple ID polynomial for databus indexing - typename Flavor::Polynomial databus_id(proving_key->circuit_size); - for (size_t i = 0; i < databus_id.size(); ++i) { - databus_id[i] = i; - } - proving_key->databus_id = databus_id.share(); - } - - return proving_key; + auto table_polynomials = construct_lookup_table_polynomials(circuit, dyadic_circuit_size); + proving_key->table_1 = table_polynomials[0].share(); + proving_key->table_2 = table_polynomials[1].share(); + proving_key->table_3 = table_polynomials[2].share(); + proving_key->table_4 = table_polynomials[3].share(); } template void ProverInstance_::initialize_prover_polynomials() diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.hpp index 3ab3a2da938..d2b8da8f28e 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.hpp @@ -3,6 +3,8 @@ #include "barretenberg/flavor/goblin_ultra.hpp" #include "barretenberg/flavor/ultra.hpp" #include "barretenberg/proof_system/composer/composer_lib.hpp" +#include "barretenberg/proof_system/composer/permutation_lib.hpp" +#include "barretenberg/proof_system/execution_trace/execution_trace.hpp" #include "barretenberg/relations/relation_parameters.hpp" namespace bb { @@ -28,6 +30,8 @@ template class ProverInstance_ { using CommitmentLabels = typename Flavor::CommitmentLabels; using RelationSeparator = typename Flavor::RelationSeparator; + using Trace = ExecutionTrace_; + public: std::shared_ptr proving_key; std::shared_ptr verification_key; @@ -60,9 +64,31 @@ template class ProverInstance_ { ProverInstance_(Circuit& circuit) { - compute_circuit_size_parameters(circuit); - compute_proving_key(circuit); - compute_witness(circuit); + dyadic_circuit_size = compute_dyadic_size(circuit); + + proving_key = std::make_shared(dyadic_circuit_size, circuit.public_inputs.size()); + + // Construct and add to proving key the wire, selector and copy constraint polynomials + Trace::generate(circuit, proving_key); + + // If Goblin, construct the ECC op queue wire and databus polynomials + // TODO(https://github.com/AztecProtocol/barretenberg/issues/862): Maybe do this in trace generation? + if constexpr (IsGoblinFlavor) { + construct_ecc_op_wire_polynomials(circuit); + construct_databus_polynomials(circuit); + } + + compute_first_and_last_lagrange_polynomials(proving_key.get()); + + construct_table_polynomials(circuit, dyadic_circuit_size); + + proving_key->recursive_proof_public_input_indices = std::vector( + recursive_proof_public_input_indices.begin(), recursive_proof_public_input_indices.end()); + proving_key->contains_recursive_proof = contains_recursive_proof; + + sorted_polynomials = construct_sorted_list_polynomials(circuit, dyadic_circuit_size); + + populate_memory_read_write_records(circuit, proving_key); } ProverInstance_() = default; @@ -77,31 +103,29 @@ template class ProverInstance_ { void compute_logderivative_inverse(FF, FF) requires IsGoblinFlavor; + void compute_databus_id() + requires IsGoblinFlavor; + void compute_grand_product_polynomials(FF, FF); private: static constexpr size_t num_zero_rows = Flavor::has_zero_row ? 1 : 0; static constexpr size_t NUM_WIRES = Circuit::NUM_WIRES; 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; - size_t num_ecc_op_gates = 0; - - std::shared_ptr compute_proving_key(Circuit&); - void compute_circuit_size_parameters(Circuit&); + size_t compute_dyadic_size(Circuit&); - void compute_witness(Circuit&); - - void construct_ecc_op_wire_polynomials(auto&); + void construct_ecc_op_wire_polynomials(Circuit&) + requires IsGoblinFlavor; void construct_databus_polynomials(Circuit&) requires IsGoblinFlavor; + void construct_table_polynomials(Circuit&, size_t); + + void add_memory_records_to_proving_key(Circuit&); + void add_table_column_selector_poly_to_proving_key(bb::polynomial& small, const std::string& tag); void add_plookup_memory_records_to_wire_4(FF); diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp index fe8098fd84f..d8cb79f6bcc 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp @@ -240,19 +240,10 @@ template class SumcheckVerifier { auto transcript_evaluations = transcript->template receive_from_prover>("Sumcheck:evaluations"); - // GCC has a bug where it says this is above array bounds - // but this is likely due to this bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104165 - // We disable this - if GCC was right, we would have caught this at runtime -#if !defined(__clang__) && defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" -#endif for (auto [eval, transcript_eval] : zip_view(purported_evaluations.get_all(), transcript_evaluations)) { eval = transcript_eval; } -#if !defined(__clang__) && defined(__GNUC__) -#pragma GCC diagnostic pop -#endif + FF full_honk_relation_purported_value = round.compute_full_honk_relation_purported_value( purported_evaluations, relation_parameters, pow_univariate, alpha);