From f7d3ec9716ee8fa3cde00b79797ecb6cea92b3d3 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Tue, 13 Aug 2024 16:44:19 +0000 Subject: [PATCH 1/8] remove use of accumulate altogether for aztec_ivc --- .../src/barretenberg/aztec_ivc/aztec_ivc.cpp | 68 +------- .../src/barretenberg/aztec_ivc/aztec_ivc.hpp | 9 +- .../barretenberg/aztec_ivc/aztec_ivc.test.cpp | 146 +++++++++++------- .../aztec_ivc/aztec_ivc_integration.test.cpp | 76 +++++++++ .../aztec_ivc/mock_circuit_producer.hpp | 71 +++++++++ .../aztec_ivc_bench/aztec_ivc.bench.cpp | 57 +------ .../stdlib/primitives/databus/databus.hpp | 2 + 7 files changed, 261 insertions(+), 168 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc_integration.test.cpp create mode 100644 barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp index 87947d8edd6e..b5d2c03befbe 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp @@ -2,33 +2,6 @@ namespace bb { -/** - * @brief Accumulate a circuit into the IVC scheme - * @details If this is the first circuit being accumulated, initialize the prover and verifier accumulators. Otherwise, - * fold the instance for the provided circuit into the accumulator. When two fold proofs have been enqueued, two - * recursive folding verifications are appended to the next circuit that is accumulated, which must be a kernel. - * Similarly, merge proofs are stored in a queue and recursively verified in kernels. - * - * @param circuit Circuit to be accumulated/folded - * @param precomputed_vk Optional precomputed VK (otherwise will be computed herein) - */ -void AztecIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr& precomputed_vk) -{ - circuit_count++; // increment the count of circuits processed into the IVC - - // The aztec architecture dictates that every second circuit is a kernel. This check can be triggered/replaced by - // the presence of the recursive folding verify opcode once it is introduced into noir. - is_kernel = (circuit_count % 2 == 0); - - // If present circuit is a kernel, perform required recursive PG and/or merge verifications and databus checks - if (is_kernel) { - complete_kernel_circuit_logic(circuit); - } - - // Perform PG and/or merge proving - execute_accumulation_prover(circuit, precomputed_vk); -} - /** * @brief Append logic to complete a kernel circuit * @details A kernel circuit may contain some combination of PG recursive verification, merge recursive verification, @@ -38,12 +11,16 @@ void AztecIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr(prover_instance->proving_key); - instance_vk->databus_propagation_data.is_kernel = is_kernel; // Store whether the present circuit is a kernel // If this is the first circuit simply initialize the prover and verifier accumulator instances - if (circuit_count == 1) { + if (!initialized) { fold_output.accumulator = prover_instance; verifier_accumulator = std::make_shared(instance_vk); + initialized = true; } else { // Otherwise, fold the new instance into the accumulator FoldingProver folding_prover({ fold_output.accumulator, prover_instance }); fold_output = folding_prover.fold_instances(); @@ -158,36 +136,6 @@ HonkProof AztecIVC::decider_prove() const return decider_prover.construct_proof(); } -/** - * @brief Given a set of circuits, compute the verification keys that will be required by the IVC scheme - * @details The verification keys computed here are in general not the same as the verification keys for the - * raw input circuits because recursive verifier circuits (merge and/or folding) may be appended to the incoming - * circuits as part accumulation. - * @note This method exists for convenience and is not not meant to be used in practice for IVC. Given a set of - * circuits, it could be run once and for all to compute then save the required VKs. It also provides a convenient - * (albeit innefficient) way of separating out the cost of computing VKs from a benchmark. - * - * @param circuits A copy of the circuits to be accumulated (passing by reference would alter the original circuits) - * @return std::vector> - */ -std::vector> AztecIVC::precompute_folding_verification_keys( - std::vector circuits) -{ - std::vector> vkeys; - - for (auto& circuit : circuits) { - accumulate(circuit); - vkeys.emplace_back(instance_vk); - } - - // Reset the scheme so it can be reused for actual accumulation, maintaining the trace structure setting as is - TraceStructure structure = trace_structure; - *this = AztecIVC(); - this->trace_structure = structure; - - return vkeys; -} - /** * @brief Construct and verify a proof for the IVC * @note Use of this method only makes sense when the prover and verifier are the same entity, e.g. in diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp index c6da51d99e8a..96f7ab6e4443 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp @@ -90,8 +90,9 @@ class AztecIVC { // A flag indicating whether or not to construct a structured trace in the ProverInstance TraceStructure trace_structure = TraceStructure::NONE; - size_t circuit_count = 0; // the number of circuits processed into the IVC - bool is_kernel = false; // is the present circuit a kernel + bool initialized = false; // Is the IVC accumulator initialized + + bool is_kernel = false; // is the present circuit a kernel // Complete the logic of a kernel circuit (e.g. PG/merge recursive verification, databus consistency checks) void complete_kernel_circuit_logic(ClientCircuit& circuit); @@ -100,8 +101,6 @@ class AztecIVC { void execute_accumulation_prover(ClientCircuit& circuit, const std::shared_ptr& precomputed_vk = nullptr); - void accumulate(ClientCircuit& circuit, const std::shared_ptr& precomputed_vk = nullptr); - Proof prove(); static bool verify(const Proof& proof, @@ -115,7 +114,5 @@ class AztecIVC { bool prove_and_verify(); HonkProof decider_prove() const; - - std::vector> precompute_folding_verification_keys(std::vector); }; } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.test.cpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.test.cpp index 1f0cf11a807b..2192b9d8b656 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.test.cpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.test.cpp @@ -52,6 +52,44 @@ class AztecIVCTests : public ::testing::Test { return circuit; } + class MockCircuitProducer { + using ClientCircuit = AztecIVC::ClientCircuit; + + bool is_kernel = false; + + public: + ClientCircuit create_next_circuit(AztecIVC& ivc, size_t log2_num_gates = 16) + { + ClientCircuit circuit{ ivc.goblin.op_queue }; + circuit = create_mock_circuit(ivc, log2_num_gates); // construct mock base logic + if (is_kernel) { + ivc.complete_kernel_circuit_logic(circuit); // complete with recursive verifiers etc + } + is_kernel = !is_kernel; // toggle is_kernel on/off alternatingly + + return circuit; + } + + auto precompute_verification_keys(const size_t num_circuits, + TraceStructure trace_structure, + size_t log2_num_gates = 16) + { + AztecIVC ivc; // temporary IVC instance needed to produce the complete kernel circuits + ivc.trace_structure = trace_structure; + + std::vector> vkeys; + + for (size_t idx = 0; idx < num_circuits; ++idx) { + ClientCircuit circuit = create_next_circuit(ivc, log2_num_gates); // create the next circuit + ivc.execute_accumulation_prover(circuit); // accumulate the circuit + vkeys.emplace_back(ivc.instance_vk); // save the VK for the circuit + } + is_kernel = false; + + return vkeys; + } + }; + /** * @brief Tamper with a proof by finding the first non-zero value and incrementing it by 1 * @@ -77,22 +115,24 @@ TEST_F(AztecIVCTests, Basic) { AztecIVC ivc; + MockCircuitProducer circuit_producer; + // Initialize the IVC with an arbitrary circuit - Builder circuit_0 = create_mock_circuit(ivc); - ivc.accumulate(circuit_0); + Builder circuit_0 = circuit_producer.create_next_circuit(ivc); + ivc.execute_accumulation_prover(circuit_0); // Create another circuit and accumulate - Builder circuit_1 = create_mock_circuit(ivc); - ivc.accumulate(circuit_1); + Builder circuit_1 = circuit_producer.create_next_circuit(ivc); + ivc.execute_accumulation_prover(circuit_1); EXPECT_TRUE(ivc.prove_and_verify()); }; /** * @brief Check that the IVC fails to verify if an intermediate fold proof is invalid - * @details When accumulating 4 circuits, there are 3 fold proofs to verify (the first two are recursively verfied and - * the 3rd is verified as part of the IVC proof). Check that if any of one of these proofs is invalid, the IVC will fail - * to verify. + * @details When accumulating 4 circuits, there are 3 fold proofs to verify (the first two are recursively verfied + * and the 3rd is verified as part of the IVC proof). Check that if any of one of these proofs is invalid, the IVC + * will fail to verify. * */ TEST_F(AztecIVCTests, BadProofFailure) @@ -102,11 +142,13 @@ TEST_F(AztecIVCTests, BadProofFailure) AztecIVC ivc; ivc.trace_structure = TraceStructure::SMALL_TEST; + MockCircuitProducer circuit_producer; + // Construct a set of arbitrary circuits size_t NUM_CIRCUITS = 4; for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { - auto circuit = create_mock_circuit(ivc, /*log2_num_gates=*/5); - ivc.accumulate(circuit); + auto circuit = circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5); + ivc.execute_accumulation_prover(circuit); } EXPECT_TRUE(ivc.prove_and_verify()); } @@ -116,11 +158,13 @@ TEST_F(AztecIVCTests, BadProofFailure) AztecIVC ivc; ivc.trace_structure = TraceStructure::SMALL_TEST; + MockCircuitProducer circuit_producer; + // Construct a set of arbitrary circuits size_t NUM_CIRCUITS = 4; for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { - auto circuit = create_mock_circuit(ivc, /*log2_num_gates=*/5); - ivc.accumulate(circuit); + auto circuit = circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5); + ivc.execute_accumulation_prover(circuit); if (idx == 2) { EXPECT_EQ(ivc.verification_queue.size(), 2); // two proofs after 3 calls to accumulation @@ -136,11 +180,13 @@ TEST_F(AztecIVCTests, BadProofFailure) AztecIVC ivc; ivc.trace_structure = TraceStructure::SMALL_TEST; + MockCircuitProducer circuit_producer; + // Construct a set of arbitrary circuits size_t NUM_CIRCUITS = 4; for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { - auto circuit = create_mock_circuit(ivc, /*log2_num_gates=*/5); - ivc.accumulate(circuit); + auto circuit = circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5); + ivc.execute_accumulation_prover(circuit); if (idx == 2) { EXPECT_EQ(ivc.verification_queue.size(), 2); // two proofs after 3 calls to accumulation @@ -156,11 +202,13 @@ TEST_F(AztecIVCTests, BadProofFailure) AztecIVC ivc; ivc.trace_structure = TraceStructure::SMALL_TEST; + MockCircuitProducer circuit_producer; + // Construct a set of arbitrary circuits size_t NUM_CIRCUITS = 4; for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { - auto circuit = create_mock_circuit(ivc, /*log2_num_gates=*/5); - ivc.accumulate(circuit); + auto circuit = circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5); + ivc.execute_accumulation_prover(circuit); } // Only a single proof should be present in the queue when verification of the IVC is performed @@ -181,16 +229,14 @@ TEST_F(AztecIVCTests, BasicLarge) { AztecIVC ivc; - // Construct a set of arbitrary circuits + MockCircuitProducer circuit_producer; + + // Construct and accumulate set of arbitrary circuits size_t NUM_CIRCUITS = 6; std::vector circuits; for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { - circuits.emplace_back(create_mock_circuit(ivc)); - } - - // Accumulate each circuit - for (auto& circuit : circuits) { - ivc.accumulate(circuit); + auto circuit = circuit_producer.create_next_circuit(ivc); + ivc.execute_accumulation_prover(circuit); } EXPECT_TRUE(ivc.prove_and_verify()); @@ -205,17 +251,17 @@ TEST_F(AztecIVCTests, BasicStructured) AztecIVC ivc; ivc.trace_structure = TraceStructure::SMALL_TEST; - // Construct some circuits of varying size - Builder circuit_0 = create_mock_circuit(ivc, /*log2_num_gates=*/5); - Builder circuit_1 = create_mock_circuit(ivc, /*log2_num_gates=*/6); - Builder circuit_2 = create_mock_circuit(ivc, /*log2_num_gates=*/7); - Builder circuit_3 = create_mock_circuit(ivc, /*log2_num_gates=*/8); + MockCircuitProducer circuit_producer; + + size_t NUM_CIRCUITS = 4; - // The circuits can be accumulated as normal due to the structured trace - ivc.accumulate(circuit_0); - ivc.accumulate(circuit_1); - ivc.accumulate(circuit_2); - ivc.accumulate(circuit_3); + // Construct and accumulate some circuits of varying size + size_t log2_num_gates = 5; + for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { + auto circuit = circuit_producer.create_next_circuit(ivc, log2_num_gates); + ivc.execute_accumulation_prover(circuit); + log2_num_gates += 2; + } EXPECT_TRUE(ivc.prove_and_verify()); }; @@ -228,19 +274,16 @@ TEST_F(AztecIVCTests, PrecomputedVerificationKeys) { AztecIVC ivc; - // Construct a set of arbitrary circuits size_t NUM_CIRCUITS = 4; - std::vector circuits; - for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { - circuits.emplace_back(create_mock_circuit(ivc)); - } - // Precompute the verification keys that will be needed for the IVC - auto precomputed_vkeys = ivc.precompute_folding_verification_keys(circuits); + MockCircuitProducer circuit_producer; + + auto precomputed_vks = circuit_producer.precompute_verification_keys(NUM_CIRCUITS, TraceStructure::NONE); - // Accumulate each circuit using the precomputed VKs - for (auto [circuit, precomputed_vk] : zip_view(circuits, precomputed_vkeys)) { - ivc.accumulate(circuit, precomputed_vk); + // Construct and accumulate set of arbitrary circuits using the precomputed vkeys + for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { + auto circuit = circuit_producer.create_next_circuit(ivc); + ivc.execute_accumulation_prover(circuit, precomputed_vks[idx]); } EXPECT_TRUE(ivc.prove_and_verify()); @@ -255,19 +298,18 @@ TEST_F(AztecIVCTests, StructuredPrecomputedVKs) AztecIVC ivc; ivc.trace_structure = TraceStructure::SMALL_TEST; - // Construct a set of arbitrary circuits size_t NUM_CIRCUITS = 4; - std::vector circuits; - for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { - circuits.emplace_back(create_mock_circuit(ivc, /*log2_num_gates=*/5)); - } + size_t log2_num_gates = 5; // number of gates in baseline mocked circuit - // Precompute the (structured) verification keys that will be needed for the IVC - auto precomputed_vkeys = ivc.precompute_folding_verification_keys(circuits); + MockCircuitProducer circuit_producer; - // Accumulate each circuit - for (auto [circuit, precomputed_vk] : zip_view(circuits, precomputed_vkeys)) { - ivc.accumulate(circuit, precomputed_vk); + auto precomputed_vks = + circuit_producer.precompute_verification_keys(NUM_CIRCUITS, ivc.trace_structure, log2_num_gates); + + // Construct and accumulate set of arbitrary circuits using the precomputed vkeys + for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { + auto circuit = circuit_producer.create_next_circuit(ivc, log2_num_gates); + ivc.execute_accumulation_prover(circuit, precomputed_vks[idx]); } EXPECT_TRUE(ivc.prove_and_verify()); diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc_integration.test.cpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc_integration.test.cpp new file mode 100644 index 000000000000..5ff25da16f1b --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc_integration.test.cpp @@ -0,0 +1,76 @@ +#include "barretenberg/aztec_ivc/aztec_ivc.hpp" +#include "barretenberg/aztec_ivc/mock_circuit_producer.hpp" +#include "barretenberg/goblin/goblin.hpp" +#include "barretenberg/goblin/mock_circuits.hpp" +#include "barretenberg/stdlib_circuit_builders/mega_circuit_builder.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" + +#include + +using namespace bb; + +class AztecIVCIntegrationTests : public ::testing::Test { + protected: + static void SetUpTestSuite() + { + srs::init_crs_factory("../srs_db/ignition"); + srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); + } + + using Flavor = AztecIVC::Flavor; + using FF = typename Flavor::FF; + using VerificationKey = Flavor::VerificationKey; + using Builder = AztecIVC::ClientCircuit; + using MockCircuitProducer = PrivateFunctionExecutionMockCircuitProducer; +}; + +/** + * @brief Prove and verify accumulation of a set of mocked private function execution circuits + * @details This case is meant to mirror the medium complexity benchmark configuration case but processes only 6 + * circuits total (3 app, 3 kernel) to save time. + * + */ +TEST_F(AztecIVCIntegrationTests, BenchmarkCaseSimple) +{ + AztecIVC ivc; + ivc.trace_structure = TraceStructure::AZTEC_IVC_BENCH; + + MockCircuitProducer circuit_producer; + + // Construct and accumulate a series of mocked private function execution circuits + size_t NUM_CIRCUITS = 6; + for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { + Builder circuit = circuit_producer.create_next_circuit(ivc); + + ivc.execute_accumulation_prover(circuit); + } + + EXPECT_TRUE(ivc.prove_and_verify()); +}; + +/** + * @brief Prove and verify accumulation of a set of mocked private function execution circuits + * @details This case is meant to mirror the medium complexity benchmark configuration case but processes only 6 + * circuits total (3 app, 3 kernel) to save time. + * + */ +TEST_F(AztecIVCIntegrationTests, BenchmarkCasePrecomputedVKs) +{ + AztecIVC ivc; + ivc.trace_structure = TraceStructure::AZTEC_IVC_BENCH; + + size_t NUM_CIRCUITS = 6; + + MockCircuitProducer circuit_producer; + + auto precomputed_vks = circuit_producer.precompute_verification_keys(NUM_CIRCUITS, ivc.trace_structure); + + // Construct and accumulate a series of mocked private function execution circuits + for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { + Builder circuit = circuit_producer.create_next_circuit(ivc); + + ivc.execute_accumulation_prover(circuit, precomputed_vks[idx]); + } + + EXPECT_TRUE(ivc.prove_and_verify()); +}; diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp new file mode 100644 index 000000000000..461b8dd730d6 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp @@ -0,0 +1,71 @@ + +#include "barretenberg/aztec_ivc/aztec_ivc.hpp" +#include "barretenberg/common/op_count.hpp" +#include "barretenberg/goblin/mock_circuits.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" +#include "barretenberg/ultra_honk/ultra_verifier.hpp" + +using namespace bb; + +namespace { + +/** + * @brief Manage the construction of mock app/kernel circuits for the private function execution setting + * @details Per the medium complexity benchmark spec, the first app circuit is size 2^19. Subsequent app and kernel + * circuits are size 2^17. Circuits produced are alternatingly app and kernel. + */ +class PrivateFunctionExecutionMockCircuitProducer { + using ClientCircuit = AztecIVC::ClientCircuit; + using Flavor = MegaFlavor; + using VerificationKey = Flavor::VerificationKey; + + size_t circuit_counter = 0; + + public: + ClientCircuit create_next_circuit(AztecIVC& ivc) + { + circuit_counter++; + + bool is_kernel = (circuit_counter % 2 == 0); // Every other circuit is a kernel, starting from the second + + ClientCircuit circuit{ ivc.goblin.op_queue }; + if (is_kernel) { + GoblinMockCircuits::construct_mock_folding_kernel(circuit); // construct mock base logic + ivc.complete_kernel_circuit_logic(circuit); // complete with recursive verifiers etc + } else { + // bool use_large_circuit = false; // first circuit is size 2^19 + bool use_large_circuit = (circuit_counter == 1); // first circuit is size 2^19 + GoblinMockCircuits::construct_mock_app_circuit(circuit, use_large_circuit); + } + return circuit; + } + + /** + * @brief Compute and return the verification keys for a mocked private function execution IVC + * @details For testing/benchmarking only. This method is robust at the cost of being extremely inefficient. It + * simply executes a full IVC for a given number of circuits and stores the verification keys along the way. (In + * practice these VKs will be known to a client prover in advance). + * + * @param num_circuits + * @param trace_structure Trace structuring must be known in advance because it effects the VKs + * @return set of num_circuits-many verification keys + */ + auto precompute_verification_keys(const size_t num_circuits, TraceStructure trace_structure) + { + AztecIVC ivc; // temporary IVC instance needed to produce the complete kernel circuits + ivc.trace_structure = trace_structure; + + std::vector> vkeys; + + for (size_t idx = 0; idx < num_circuits; ++idx) { + ClientCircuit circuit = create_next_circuit(ivc); // create the next circuit + ivc.execute_accumulation_prover(circuit); // accumulate the circuit + vkeys.emplace_back(ivc.instance_vk); // save the VK for the circuit + } + circuit_counter = 0; // reset the internal circuit counter back to 0 + + return vkeys; + } +}; + +} // namespace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/benchmark/aztec_ivc_bench/aztec_ivc.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/aztec_ivc_bench/aztec_ivc.bench.cpp index a24535ea22bf..2c8cb478ed5a 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/aztec_ivc_bench/aztec_ivc.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/aztec_ivc_bench/aztec_ivc.bench.cpp @@ -2,6 +2,7 @@ #include #include "barretenberg/aztec_ivc/aztec_ivc.hpp" +#include "barretenberg/aztec_ivc/mock_circuit_producer.hpp" #include "barretenberg/common/op_count.hpp" #include "barretenberg/common/op_count_google_bench.hpp" #include "barretenberg/goblin/mock_circuits.hpp" @@ -22,6 +23,7 @@ class AztecIVCBench : public benchmark::Fixture { using Builder = MegaCircuitBuilder; using VerifierInstance = VerifierInstance_; using Proof = AztecIVC::Proof; + using MockCircuitProducer = PrivateFunctionExecutionMockCircuitProducer; // Number of function circuits to accumulate(based on Zacs target numbers) static constexpr size_t NUM_ITERATIONS_MEDIUM_COMPLEXITY = 6; @@ -50,52 +52,6 @@ class AztecIVCBench : public benchmark::Fixture { return verified; } - /** - * @brief Precompute the verification keys for the bench given the number of circuits in the IVC - * - * @param ivc - * @param num_function_circuits - * @return auto - */ - static auto precompute_verification_keys(AztecIVC& ivc, const size_t num_circuits) - { - // Produce the set of mocked circuits to be accumulated - MockCircuitMaker mock_circuit_maker; - std::vector circuits; - for (size_t circuit_idx = 0; circuit_idx < num_circuits; ++circuit_idx) { - circuits.emplace_back(mock_circuit_maker.create_next_circuit(ivc)); - } - - // Compute and return the corresponding set of verfication keys - return ivc.precompute_folding_verification_keys(circuits); - } - - /** - * @brief Manage the construction of mock app/kernel circuits - * @details Per the medium complexity benchmark spec, the first app circuit is size 2^19. Subsequent app and kernel - * circuits are size 2^17. Circuits produced are alternatingly app and kernel. - */ - class MockCircuitMaker { - size_t circuit_counter = 0; - - public: - Builder create_next_circuit(AztecIVC& ivc) - { - circuit_counter++; - - bool is_kernel = (circuit_counter % 2 == 0); // Every other circuit is a kernel, starting from the second - - Builder circuit{ ivc.goblin.op_queue }; - if (is_kernel) { // construct mock kernel - GoblinMockCircuits::construct_mock_folding_kernel(circuit); - } else { // construct mock app - bool use_large_circuit = (circuit_counter == 1); - GoblinMockCircuits::construct_mock_app_circuit(circuit, use_large_circuit); - } - return circuit; - } - }; - /** * @brief Perform a specified number of circuit accumulation rounds * @@ -105,16 +61,16 @@ class AztecIVCBench : public benchmark::Fixture { { ASSERT(precomputed_vks.size() == NUM_CIRCUITS); // ensure presence of a precomputed VK for each circuit - MockCircuitMaker mock_circuit_maker; + MockCircuitProducer circuit_producer; for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { Builder circuit; { BB_OP_COUNT_TIME_NAME("construct_circuits"); - circuit = mock_circuit_maker.create_next_circuit(ivc); + circuit = circuit_producer.create_next_circuit(ivc); } - ivc.accumulate(circuit, precomputed_vks[circuit_idx]); + ivc.execute_accumulation_prover(circuit, precomputed_vks[circuit_idx]); } } }; @@ -131,7 +87,8 @@ BENCHMARK_DEFINE_F(AztecIVCBench, FullStructured)(benchmark::State& state) auto total_num_circuits = 2 * static_cast(state.range(0)); // 2x accounts for kernel circuits // Precompute the verification keys for the benchmark circuits - auto precomputed_vkeys = precompute_verification_keys(ivc, total_num_circuits); + MockCircuitProducer circuit_producer; + auto precomputed_vkeys = circuit_producer.precompute_verification_keys(total_num_circuits, ivc.trace_structure); Proof proof; for (auto _ : state) { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp index 6979a32a85b7..c8133192a536 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp @@ -119,6 +119,8 @@ template class DataBusDepot { auto& public_inputs = inst_2->public_inputs; auto& commitments = inst_2->witness_commitments; + info("Databus execute: is_kernel = ", is_kernel_instance); + // Assert equality between return data commitments propagated via the public inputs and the corresponding // calldata commitment if (is_kernel_instance) { // only kernels can contain commitments propagated via public inputs From 253b6ace851c8416b7e2711ba0995898026fa58f Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Tue, 13 Aug 2024 18:28:13 +0000 Subject: [PATCH 2/8] add nontrivial databus data; tests pass and failure mode works --- .../src/barretenberg/aztec_ivc/aztec_ivc.cpp | 36 ++++++++++++ .../aztec_ivc/mock_circuit_producer.hpp | 56 ++++++++++++++++++- 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp index b5d2c03befbe..bc162ade2a34 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp @@ -61,6 +61,42 @@ void AztecIVC::execute_accumulation_prover(ClientCircuit& circuit, // Set the instance verification key from precomputed if available, else compute it instance_vk = precomputed_vk ? precomputed_vk : std::make_shared(prover_instance->proving_key); + if (instance_vk->databus_propagation_data.is_kernel) { + info("Kernel:"); + info("calldata:"); + for (size_t i = 0; i < 5; ++i) { + auto& val = prover_instance->proving_key.polynomials.calldata[i]; + info(val); + } + info("secondary calldata:"); + for (size_t i = 0; i < 5; ++i) { + auto& val = prover_instance->proving_key.polynomials.secondary_calldata[i]; + info(val); + } + info("return data:"); + for (size_t i = 0; i < 5; ++i) { + auto& val = prover_instance->proving_key.polynomials.return_data[i]; + info(val); + } + } else { + info("App:"); + info("calldata:"); + for (size_t i = 0; i < 5; ++i) { + auto& val = prover_instance->proving_key.polynomials.calldata[i]; + info(val); + } + info("secondary calldata:"); + for (size_t i = 0; i < 5; ++i) { + auto& val = prover_instance->proving_key.polynomials.secondary_calldata[i]; + info(val); + } + info("return data:"); + for (size_t i = 0; i < 5; ++i) { + auto& val = prover_instance->proving_key.polynomials.return_data[i]; + info(val); + } + } + // If this is the first circuit simply initialize the prover and verifier accumulator instances if (!initialized) { fold_output.accumulator = prover_instance; diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp index 461b8dd730d6..148af19bbceb 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp @@ -17,10 +17,59 @@ namespace { class PrivateFunctionExecutionMockCircuitProducer { using ClientCircuit = AztecIVC::ClientCircuit; using Flavor = MegaFlavor; + using FF = Flavor::FF; using VerificationKey = Flavor::VerificationKey; size_t circuit_counter = 0; + struct MockDatabusProducer { + using BusArray = std::vector; + + static constexpr size_t BUS_ARRAY_SIZE = 3; // arbitrary length of mock bus arrrays + BusArray app_return_data; + BusArray kernel_return_data; + + FF dummy_val = 1; + + BusArray generate_random_bus_array() + { + BusArray result; + for (size_t i = 0; i < BUS_ARRAY_SIZE; ++i) { + result.emplace_back(dummy_val); + // result.emplace_back(FF::random_element()); + } + dummy_val += 1; + return result; + } + + void populate_app_databus(ClientCircuit& circuit) + { + // update the app return data and populate it in the circuit + app_return_data = generate_random_bus_array(); + for (auto& val : app_return_data) { + circuit.add_public_return_data(circuit.add_variable(val)); + } + }; + + void populate_kernel_databus(ClientCircuit& circuit) + { + // populate the two calldata inputs from the previous kernel and app return data (if it exists) + for (auto& val : kernel_return_data) { + circuit.add_public_calldata(circuit.add_variable(val)); + } + for (auto& val : app_return_data) { + circuit.add_public_secondary_calldata(circuit.add_variable(val)); + } + // update the kernel return data and populate it in the circuit + kernel_return_data = generate_random_bus_array(); + for (auto& val : kernel_return_data) { + circuit.add_public_return_data(circuit.add_variable(val)); + } + }; + }; + + MockDatabusProducer mock_databus; + public: ClientCircuit create_next_circuit(AztecIVC& ivc) { @@ -31,11 +80,12 @@ class PrivateFunctionExecutionMockCircuitProducer { ClientCircuit circuit{ ivc.goblin.op_queue }; if (is_kernel) { GoblinMockCircuits::construct_mock_folding_kernel(circuit); // construct mock base logic + mock_databus.populate_kernel_databus(circuit); // populate databus inputs/outputs ivc.complete_kernel_circuit_logic(circuit); // complete with recursive verifiers etc } else { - // bool use_large_circuit = false; // first circuit is size 2^19 - bool use_large_circuit = (circuit_counter == 1); // first circuit is size 2^19 - GoblinMockCircuits::construct_mock_app_circuit(circuit, use_large_circuit); + bool use_large_circuit = (circuit_counter == 1); // first circuit is size 2^19 + GoblinMockCircuits::construct_mock_app_circuit(circuit, use_large_circuit); // construct mock app + mock_databus.populate_app_databus(circuit); // populate databus outputs } return circuit; } From f6105d074fd56125967c866323bddcb494cb5680 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Tue, 13 Aug 2024 20:14:00 +0000 Subject: [PATCH 3/8] failure test for databus consistency checks --- .../aztec_ivc/aztec_ivc_integration.test.cpp | 41 ++++++++++++++-- .../aztec_ivc/mock_circuit_producer.hpp | 48 ++++++++++++++----- .../stdlib/primitives/databus/databus.hpp | 5 +- 3 files changed, 78 insertions(+), 16 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc_integration.test.cpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc_integration.test.cpp index 5ff25da16f1b..abc34ab49bda 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc_integration.test.cpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc_integration.test.cpp @@ -9,6 +9,10 @@ using namespace bb; +/** + * @brief A test suite that mirrors the logic in the nominal IVC benchmark case + * + */ class AztecIVCIntegrationTests : public ::testing::Test { protected: static void SetUpTestSuite() @@ -49,9 +53,8 @@ TEST_F(AztecIVCIntegrationTests, BenchmarkCaseSimple) }; /** - * @brief Prove and verify accumulation of a set of mocked private function execution circuits - * @details This case is meant to mirror the medium complexity benchmark configuration case but processes only 6 - * circuits total (3 app, 3 kernel) to save time. + * @brief Prove and verify accumulation of a set of mocked private function execution circuits with precomputed + * verification keys * */ TEST_F(AztecIVCIntegrationTests, BenchmarkCasePrecomputedVKs) @@ -74,3 +77,35 @@ TEST_F(AztecIVCIntegrationTests, BenchmarkCasePrecomputedVKs) EXPECT_TRUE(ivc.prove_and_verify()); }; + +/** + * @brief Demonstrate that a databus inconsistency leads to verification failure for the IVC + * @details Kernel circuits contain databus consistency checks that establish that data was passed faithfully between + * circuits, e.g. the output (return_data) of an app was the input (secondary_calldata) of a kernel. This test tampers + * with the databus in such a way that one of the kernels receives secondary_calldata based on tampered app return data. + * This leads to an invalid witness in the check that ensures that the two corresponding commitments are equal and thus + * causes failure of the IVC to verify. + * + */ +TEST_F(AztecIVCIntegrationTests, DatabusFailure) +{ + AztecIVC ivc; + ivc.trace_structure = TraceStructure::AZTEC_IVC_BENCH; + + MockCircuitProducer circuit_producer; + + // Construct and accumulate a series of mocked private function execution circuits + size_t NUM_CIRCUITS = 6; + for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { + Builder circuit = circuit_producer.create_next_circuit(ivc); + + // Tamper with the return data of the second app circuit before it is processed as input to the next kernel + if (idx == 2) { + circuit_producer.tamper_with_databus(); + } + + ivc.execute_accumulation_prover(circuit); + } + + EXPECT_FALSE(ivc.prove_and_verify()); +}; diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp index 148af19bbceb..4963197203e3 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp @@ -22,55 +22,76 @@ class PrivateFunctionExecutionMockCircuitProducer { size_t circuit_counter = 0; - struct MockDatabusProducer { + /** + * @brief Test utility for coordinating passing of databus data between mocked private function execution circuits + * @details Facilitates testing of the databus consistency checks that establish the correct passing of databus data + * between circuits. Generates arbitrary return data for each app/kernel. Sets the kernel calldata and + * secondary_calldata based respectively on the previous kernel return data and app return data. + */ + class MockDatabusProducer { + private: using BusArray = std::vector; static constexpr size_t BUS_ARRAY_SIZE = 3; // arbitrary length of mock bus arrrays BusArray app_return_data; BusArray kernel_return_data; - FF dummy_val = 1; + FF dummy_return_val = 1; // use simple return val for easier test debugging BusArray generate_random_bus_array() { BusArray result; for (size_t i = 0; i < BUS_ARRAY_SIZE; ++i) { - result.emplace_back(dummy_val); - // result.emplace_back(FF::random_element()); + result.emplace_back(dummy_return_val); } - dummy_val += 1; + dummy_return_val += 1; return result; } + public: + /** + * @brief Update the app return data and populate it in the app circuit + */ void populate_app_databus(ClientCircuit& circuit) { - // update the app return data and populate it in the circuit app_return_data = generate_random_bus_array(); for (auto& val : app_return_data) { circuit.add_public_return_data(circuit.add_variable(val)); } }; + /** + * @brief Populate the calldata and secondary calldata in the kernel from respectively the previous kernel and + * app return data. Update and populate the return data for the present kernel. + */ void populate_kernel_databus(ClientCircuit& circuit) { - // populate the two calldata inputs from the previous kernel and app return data (if it exists) - for (auto& val : kernel_return_data) { + for (auto& val : kernel_return_data) { // populate calldata from previous kernel return data circuit.add_public_calldata(circuit.add_variable(val)); } - for (auto& val : app_return_data) { + for (auto& val : app_return_data) { // populate secondary_calldata from app return data circuit.add_public_secondary_calldata(circuit.add_variable(val)); } - // update the kernel return data and populate it in the circuit - kernel_return_data = generate_random_bus_array(); + kernel_return_data = generate_random_bus_array(); // update the return data for the present kernel circuit for (auto& val : kernel_return_data) { circuit.add_public_return_data(circuit.add_variable(val)); } }; + + /** + * @brief Add an arbitrary value to the app return data. This leads to a descrepency between the values used by + * the app itself and the secondary_calldata values in the kernel that will be set based on these tampered + * values. + */ + void tamper_with_app_return_data() { app_return_data.emplace_back(17); } }; MockDatabusProducer mock_databus; public: + /** + * @brief Create a the next circuit (app/kernel) in a mocked private function execution stack + */ ClientCircuit create_next_circuit(AztecIVC& ivc) { circuit_counter++; @@ -90,6 +111,11 @@ class PrivateFunctionExecutionMockCircuitProducer { return circuit; } + /** + * @brief Tamper with databus data to facilitate failure testing + */ + void tamper_with_databus() { mock_databus.tamper_with_app_return_data(); } + /** * @brief Compute and return the verification keys for a mocked private function execution IVC * @details For testing/benchmarking only. This method is robust at the cost of being extremely inefficient. It diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp index c8133192a536..f7085bc1ea3b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp @@ -119,8 +119,6 @@ template class DataBusDepot { auto& public_inputs = inst_2->public_inputs; auto& commitments = inst_2->witness_commitments; - info("Databus execute: is_kernel = ", is_kernel_instance); - // Assert equality between return data commitments propagated via the public inputs and the corresponding // calldata commitment if (is_kernel_instance) { // only kernels can contain commitments propagated via public inputs @@ -227,6 +225,9 @@ template class DataBusDepot { void assert_equality_of_commitments(Commitment& P0, Commitment& P1) { + if (P0.get_value() != P1.get_value()) { // debug print indicating consistency check failure + info("DataBusDepot: Databus consistency check failed!"); + } P0.x.assert_equal(P1.x); P0.y.assert_equal(P1.y); } From f50f0e6a70e03452a4f5f13ff2f8cff73861adfa Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Tue, 13 Aug 2024 22:44:57 +0000 Subject: [PATCH 4/8] change method name to accumulate and clean up --- .../src/barretenberg/aztec_ivc/aztec_ivc.cpp | 44 +------------------ .../src/barretenberg/aztec_ivc/aztec_ivc.hpp | 3 +- .../barretenberg/aztec_ivc/aztec_ivc.test.cpp | 22 +++++----- .../aztec_ivc/aztec_ivc_integration.test.cpp | 6 +-- .../aztec_ivc/mock_circuit_producer.hpp | 2 +- .../aztec_ivc_bench/aztec_ivc.bench.cpp | 2 +- 6 files changed, 18 insertions(+), 61 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp index bc162ade2a34..04e44eabb0dd 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp @@ -16,11 +16,7 @@ void AztecIVC::complete_kernel_circuit_logic(ClientCircuit& circuit) // The folding verification queue should be either empty or contain two fold proofs ASSERT(verification_queue.empty() || verification_queue.size() == 2); - info("Complete kernel."); - info("verification_queue.size() = ", verification_queue.size()); - info("merge_verification_queue.size() = ", merge_verification_queue.size()); for (auto& [proof, vkey] : verification_queue) { - info("Append recursive verifier."); // Perform folding recursive verification FoldingRecursiveVerifier verifier{ &circuit, { verifier_accumulator, { vkey } } }; auto verifier_accum = verifier.verify_folding_proof(proof); @@ -33,7 +29,6 @@ void AztecIVC::complete_kernel_circuit_logic(ClientCircuit& circuit) // Recusively verify all merge proofs in queue for (auto& proof : merge_verification_queue) { - info("Append recursive merge."); goblin.verify_merge(circuit, proof); } merge_verification_queue.clear(); @@ -48,8 +43,7 @@ void AztecIVC::complete_kernel_circuit_logic(ClientCircuit& circuit) * @param circuit * @param precomputed_vk */ -void AztecIVC::execute_accumulation_prover(ClientCircuit& circuit, - const std::shared_ptr& precomputed_vk) +void AztecIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr& precomputed_vk) { // Construct merge proof for the present circuit and add to merge verification queue MergeProof merge_proof = goblin.prove_merge(circuit); @@ -61,42 +55,6 @@ void AztecIVC::execute_accumulation_prover(ClientCircuit& circuit, // Set the instance verification key from precomputed if available, else compute it instance_vk = precomputed_vk ? precomputed_vk : std::make_shared(prover_instance->proving_key); - if (instance_vk->databus_propagation_data.is_kernel) { - info("Kernel:"); - info("calldata:"); - for (size_t i = 0; i < 5; ++i) { - auto& val = prover_instance->proving_key.polynomials.calldata[i]; - info(val); - } - info("secondary calldata:"); - for (size_t i = 0; i < 5; ++i) { - auto& val = prover_instance->proving_key.polynomials.secondary_calldata[i]; - info(val); - } - info("return data:"); - for (size_t i = 0; i < 5; ++i) { - auto& val = prover_instance->proving_key.polynomials.return_data[i]; - info(val); - } - } else { - info("App:"); - info("calldata:"); - for (size_t i = 0; i < 5; ++i) { - auto& val = prover_instance->proving_key.polynomials.calldata[i]; - info(val); - } - info("secondary calldata:"); - for (size_t i = 0; i < 5; ++i) { - auto& val = prover_instance->proving_key.polynomials.secondary_calldata[i]; - info(val); - } - info("return data:"); - for (size_t i = 0; i < 5; ++i) { - auto& val = prover_instance->proving_key.polynomials.return_data[i]; - info(val); - } - } - // If this is the first circuit simply initialize the prover and verifier accumulator instances if (!initialized) { fold_output.accumulator = prover_instance; diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp index 96f7ab6e4443..6171ecceafdd 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp @@ -98,8 +98,7 @@ class AztecIVC { void complete_kernel_circuit_logic(ClientCircuit& circuit); // Perform prover work for accumulation (e.g. PG folding, merge proving) - void execute_accumulation_prover(ClientCircuit& circuit, - const std::shared_ptr& precomputed_vk = nullptr); + void accumulate(ClientCircuit& circuit, const std::shared_ptr& precomputed_vk = nullptr); Proof prove(); diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.test.cpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.test.cpp index 2192b9d8b656..0dae210a1eff 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.test.cpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.test.cpp @@ -81,7 +81,7 @@ class AztecIVCTests : public ::testing::Test { for (size_t idx = 0; idx < num_circuits; ++idx) { ClientCircuit circuit = create_next_circuit(ivc, log2_num_gates); // create the next circuit - ivc.execute_accumulation_prover(circuit); // accumulate the circuit + ivc.accumulate(circuit); // accumulate the circuit vkeys.emplace_back(ivc.instance_vk); // save the VK for the circuit } is_kernel = false; @@ -119,11 +119,11 @@ TEST_F(AztecIVCTests, Basic) // Initialize the IVC with an arbitrary circuit Builder circuit_0 = circuit_producer.create_next_circuit(ivc); - ivc.execute_accumulation_prover(circuit_0); + ivc.accumulate(circuit_0); // Create another circuit and accumulate Builder circuit_1 = circuit_producer.create_next_circuit(ivc); - ivc.execute_accumulation_prover(circuit_1); + ivc.accumulate(circuit_1); EXPECT_TRUE(ivc.prove_and_verify()); }; @@ -148,7 +148,7 @@ TEST_F(AztecIVCTests, BadProofFailure) size_t NUM_CIRCUITS = 4; for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { auto circuit = circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5); - ivc.execute_accumulation_prover(circuit); + ivc.accumulate(circuit); } EXPECT_TRUE(ivc.prove_and_verify()); } @@ -164,7 +164,7 @@ TEST_F(AztecIVCTests, BadProofFailure) size_t NUM_CIRCUITS = 4; for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { auto circuit = circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5); - ivc.execute_accumulation_prover(circuit); + ivc.accumulate(circuit); if (idx == 2) { EXPECT_EQ(ivc.verification_queue.size(), 2); // two proofs after 3 calls to accumulation @@ -186,7 +186,7 @@ TEST_F(AztecIVCTests, BadProofFailure) size_t NUM_CIRCUITS = 4; for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { auto circuit = circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5); - ivc.execute_accumulation_prover(circuit); + ivc.accumulate(circuit); if (idx == 2) { EXPECT_EQ(ivc.verification_queue.size(), 2); // two proofs after 3 calls to accumulation @@ -208,7 +208,7 @@ TEST_F(AztecIVCTests, BadProofFailure) size_t NUM_CIRCUITS = 4; for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { auto circuit = circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5); - ivc.execute_accumulation_prover(circuit); + ivc.accumulate(circuit); } // Only a single proof should be present in the queue when verification of the IVC is performed @@ -236,7 +236,7 @@ TEST_F(AztecIVCTests, BasicLarge) std::vector circuits; for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { auto circuit = circuit_producer.create_next_circuit(ivc); - ivc.execute_accumulation_prover(circuit); + ivc.accumulate(circuit); } EXPECT_TRUE(ivc.prove_and_verify()); @@ -259,7 +259,7 @@ TEST_F(AztecIVCTests, BasicStructured) size_t log2_num_gates = 5; for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { auto circuit = circuit_producer.create_next_circuit(ivc, log2_num_gates); - ivc.execute_accumulation_prover(circuit); + ivc.accumulate(circuit); log2_num_gates += 2; } @@ -283,7 +283,7 @@ TEST_F(AztecIVCTests, PrecomputedVerificationKeys) // Construct and accumulate set of arbitrary circuits using the precomputed vkeys for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { auto circuit = circuit_producer.create_next_circuit(ivc); - ivc.execute_accumulation_prover(circuit, precomputed_vks[idx]); + ivc.accumulate(circuit, precomputed_vks[idx]); } EXPECT_TRUE(ivc.prove_and_verify()); @@ -309,7 +309,7 @@ TEST_F(AztecIVCTests, StructuredPrecomputedVKs) // Construct and accumulate set of arbitrary circuits using the precomputed vkeys for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { auto circuit = circuit_producer.create_next_circuit(ivc, log2_num_gates); - ivc.execute_accumulation_prover(circuit, precomputed_vks[idx]); + ivc.accumulate(circuit, precomputed_vks[idx]); } EXPECT_TRUE(ivc.prove_and_verify()); diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc_integration.test.cpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc_integration.test.cpp index abc34ab49bda..55eca16b1260 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc_integration.test.cpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc_integration.test.cpp @@ -46,7 +46,7 @@ TEST_F(AztecIVCIntegrationTests, BenchmarkCaseSimple) for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { Builder circuit = circuit_producer.create_next_circuit(ivc); - ivc.execute_accumulation_prover(circuit); + ivc.accumulate(circuit); } EXPECT_TRUE(ivc.prove_and_verify()); @@ -72,7 +72,7 @@ TEST_F(AztecIVCIntegrationTests, BenchmarkCasePrecomputedVKs) for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { Builder circuit = circuit_producer.create_next_circuit(ivc); - ivc.execute_accumulation_prover(circuit, precomputed_vks[idx]); + ivc.accumulate(circuit, precomputed_vks[idx]); } EXPECT_TRUE(ivc.prove_and_verify()); @@ -104,7 +104,7 @@ TEST_F(AztecIVCIntegrationTests, DatabusFailure) circuit_producer.tamper_with_databus(); } - ivc.execute_accumulation_prover(circuit); + ivc.accumulate(circuit); } EXPECT_FALSE(ivc.prove_and_verify()); diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp index 4963197203e3..c5a8308578b2 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp @@ -135,7 +135,7 @@ class PrivateFunctionExecutionMockCircuitProducer { for (size_t idx = 0; idx < num_circuits; ++idx) { ClientCircuit circuit = create_next_circuit(ivc); // create the next circuit - ivc.execute_accumulation_prover(circuit); // accumulate the circuit + ivc.accumulate(circuit); // accumulate the circuit vkeys.emplace_back(ivc.instance_vk); // save the VK for the circuit } circuit_counter = 0; // reset the internal circuit counter back to 0 diff --git a/barretenberg/cpp/src/barretenberg/benchmark/aztec_ivc_bench/aztec_ivc.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/aztec_ivc_bench/aztec_ivc.bench.cpp index 2c8cb478ed5a..96004352476e 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/aztec_ivc_bench/aztec_ivc.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/aztec_ivc_bench/aztec_ivc.bench.cpp @@ -70,7 +70,7 @@ class AztecIVCBench : public benchmark::Fixture { circuit = circuit_producer.create_next_circuit(ivc); } - ivc.execute_accumulation_prover(circuit, precomputed_vks[circuit_idx]); + ivc.accumulate(circuit, precomputed_vks[circuit_idx]); } } }; From 8fbfcd6280228882fe20849611a8e152624bf849 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Wed, 14 Aug 2024 00:47:38 +0000 Subject: [PATCH 5/8] more cleanup and comments --- .../src/barretenberg/aztec_ivc/aztec_ivc.hpp | 2 - .../barretenberg/aztec_ivc/aztec_ivc.test.cpp | 24 ++-- .../aztec_ivc/mock_circuit_producer.hpp | 131 +++++++++--------- 3 files changed, 81 insertions(+), 76 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp index 6171ecceafdd..eafb711549d3 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp @@ -92,8 +92,6 @@ class AztecIVC { bool initialized = false; // Is the IVC accumulator initialized - bool is_kernel = false; // is the present circuit a kernel - // Complete the logic of a kernel circuit (e.g. PG/merge recursive verification, databus consistency checks) void complete_kernel_circuit_logic(ClientCircuit& circuit); diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.test.cpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.test.cpp index 0dae210a1eff..ee3a66d1aba5 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.test.cpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.test.cpp @@ -52,6 +52,10 @@ class AztecIVCTests : public ::testing::Test { return circuit; } + /** + * @brief A test utility for generating alternating mock app and kernel circuits and precomputing verification keys + * + */ class MockCircuitProducer { using ClientCircuit = AztecIVC::ClientCircuit; @@ -130,9 +134,9 @@ TEST_F(AztecIVCTests, Basic) /** * @brief Check that the IVC fails to verify if an intermediate fold proof is invalid - * @details When accumulating 4 circuits, there are 3 fold proofs to verify (the first two are recursively verfied - * and the 3rd is verified as part of the IVC proof). Check that if any of one of these proofs is invalid, the IVC - * will fail to verify. + * @details When accumulating 4 circuits, there are 3 fold proofs to verify (the first two are recursively verfied and + * the 3rd is verified as part of the IVC proof). Check that if any of one of these proofs is invalid, the IVC will fail + * to verify. * */ TEST_F(AztecIVCTests, BadProofFailure) @@ -144,7 +148,7 @@ TEST_F(AztecIVCTests, BadProofFailure) MockCircuitProducer circuit_producer; - // Construct a set of arbitrary circuits + // Construct and accumulate a set of mocked private function execution circuits size_t NUM_CIRCUITS = 4; for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { auto circuit = circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5); @@ -160,7 +164,7 @@ TEST_F(AztecIVCTests, BadProofFailure) MockCircuitProducer circuit_producer; - // Construct a set of arbitrary circuits + // Construct and accumulate a set of mocked private function execution circuits size_t NUM_CIRCUITS = 4; for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { auto circuit = circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5); @@ -182,7 +186,7 @@ TEST_F(AztecIVCTests, BadProofFailure) MockCircuitProducer circuit_producer; - // Construct a set of arbitrary circuits + // Construct and accumulate a set of mocked private function execution circuits size_t NUM_CIRCUITS = 4; for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { auto circuit = circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5); @@ -204,7 +208,7 @@ TEST_F(AztecIVCTests, BadProofFailure) MockCircuitProducer circuit_producer; - // Construct a set of arbitrary circuits + // Construct and accumulate a set of mocked private function execution circuits size_t NUM_CIRCUITS = 4; for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { auto circuit = circuit_producer.create_next_circuit(ivc, /*log2_num_gates=*/5); @@ -231,7 +235,7 @@ TEST_F(AztecIVCTests, BasicLarge) MockCircuitProducer circuit_producer; - // Construct and accumulate set of arbitrary circuits + // Construct and accumulate a set of mocked private function execution circuits size_t NUM_CIRCUITS = 6; std::vector circuits; for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { @@ -280,7 +284,7 @@ TEST_F(AztecIVCTests, PrecomputedVerificationKeys) auto precomputed_vks = circuit_producer.precompute_verification_keys(NUM_CIRCUITS, TraceStructure::NONE); - // Construct and accumulate set of arbitrary circuits using the precomputed vkeys + // Construct and accumulate set of circuits using the precomputed vkeys for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { auto circuit = circuit_producer.create_next_circuit(ivc); ivc.accumulate(circuit, precomputed_vks[idx]); @@ -306,7 +310,7 @@ TEST_F(AztecIVCTests, StructuredPrecomputedVKs) auto precomputed_vks = circuit_producer.precompute_verification_keys(NUM_CIRCUITS, ivc.trace_structure, log2_num_gates); - // Construct and accumulate set of arbitrary circuits using the precomputed vkeys + // Construct and accumulate set of circuits using the precomputed vkeys for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { auto circuit = circuit_producer.create_next_circuit(ivc, log2_num_gates); ivc.accumulate(circuit, precomputed_vks[idx]); diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp index c5a8308578b2..e6d48e08b321 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/mock_circuit_producer.hpp @@ -10,82 +10,85 @@ using namespace bb; namespace { /** - * @brief Manage the construction of mock app/kernel circuits for the private function execution setting - * @details Per the medium complexity benchmark spec, the first app circuit is size 2^19. Subsequent app and kernel - * circuits are size 2^17. Circuits produced are alternatingly app and kernel. + * @brief Test utility for coordinating passing of databus data between mocked private function execution circuits + * @details Facilitates testing of the databus consistency checks that establish the correct passing of databus data + * between circuits. Generates arbitrary return data for each app/kernel. Sets the kernel calldata and + * secondary_calldata based respectively on the previous kernel return data and app return data. */ -class PrivateFunctionExecutionMockCircuitProducer { +class MockDatabusProducer { + private: using ClientCircuit = AztecIVC::ClientCircuit; using Flavor = MegaFlavor; using FF = Flavor::FF; - using VerificationKey = Flavor::VerificationKey; + using BusDataArray = std::vector; - size_t circuit_counter = 0; + static constexpr size_t BUS_ARRAY_SIZE = 3; // arbitrary length of mock bus inputs + BusDataArray app_return_data; + BusDataArray kernel_return_data; + FF dummy_return_val = 1; // use simple return val for easier test debugging + + BusDataArray generate_random_bus_array() + { + BusDataArray result; + for (size_t i = 0; i < BUS_ARRAY_SIZE; ++i) { + result.emplace_back(dummy_return_val); + } + dummy_return_val += 1; + return result; + } + + public: /** - * @brief Test utility for coordinating passing of databus data between mocked private function execution circuits - * @details Facilitates testing of the databus consistency checks that establish the correct passing of databus data - * between circuits. Generates arbitrary return data for each app/kernel. Sets the kernel calldata and - * secondary_calldata based respectively on the previous kernel return data and app return data. + * @brief Update the app return data and populate it in the app circuit */ - class MockDatabusProducer { - private: - using BusArray = std::vector; - - static constexpr size_t BUS_ARRAY_SIZE = 3; // arbitrary length of mock bus arrrays - BusArray app_return_data; - BusArray kernel_return_data; - - FF dummy_return_val = 1; // use simple return val for easier test debugging - - BusArray generate_random_bus_array() - { - BusArray result; - for (size_t i = 0; i < BUS_ARRAY_SIZE; ++i) { - result.emplace_back(dummy_return_val); - } - dummy_return_val += 1; - return result; + void populate_app_databus(ClientCircuit& circuit) + { + app_return_data = generate_random_bus_array(); + for (auto& val : app_return_data) { + circuit.add_public_return_data(circuit.add_variable(val)); } + }; - public: - /** - * @brief Update the app return data and populate it in the app circuit - */ - void populate_app_databus(ClientCircuit& circuit) - { - app_return_data = generate_random_bus_array(); - for (auto& val : app_return_data) { - circuit.add_public_return_data(circuit.add_variable(val)); - } - }; - - /** - * @brief Populate the calldata and secondary calldata in the kernel from respectively the previous kernel and - * app return data. Update and populate the return data for the present kernel. - */ - void populate_kernel_databus(ClientCircuit& circuit) - { - for (auto& val : kernel_return_data) { // populate calldata from previous kernel return data - circuit.add_public_calldata(circuit.add_variable(val)); - } - for (auto& val : app_return_data) { // populate secondary_calldata from app return data - circuit.add_public_secondary_calldata(circuit.add_variable(val)); - } - kernel_return_data = generate_random_bus_array(); // update the return data for the present kernel circuit - for (auto& val : kernel_return_data) { - circuit.add_public_return_data(circuit.add_variable(val)); - } - }; - - /** - * @brief Add an arbitrary value to the app return data. This leads to a descrepency between the values used by - * the app itself and the secondary_calldata values in the kernel that will be set based on these tampered - * values. - */ - void tamper_with_app_return_data() { app_return_data.emplace_back(17); } + /** + * @brief Populate the calldata and secondary calldata in the kernel from respectively the previous kernel and app + * return data. Update and populate the return data for the present kernel. + */ + void populate_kernel_databus(ClientCircuit& circuit) + { + for (auto& val : kernel_return_data) { // populate calldata from previous kernel return data + circuit.add_public_calldata(circuit.add_variable(val)); + } + for (auto& val : app_return_data) { // populate secondary_calldata from app return data + circuit.add_public_secondary_calldata(circuit.add_variable(val)); + } + kernel_return_data = generate_random_bus_array(); // update the return data for the present kernel circuit + for (auto& val : kernel_return_data) { + circuit.add_public_return_data(circuit.add_variable(val)); + } }; + /** + * @brief Add an arbitrary value to the app return data. This leads to a descrepency between the values used by the + * app itself and the secondary_calldata values in the kernel that will be set based on these tampered values. + */ + void tamper_with_app_return_data() { app_return_data.emplace_back(17); } +}; + +/** + * @brief Manage the construction of mock app/kernel circuits for the private function execution setting + * @details Per the medium complexity benchmark spec, the first app circuit is size 2^19. Subsequent app and kernel + * circuits are size 2^17. Circuits produced are alternatingly app and kernel. Mock databus data is passed between the + * circuits in a manor conistent with the real architecture in order to facilitate testing of databus consistency + * checks. + */ +class PrivateFunctionExecutionMockCircuitProducer { + using ClientCircuit = AztecIVC::ClientCircuit; + using Flavor = MegaFlavor; + using VerificationKey = Flavor::VerificationKey; + + size_t circuit_counter = 0; + MockDatabusProducer mock_databus; public: From a5c55bfbd45bb2399a98567753261934a4be2368 Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Wed, 14 Aug 2024 14:17:56 +0000 Subject: [PATCH 6/8] upodate bloc sizes after master merge --- .../arithmetization/mega_arithmetization.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 8f0e9e357213..c3f898f7d702 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 @@ -89,8 +89,8 @@ template class MegaArith { this->aux = 137000; this->lookup = 72000; this->busread = 1 << 7; - this->poseidon_external = 3000; - this->poseidon_internal = 17000; + this->poseidon_external = 3500; + this->poseidon_internal = 17500; } }; From 654e8df46913120d52c62e0ea1b6a996e58fbd8e Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Wed, 14 Aug 2024 16:07:01 +0000 Subject: [PATCH 7/8] add explicit bench tracking of prover polys constructor --- .../src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp index 618b726c2ffe..e14eb3e6fa37 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp @@ -7,7 +7,6 @@ #include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/plonk_honk_shared/library/grand_product_delta.hpp" #include "barretenberg/plonk_honk_shared/library/grand_product_library.hpp" -#include "barretenberg/polynomials/polynomial.hpp" #include "barretenberg/polynomials/univariate.hpp" #include "barretenberg/relations/auxiliary_relation.hpp" #include "barretenberg/relations/databus_lookup_relation.hpp" @@ -376,6 +375,7 @@ class MegaFlavor { ProverPolynomials() = default; ProverPolynomials(size_t circuit_size) { // Initialize all unshifted polynomials to the zero polynomial and initialize the shifted polys + BB_OP_COUNT_TIME_NAME("ProverPolynomials(size_t)"); for (auto& poly : get_unshifted()) { poly = Polynomial{ circuit_size }; } From 77d65f21956e9e243f157f55654d87a918e2d17b Mon Sep 17 00:00:00 2001 From: ledwards2225 Date: Wed, 14 Aug 2024 16:08:52 +0000 Subject: [PATCH 8/8] add TODO --- .../src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp index e14eb3e6fa37..e3e3b71cbd2a 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp @@ -375,6 +375,8 @@ class MegaFlavor { ProverPolynomials() = default; ProverPolynomials(size_t circuit_size) { // Initialize all unshifted polynomials to the zero polynomial and initialize the shifted polys + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1072): Unexpected jump in time to allocate all + // of these polys (in aztec_ivc_bench only). BB_OP_COUNT_TIME_NAME("ProverPolynomials(size_t)"); for (auto& poly : get_unshifted()) { poly = Polynomial{ circuit_size };