From 9325f6ff987022da1a4dabb771781cdc999af18e Mon Sep 17 00:00:00 2001 From: ledwards2225 <98505400+ledwards2225@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:39:30 -0700 Subject: [PATCH] feat: mock data for IVC (#9893) Incremental work towards a write_vk flow for kernel circuits. kernel circuits are generated from acir via the method `acir_format::create_kernel_circuit()` which takes as input the raw acir data plus a ClientIvc instance containing the proofs to be recursively verified in that circuit (Oink/PG + merge). In the context of VK generation, those proofs are not yet known, so the IVC state has to be mocked. This PR adds such functionality but only for the oink case (i.e. the state of the IVC after accumulating the first app which only calls the pink prover). Equivalent logic for mocking the state after subsequent accumulations will be handled in a follow on. --- .../dsl/acir_format/acir_format.cpp | 13 +- .../acir_format/ivc_recursion_constraint.cpp | 165 +++++++++++++++++ .../acir_format/ivc_recursion_constraint.hpp | 27 +++ .../ivc_recursion_constraint.test.cpp | 170 ++++++++++-------- .../dsl/acir_format/proof_surgeon.hpp | 1 + .../execution_trace_usage_tracker.hpp | 2 +- .../arithmetization/mega_arithmetization.hpp | 12 +- .../arithmetization/ultra_arithmetization.hpp | 10 +- .../ultra_honk/decider_proving_key.hpp | 6 +- 9 files changed, 316 insertions(+), 90 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.cpp create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.hpp diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index bd36912f2f1..d0eb9d767d6 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -1,6 +1,7 @@ #include "acir_format.hpp" #include "barretenberg/common/log.hpp" #include "barretenberg/common/throw_or_abort.hpp" +#include "barretenberg/dsl/acir_format/ivc_recursion_constraint.hpp" #include "barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp" #include "barretenberg/stdlib/primitives/field/field_conversion.hpp" #include "barretenberg/stdlib_circuit_builders/mega_circuit_builder.hpp" @@ -490,6 +491,17 @@ MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system, ASSERT(false); } + // If no witness is provided, populate the VK and public inputs in the recursion constraint with dummy values so + // that the present kernel circuit is constructed correctly. (Used for constructing VKs without witnesses). + if (witness.empty()) { + // Create stdlib representations of each {proof, vkey} pair to be recursively verified + for (auto [constraint, queue_entry] : + zip_view(constraint_system.ivc_recursion_constraints, ivc.verification_queue)) { + + populate_dummy_vk_in_constraint(circuit, queue_entry.honk_verification_key, constraint.key); + } + } + // Construct a stdlib verification key for each constraint based on the verification key witness indices therein std::vector> stdlib_verification_keys; stdlib_verification_keys.reserve(constraint_system.ivc_recursion_constraints.size()); @@ -497,7 +509,6 @@ MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system, stdlib_verification_keys.push_back(std::make_shared( StdlibVerificationKey::from_witness_indices(circuit, constraint.key))); } - // Create stdlib representations of each {proof, vkey} pair to be recursively verified ivc.instantiate_stdlib_verification_queue(circuit, stdlib_verification_keys); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.cpp new file mode 100644 index 00000000000..4100773a486 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.cpp @@ -0,0 +1,165 @@ +#include "ivc_recursion_constraint.hpp" +#include "barretenberg/flavor/flavor.hpp" +#include "barretenberg/plonk_honk_shared/types/aggregation_object_type.hpp" +#include "barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.hpp" +#include "barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp" +#include "barretenberg/stdlib/primitives/bigfield/constants.hpp" +#include "barretenberg/stdlib/primitives/curves/bn254.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp" +#include "proof_surgeon.hpp" +#include "recursion_constraint.hpp" + +namespace acir_format { + +using namespace bb; +using field_ct = stdlib::field_t; + +ClientIVC create_mock_ivc_from_constraints(const std::vector& constraints) +{ + ClientIVC ivc; + ivc.trace_settings.structure = TraceStructure::SMALL_TEST; + + for (const auto& constraint : constraints) { + if (static_cast(PROOF_TYPE::OINK) == constraint.proof_type) { + mock_ivc_oink_accumulation(ivc, constraint.public_inputs.size()); + } else if (static_cast(PROOF_TYPE::PG) == constraint.proof_type) { + // perform equivalent mocking for PG accumulation + } + } + + return ivc; +} + +/** + * @brief Populate an IVC instance with data that mimics the state after accumulating the first app (which runs the oink + * prover) + *@details Mock state consists a mock verification queue entry of type OINK (proof, VK) and a mocked merge proof + * + * @param ivc + * @param num_public_inputs_app num pub inputs in accumulated app, excluding fixed components, e.g. pairing points + */ +void mock_ivc_oink_accumulation(ClientIVC& ivc, size_t num_public_inputs_app) +{ + ClientIVC::VerifierInputs oink_entry = + acir_format::create_dummy_vkey_and_proof_oink(ivc.trace_settings, num_public_inputs_app); + ivc.verification_queue.emplace_back(oink_entry); + ivc.merge_verification_queue.emplace_back(acir_format::create_dummy_merge_proof()); + ivc.initialized = true; +} + +/** + * @brief Create a mock oink proof and VK that have the correct structure but are not necessarily valid + * + */ +ClientIVC::VerifierInputs create_dummy_vkey_and_proof_oink(const TraceSettings& trace_settings, + const size_t num_public_inputs = 0) +{ + using Flavor = MegaFlavor; + using VerificationKey = ClientIVC::VerificationKey; + using FF = bb::fr; + + MegaArith::TraceBlocks blocks; + blocks.set_fixed_block_sizes(trace_settings); + blocks.compute_offsets(/*is_structured=*/true); + size_t structured_dyadic_size = blocks.get_structured_dyadic_size(); + size_t pub_inputs_offset = blocks.pub_inputs.trace_offset; + + ClientIVC::VerifierInputs verifier_inputs; + verifier_inputs.type = ClientIVC::QUEUE_TYPE::OINK; + + FF mock_val(5); + + auto mock_commitment = curve::BN254::AffineElement::one() * mock_val; + std::vector mock_commitment_frs = field_conversion::convert_to_bn254_frs(mock_commitment); + + // Set proof preamble (metadata plus public inputs) + size_t total_num_public_inputs = num_public_inputs + bb::PAIRING_POINT_ACCUMULATOR_SIZE; + verifier_inputs.proof.emplace_back(structured_dyadic_size); + verifier_inputs.proof.emplace_back(total_num_public_inputs); + verifier_inputs.proof.emplace_back(pub_inputs_offset); + for (size_t i = 0; i < total_num_public_inputs; ++i) { + verifier_inputs.proof.emplace_back(0); + } + + // Witness polynomial commitments + for (size_t i = 0; i < Flavor::NUM_WITNESS_ENTITIES; ++i) { + for (const FF& val : mock_commitment_frs) { + verifier_inputs.proof.emplace_back(val); + } + } + + // Set relevant VK metadata and commitments + verifier_inputs.honk_verification_key = std::make_shared(); + verifier_inputs.honk_verification_key->circuit_size = structured_dyadic_size; + verifier_inputs.honk_verification_key->num_public_inputs = total_num_public_inputs; + verifier_inputs.honk_verification_key->pub_inputs_offset = blocks.pub_inputs.trace_offset; // must be set correctly + verifier_inputs.honk_verification_key->contains_pairing_point_accumulator = true; + for (auto& commitment : verifier_inputs.honk_verification_key->get_all()) { + commitment = mock_commitment; + } + + return verifier_inputs; +} + +/** + * @brief Create a mock merge proof which has the correct structure but is not necessarily valid + * + * @return ClientIVC::MergeProof + */ +ClientIVC::MergeProof create_dummy_merge_proof() +{ + using FF = bb::fr; + + std::vector proof; + + FF mock_val(5); + auto mock_commitment = curve::BN254::AffineElement::one() * mock_val; + std::vector mock_commitment_frs = field_conversion::convert_to_bn254_frs(mock_commitment); + + // There are 12 entities in the merge protocol (4 columns x 3 components; aggregate transcript, previous aggregate + // transcript, current transcript contribution) + const size_t NUM_TRANSCRIPT_ENTITIES = 12; + + // Transcript poly commitments + for (size_t i = 0; i < NUM_TRANSCRIPT_ENTITIES; ++i) { + for (const FF& val : mock_commitment_frs) { + proof.emplace_back(val); + } + } + // Transcript poly evaluations + for (size_t i = 0; i < NUM_TRANSCRIPT_ENTITIES; ++i) { + proof.emplace_back(mock_val); + } + // Batched KZG quotient commitment + for (const FF& val : mock_commitment_frs) { + proof.emplace_back(val); + } + + return proof; +} + +/** + * @brief Populate VK witness fields from a recursion constraint from a provided VerificationKey + * + * @param builder + * @param mock_verification_key + * @param key_witness_indices + */ +void populate_dummy_vk_in_constraint(MegaCircuitBuilder& builder, + const std::shared_ptr& mock_verification_key, + std::vector& key_witness_indices) +{ + using Flavor = MegaFlavor; + using FF = Flavor::FF; + + // Convert the VerificationKey to fields + std::vector mock_vk_fields = mock_verification_key->to_field_elements(); + ASSERT(mock_vk_fields.size() == key_witness_indices.size()); + + // Add the fields to the witness and set the key witness indices accordingly + for (auto [witness_idx, value] : zip_view(key_witness_indices, mock_vk_fields)) { + witness_idx = builder.add_variable(value); + } +} + +} // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.hpp new file mode 100644 index 00000000000..5ab74ca80e6 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.hpp @@ -0,0 +1,27 @@ +#pragma once +#include "barretenberg/client_ivc/client_ivc.hpp" +#include "barretenberg/dsl/acir_format/recursion_constraint.hpp" +#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp" +#include + +namespace acir_format { + +using namespace bb; + +// TODO(https://github.com/AztecProtocol/barretenberg/issues/1148): logic in this file is incomplete. See issue for +// details. + +ClientIVC create_mock_ivc_from_constraints(const std::vector& constraints); + +void mock_ivc_oink_accumulation(ClientIVC& ivc, size_t num_public_inputs_app = 0); + +ClientIVC::VerifierInputs create_dummy_vkey_and_proof_oink(const TraceSettings& trace_settings, + const size_t num_public_inputs); + +ClientIVC::MergeProof create_dummy_merge_proof(); + +void populate_dummy_vk_in_constraint(MegaCircuitBuilder& builder, + const std::shared_ptr& mock_verification_key, + std::vector& key_witness_indices); + +} // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.test.cpp index 285106061be..50da7e166b5 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.test.cpp @@ -1,3 +1,4 @@ +#include "barretenberg/dsl/acir_format/ivc_recursion_constraint.hpp" #include "acir_format.hpp" #include "acir_format_mocks.hpp" #include "barretenberg/client_ivc/client_ivc.hpp" @@ -75,39 +76,11 @@ class IvcRecursionConstraintTest : public ::testing::Test { }; } - /** - * @brief Create an arithmetic constraint fixing the first public input witness to it's present value - * @details Meant to mimic the "business logic" of the aztec kernel. Used to facilitate failure testing since this - * will lead to failure of the kernel circuit to verify if a different proof witness is used in the business logic - * VS the recursive verification logic. - * - * @param public_inputs Witness indices of public inputs of some proof to be constrained - * @param witness - * @return ArithmeticConstraint - */ - static ArithmeticConstraint create_public_input_value_constraint(const std::vector& public_inputs, - const SlabVector& witness) - { - const uint32_t pub_input_idx = public_inputs[0]; - const FF pub_input_val = witness[pub_input_idx]; - return { - .a = pub_input_idx, - .b = 0, - .c = 0, - .q_m = 0, - .q_l = -1, - .q_r = 0, - .q_o = 0, - .q_c = pub_input_val, - }; - } - /** * @brief Generate an acir program {constraints, witness} for a mock kernel * @details The IVC contains and internal verification queue that contains proofs to be recursively verified. * Construct an AcirProgram with a RecursionConstraint for each entry in the ivc verification queue. (In practice - * these constraints would come directly from calls to verify_proof in noir). Also add mock "business logic" which - * simply enforces some constraint on the public inputs of the proof. + * these constraints would come directly from calls to verify_proof in noir). * @note This method needs the number of public inputs in each proof-to-be-verified so they can be extracted and * provided separately as is required in the acir constraint system. * @@ -130,14 +103,9 @@ class IvcRecursionConstraintTest : public ::testing::Test { verification_queue[idx], program.witness, inner_circuit_num_pub_inputs[idx])); } - // Add some mock kernel "business logic" which simply fixes one of the public inputs to a particular value - ArithmeticConstraint pub_input_constraint = - create_public_input_value_constraint(ivc_recursion_constraints[0].public_inputs, program.witness); - // Construct a constraint system containing the business logic and ivc recursion constraints program.constraints.varnum = static_cast(program.witness.size()); program.constraints.num_acir_opcodes = static_cast(ivc_recursion_constraints.size()); - program.constraints.poly_triple_constraints = { pub_input_constraint }; program.constraints.ivc_recursion_constraints = ivc_recursion_constraints; program.constraints.original_opcode_indices = create_empty_original_opcode_indices(); mock_opcode_indices(program.constraints); @@ -214,64 +182,110 @@ TEST_F(IvcRecursionConstraintTest, AccumulateFour) EXPECT_TRUE(ivc.prove_and_verify()); } -/** - * @brief Demonstrate failure of the IVC if the proof witness differs between those encoded in the constraint system - * (i.e. those used in the noir program) and those used in constructing the recursive verifiers internally - * @brief The idea is to construct two valid but unique verification queue entries of the form {proof, vkey}. One is - * used to construct the acir constraint system and the other is used to construct the recursive verification logic - * internally in the IVC. Since the proof/vkey witnesses in the constraint system are asserted equal to those used to - * construct the recursive verifiers, the use of different verification queue witnesses should result in failure as long - * as they were used in some nontrivial way in the main logic of the kernel. (Specifically, failure results from a - * failure of the "business logic" of the kernel which constrains one of the public inputs to a particular value). - */ -TEST_F(IvcRecursionConstraintTest, AccumulateTwoFailure) +// Test generation of "init" kernel VK via dummy IVC data +TEST_F(IvcRecursionConstraintTest, GenerateVK) { - // Accumulate a single app in order to construct a valid verification queue entry {proof, vkey} to be used later on. - // Demonstrate that it is indeed valid by completing the IVC with a kernel (which recursively verifies the entry) - // then proving and verifying the full IVC. - VerifierInputs alternative_verification_queue_entry; + const TraceSettings trace_settings{ TraceStructure::SMALL_TEST }; + + // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel) + std::shared_ptr expected_kernel_vk; + size_t num_app_public_inputs = 0; { ClientIVC ivc; - ivc.trace_settings.structure = TraceStructure::SMALL_TEST; + ivc.trace_settings = trace_settings; - // construct and accumulate a mock app circuit with a single unique public input + // Construct and accumulate mock app_circuit Builder app_circuit = construct_mock_app_circuit(ivc); ivc.accumulate(app_circuit); + num_app_public_inputs = app_circuit.public_inputs.size(); - // Save the single entry in the verification queue at this point - alternative_verification_queue_entry = ivc.verification_queue[0]; + // Construct and accumulate kernel consisting only of the kernel completion logic + AcirProgram program = construct_mock_kernel_program(ivc.verification_queue, { num_app_public_inputs }); + Builder kernel = acir_format::create_kernel_circuit(program.constraints, ivc, program.witness); + ivc.accumulate(kernel); + expected_kernel_vk = ivc.verification_queue.back().honk_verification_key; + } + + // Now, construct the kernel VK by mocking the post app accumulation state of the IVC + std::shared_ptr kernel_vk; + { + ClientIVC ivc; + ivc.trace_settings = trace_settings; + + acir_format::mock_ivc_oink_accumulation(ivc, num_app_public_inputs - bb::PAIRING_POINT_ACCUMULATOR_SIZE); - // Construct and accumulate kernel_0 - size_t num_pub_inputs_app = app_circuit.public_inputs.size(); - AcirProgram program_0 = construct_mock_kernel_program(ivc.verification_queue, { num_pub_inputs_app }); - Builder kernel_0 = acir_format::create_kernel_circuit(program_0.constraints, ivc, program_0.witness); - ivc.accumulate(kernel_0); + // Construct kernel consisting only of the kernel completion logic + AcirProgram program = construct_mock_kernel_program(ivc.verification_queue, { num_app_public_inputs }); + Builder kernel = acir_format::create_kernel_circuit(program.constraints, ivc); + // WORKTODO: this would normally happen in accumulate() + kernel.add_pairing_point_accumulator(stdlib::recursion::init_default_agg_obj_indices(kernel)); - EXPECT_TRUE(ivc.prove_and_verify()); + auto proving_key = std::make_shared>(kernel, trace_settings); + MegaProver prover(proving_key); + kernel_vk = std::make_shared(prover.proving_key->proving_key); } - // Repeat a similar IVC but use the alternative queue entry just created to provide different (but independently - // valid) witnesses during constraint system construction VS recursive verifier construction. + // PCS verification keys will not match so set to null before comparing + kernel_vk->pcs_verification_key = nullptr; + expected_kernel_vk->pcs_verification_key = nullptr; - ClientIVC ivc; - ivc.trace_settings.structure = TraceStructure::SMALL_TEST; + EXPECT_EQ(*kernel_vk.get(), *expected_kernel_vk.get()); +} - // construct and accumulate a mock app circuit with a single unique public input - Builder app_circuit = construct_mock_app_circuit(ivc); - ivc.accumulate(app_circuit); +// Test generation of "init" kernel VK via dummy IVC data +TEST_F(IvcRecursionConstraintTest, GenerateVKFromConstraints) +{ + const TraceSettings trace_settings{ TraceStructure::SMALL_TEST }; - // Construct kernel_0 - AcirProgram program_0 = construct_mock_kernel_program(ivc.verification_queue, { app_circuit.public_inputs.size() }); + // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel) + std::shared_ptr expected_kernel_vk; + size_t num_app_public_inputs = 0; + { + ClientIVC ivc; + ivc.trace_settings = trace_settings; - // Replace the existing verification queue entry that was used to construct the acir constraint system for the - // kernel with a different (but valid, as shown above) set of inputs - ivc.verification_queue[0] = alternative_verification_queue_entry; - Builder kernel_0 = acir_format::create_kernel_circuit(program_0.constraints, ivc, program_0.witness); + // Construct and accumulate mock app_circuit + Builder app_circuit = construct_mock_app_circuit(ivc); + ivc.accumulate(app_circuit); + num_app_public_inputs = app_circuit.public_inputs.size(); - // The witness should fail to check due to the business logic of the kernel failing - EXPECT_FALSE(CircuitChecker::check(kernel_0)); - ivc.accumulate(kernel_0); + // Construct and accumulate kernel consisting only of the kernel completion logic + AcirProgram program = construct_mock_kernel_program(ivc.verification_queue, { num_app_public_inputs }); + Builder kernel = acir_format::create_kernel_circuit(program.constraints, ivc, program.witness); - // The full IVC should of course also fail to verify since we've accumulated an invalid witness for the kernel - EXPECT_FALSE(ivc.prove_and_verify()); -} + ivc.accumulate(kernel); + expected_kernel_vk = ivc.verification_queue.back().honk_verification_key; + } + + // Now, construct the kernel VK by mocking the post app accumulation state of the IVC + std::shared_ptr kernel_vk; + { + ClientIVC ivc; + ivc.trace_settings = trace_settings; + + // Construct kernel consisting only of the kernel completion logic + acir_format::mock_ivc_oink_accumulation(ivc, num_app_public_inputs - bb::PAIRING_POINT_ACCUMULATOR_SIZE); + AcirProgram program = construct_mock_kernel_program(ivc.verification_queue, { num_app_public_inputs }); + program.witness = {}; // erase witness to mimic VK construction context + + // Create a mock IVC instance from the IVC recursion constraints in the kernel program + ClientIVC mock_ivc = create_mock_ivc_from_constraints(program.constraints.ivc_recursion_constraints); + + // Create a kernel circuit from the kernel program and the mocked IVC + Builder kernel = acir_format::create_kernel_circuit(program.constraints, mock_ivc); + // Note: adding pairing point normally happens in accumulate() + kernel.add_pairing_point_accumulator(stdlib::recursion::init_default_agg_obj_indices(kernel)); + + // Manually construct the VK for the kernel circuit + auto proving_key = std::make_shared>(kernel, ivc.trace_settings); + MegaProver prover(proving_key); + kernel_vk = std::make_shared(prover.proving_key->proving_key); + } + + // PCS verification keys will not match so set to null before comparing + kernel_vk->pcs_verification_key = nullptr; + expected_kernel_vk->pcs_verification_key = nullptr; + + // Compare the VK constructed via running the IVc with the one constructed via mocking + EXPECT_EQ(*kernel_vk.get(), *expected_kernel_vk.get()); +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/proof_surgeon.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/proof_surgeon.hpp index 224d344ae8a..0b2d1768bca 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/proof_surgeon.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/proof_surgeon.hpp @@ -149,6 +149,7 @@ class ProofSurgeon { const size_t num_public_inputs) { // Extract all public inputs except for those corresponding to the aggregation object + ASSERT(num_public_inputs >= bb::PAIRING_POINT_ACCUMULATOR_SIZE); const size_t num_public_inputs_to_extract = num_public_inputs - bb::PAIRING_POINT_ACCUMULATOR_SIZE; std::vector public_input_witnesses = cut_public_inputs_from_proof(proof_witnesses, num_public_inputs_to_extract); diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/execution_trace_usage_tracker.hpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/execution_trace_usage_tracker.hpp index 53db481ff59..f1798fa3c76 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/execution_trace_usage_tracker.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/execution_trace_usage_tracker.hpp @@ -67,7 +67,7 @@ struct ExecutionTraceUsageTracker { // The active ranges for the databus and lookup relations (both based on log-deriv lookup argument) must // incorporate both the lookup/read gate blocks as well as the rows containing the data that is being read. // Update the corresponding ranges accordingly. (Note: tables are constructed at the 'bottom' of the trace). - size_t dyadic_circuit_size = circuit.get_circuit_subgroup_size(fixed_sizes.get_total_structured_size()); + size_t dyadic_circuit_size = fixed_sizes.get_structured_dyadic_size(); active_ranges.busread.first = 0; // databus data is stored at the top of the trace active_ranges.busread.second = std::max(max_databus_size, active_ranges.busread.second); active_ranges.lookup.first = std::min(dyadic_circuit_size - max_tables_size, active_ranges.lookup.first); diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/mega_arithmetization.hpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/mega_arithmetization.hpp index 7cb03ae310a..8a19c73cc43 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/mega_arithmetization.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/mega_arithmetization.hpp @@ -3,6 +3,7 @@ #include "barretenberg/common/ref_vector.hpp" #include "barretenberg/common/zip_view.hpp" #include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/numeric/bitop/get_msb.hpp" #include "barretenberg/plonk_honk_shared/arithmetization/arithmetization.hpp" #include "barretenberg/plonk_honk_shared/types/circuit_type.hpp" @@ -309,13 +310,18 @@ template class MegaArith { info(""); } - size_t get_total_structured_size() + size_t get_structured_dyadic_size() { - size_t total_size = 0; + size_t total_size = 1; // start at 1 because the 0th row is unused for selectors for Honk for (auto block : this->get()) { total_size += block.get_fixed_size(); } - return total_size; + + auto log2_n = static_cast(numeric::get_msb(total_size)); + if ((1UL << log2_n) != (total_size)) { + ++log2_n; + } + return 1UL << log2_n; } bool operator==(const TraceBlocks& other) const = default; diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/ultra_arithmetization.hpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/ultra_arithmetization.hpp index a188d2faef5..d4443a11dbd 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/ultra_arithmetization.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/ultra_arithmetization.hpp @@ -1,6 +1,7 @@ #pragma once #include "barretenberg/common/ref_vector.hpp" +#include "barretenberg/numeric/bitop/get_msb.hpp" #include "barretenberg/plonk_honk_shared/arithmetization/arithmetization.hpp" namespace bb { @@ -167,13 +168,18 @@ template class UltraArith { info("overflow :\t", this->overflow.size()); } - size_t get_total_structured_size() + size_t get_structured_dyadic_size() { size_t total_size = 1; // start at 1 because the 0th row is unused for selectors for Honk for (auto block : this->get()) { total_size += block.get_fixed_size(); } - return total_size; + + auto log2_n = static_cast(numeric::get_msb(total_size)); + if ((1UL << log2_n) != (total_size)) { + ++log2_n; + } + return 1UL << log2_n; } bool operator==(const TraceBlocks& other) const = default; diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/decider_proving_key.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/decider_proving_key.hpp index dd8c8593b84..1de641beec9 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/decider_proving_key.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/decider_proving_key.hpp @@ -317,11 +317,7 @@ template class DeciderProvingKey_ { * @brief Compute dyadic size based on a structured trace with fixed block size * */ - size_t compute_structured_dyadic_size(Circuit& circuit) - { - size_t minimum_size = circuit.blocks.get_total_structured_size(); - return circuit.get_circuit_subgroup_size(minimum_size); - } + size_t compute_structured_dyadic_size(Circuit& circuit) { return circuit.blocks.get_structured_dyadic_size(); } void construct_databus_polynomials(Circuit&) requires IsGoblinFlavor;