From c75fbd44c4b564e703ed9e33e948368eadc0867a Mon Sep 17 00:00:00 2001 From: ludamad Date: Fri, 17 May 2024 17:12:53 -0400 Subject: [PATCH 1/9] chore(ci): only run circleci on master (#6525) We should be catching errors in old CI, and we were hitting spot instance interruptions that seemingly the best advice was 'ignore them'. Probably time for this --- .circleci/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 534fe6bec59..361d11a9a72 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -483,6 +483,9 @@ workflows: # # Used to generate a dynamic 'system' workflow # # This is rewritten to 'system' on the real workflow (otherwise this is ignored by circleci) # equal: [NEVER, << pipeline.parameters.workflow >>] + when: + and: + - equal: [ master, << pipeline.git.branch >> ] jobs: # Noir - noir-x86_64: *defaults From f360b3fd30b9dd1e80e5f1a3d42c325c0f54f8ed Mon Sep 17 00:00:00 2001 From: ledwards2225 <98505400+ledwards2225@users.noreply.github.com> Date: Fri, 17 May 2024 14:17:14 -0700 Subject: [PATCH 2/9] feat: improved ClientIvc (#6429) The goal is to make `ClientIvc` more flexible and user friendly, primarily through making recursive folding verification an internal component to the class. E.g. given an arbitrary collection of circuits in container `circuits`, I can now simply do this: ```cpp ClientIvc ivc; for (auto circuit : circuits) { ivc.accumulate(circuit); } proof = ivc.prove(); ``` where `proof`, if verified, establishes knowledge of a witness satisfying all 10 circuits. Previously this was not the case unless the required recursive folding verifiers were baked into the circuits being accumulated (as was the case with the kernel). Now, `accumulate` automatically appends a recursive folding verifier to each circuit (beginning with the third call to accumulate since prior to that no fold proof exists). Note that this is already how we were handling the recursive merge verification. This has the advantage of removing details of the IVC scheme from the kernel itself, generalizing ClientIvc for use on an arbitrary set of circuits, and improving the interface in all cases. As expected, the benchmark does not change in any substantial way. (To the extent it does change, its because the gate counts in the circuits have changed slightly due to this new structure). ``` -------------------------------------------------------------------------------- Benchmark Time CPU Iterations UserCounters... -------------------------------------------------------------------------------- ClientIVCBench/Full/6 17908 ms 12848 ms 1 ``` --- .../client_ivc_bench/client_ivc.bench.cpp | 146 +++++----- .../barretenberg/client_ivc/client_ivc.cpp | 133 +++++---- .../barretenberg/client_ivc/client_ivc.hpp | 32 +-- .../client_ivc/client_ivc.test.cpp | 253 +++++++++++------- .../client_ivc/mock_kernel_pinning.test.cpp | 43 +-- .../src/barretenberg/goblin/mock_circuits.hpp | 63 ++--- .../protogalaxy/folding_result.hpp | 2 +- .../protogalaxy/protogalaxy_prover.cpp | 2 +- .../protogalaxy_recursive_verifier.test.cpp | 10 +- 9 files changed, 342 insertions(+), 342 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/benchmark/client_ivc_bench/client_ivc.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/client_ivc_bench/client_ivc.bench.cpp index ded7acc08f1..80d3e326144 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/client_ivc_bench/client_ivc.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/client_ivc_bench/client_ivc.bench.cpp @@ -20,7 +20,6 @@ namespace { class ClientIVCBench : public benchmark::Fixture { public: using Builder = GoblinUltraCircuitBuilder; - using VerifierFoldData = GoblinMockCircuits::VerifierFoldData; // Number of function circuits to accumulate(based on Zacs target numbers) static constexpr size_t NUM_ITERATIONS_MEDIUM_COMPLEXITY = 6; @@ -31,6 +30,36 @@ class ClientIVCBench : public benchmark::Fixture { bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); } + /** + * @brief Compute verification key for each circuit in the IVC based on the number of desired function circuits + * @details Assumes the following circuit ordering: one initial function circuit followed by pairs of {function, + * kernel} until the desired number of function circuits has been reached. + * + * @param ivc + * @param num_function_circuits + */ + static auto precompute_verification_keys(ClientIVC& ivc, const size_t num_function_circuits) + { + // Populate the set of mock function and kernel circuits to be accumulated in the IVC + std::vector circuits; + Builder function_circuit{ ivc.goblin.op_queue }; + GoblinMockCircuits::construct_mock_function_circuit(function_circuit); + circuits.emplace_back(function_circuit); + + for (size_t idx = 1; idx < num_function_circuits; ++idx) { + Builder function_circuit{ ivc.goblin.op_queue }; + GoblinMockCircuits::construct_mock_function_circuit(function_circuit); + circuits.emplace_back(function_circuit); + + Builder kernel_circuit{ ivc.goblin.op_queue }; + GoblinMockCircuits::construct_mock_folding_kernel(kernel_circuit); + circuits.emplace_back(kernel_circuit); + } + + // Compute and return the verfication keys corresponding to this set of circuits + return ivc.precompute_folding_verification_keys(circuits); + } + /** * @brief Perform a specified number of function circuit accumulation rounds * @details Each round "accumulates" a mock function circuit and a mock kernel circuit. Each round thus consists of @@ -40,9 +69,13 @@ class ClientIVCBench : public benchmark::Fixture { * we set the size of the function circuit to be 2^17. The first one should be 2^19 but we can't currently support * folding circuits of unequal size. * + * @param NUM_CIRCUITS Number of function circuits to accumulate */ - static void perform_ivc_accumulation_rounds(State& state, ClientIVC& ivc) + static void perform_ivc_accumulation_rounds(size_t NUM_CIRCUITS, ClientIVC& ivc, auto& precomputed_vks) { + size_t TOTAL_NUM_CIRCUITS = NUM_CIRCUITS * 2 - 1; // need one less kernel than number of function circuits + ASSERT(precomputed_vks.size() == TOTAL_NUM_CIRCUITS); // ensure presence of a precomputed VK for each circuit + const size_t size_hint = 1 << 17; // Size hint for reserving wires/selector vector memory in builders std::vector initial_function_circuits(2); @@ -57,31 +90,21 @@ class ClientIVCBench : public benchmark::Fixture { // Prepend queue to the first circuit initial_function_circuits[0].op_queue->prepend_previous_queue(*ivc.goblin.op_queue); // Initialize ivc - ivc.initialize(initial_function_circuits[0]); + ivc.accumulate(initial_function_circuits[0], precomputed_vks[0]); // Retrieve the queue std::swap(*ivc.goblin.op_queue, *initial_function_circuits[0].op_queue); // Prepend queue to the second circuit initial_function_circuits[1].op_queue->prepend_previous_queue(*ivc.goblin.op_queue); // Accumulate another function circuit - auto function_fold_proof = ivc.accumulate(initial_function_circuits[1]); + ivc.accumulate(initial_function_circuits[1], precomputed_vks[1]); // Retrieve the queue std::swap(*ivc.goblin.op_queue, *initial_function_circuits[1].op_queue); - VerifierFoldData function_fold_output = { function_fold_proof, ivc.vks.func_vk }; // Free memory initial_function_circuits.clear(); - auto NUM_CIRCUITS = static_cast(state.range(0)); - // Subtract two to account for the "initialization" round above i.e. we have already folded two function - // circuits - NUM_CIRCUITS -= 2; - - // The accumulator for kernel uses the function accumulation verification key - auto kernel_verifier_accumulator = std::make_shared(ivc.vks.first_func_vk); - - VerifierFoldData kernel_fold_output; - for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { + for (size_t circuit_idx = 2; circuit_idx < TOTAL_NUM_CIRCUITS - 1; circuit_idx += 2) { Builder kernel_circuit{ size_hint, ivc.goblin.op_queue }; Builder function_circuit{ size_hint }; // Construct function and kernel circuits in parallel @@ -90,18 +113,7 @@ class ClientIVCBench : public benchmark::Fixture { parallel_for(2, [&](size_t workload_idx) { // workload index is 0 for kernel and 1 for function if (workload_idx == 0) { - if (circuit_idx == 0) { - - // Create the first folding kernel which only verifies the accumulation of a - // function circuit - kernel_verifier_accumulator = GoblinMockCircuits::construct_mock_folding_kernel( - kernel_circuit, function_fold_output, {}, kernel_verifier_accumulator); - } else { - // Create kernel circuit containing the recursive folding verification of a function circuit - // and a kernel circuit - kernel_verifier_accumulator = GoblinMockCircuits::construct_mock_folding_kernel( - kernel_circuit, function_fold_output, kernel_fold_output, kernel_verifier_accumulator); - } + GoblinMockCircuits::construct_mock_folding_kernel(kernel_circuit); } else { GoblinMockCircuits::construct_mock_function_circuit(function_circuit); } @@ -110,48 +122,25 @@ class ClientIVCBench : public benchmark::Fixture { // No need to prepend queue, it's the same after last swap // Accumulate kernel circuit - auto kernel_fold_proof = ivc.accumulate(kernel_circuit); - - // First iteration and the following ones differ - if (circuit_idx == 0) { - kernel_fold_output = { kernel_fold_proof, ivc.vks.first_kernel_vk }; - } else { - kernel_fold_output = { kernel_fold_proof, ivc.vks.kernel_vk }; - } + ivc.accumulate(kernel_circuit, precomputed_vks[circuit_idx]); // Prepend queue to function circuit function_circuit.op_queue->prepend_previous_queue(*ivc.goblin.op_queue); // Accumulate function circuit - auto function_fold_proof = ivc.accumulate(function_circuit); - function_fold_output = { function_fold_proof, ivc.vks.func_vk }; + ivc.accumulate(function_circuit, precomputed_vks[circuit_idx + 1]); // Retrieve queue std::swap(*ivc.goblin.op_queue, *function_circuit.op_queue); } - // If we haven't entered the cycle, the kernel proof accumulates just function proofs - if (NUM_CIRCUITS == 0) { - // Create and accumulate the first folding kernel which only verifies the accumulation of a function circuit - Builder kernel_circuit{ size_hint, ivc.goblin.op_queue }; - auto kernel_verifier_accumulator = std::make_shared(ivc.vks.first_func_vk); - { - BB_OP_COUNT_TIME_NAME("construct_circuits"); - kernel_verifier_accumulator = GoblinMockCircuits::construct_mock_folding_kernel( - kernel_circuit, function_fold_output, {}, kernel_verifier_accumulator); - } - auto kernel_fold_proof = ivc.accumulate(kernel_circuit); - kernel_fold_output = { kernel_fold_proof, ivc.vks.first_kernel_vk }; - } else { - Builder kernel_circuit{ size_hint, ivc.goblin.op_queue }; - { - BB_OP_COUNT_TIME_NAME("construct_circuits"); - kernel_verifier_accumulator = GoblinMockCircuits::construct_mock_folding_kernel( - kernel_circuit, function_fold_output, kernel_fold_output, kernel_verifier_accumulator); - } - auto kernel_fold_proof = ivc.accumulate(kernel_circuit); - kernel_fold_output = { kernel_fold_proof, ivc.vks.kernel_vk }; + // Final kernel + Builder kernel_circuit{ size_hint, ivc.goblin.op_queue }; + { + BB_OP_COUNT_TIME_NAME("construct_circuits"); + GoblinMockCircuits::construct_mock_folding_kernel(kernel_circuit); } + ivc.accumulate(kernel_circuit, precomputed_vks.back()); } }; @@ -162,11 +151,14 @@ class ClientIVCBench : public benchmark::Fixture { BENCHMARK_DEFINE_F(ClientIVCBench, Full)(benchmark::State& state) { ClientIVC ivc; - ivc.precompute_folding_verification_keys(); + + auto num_circuits = static_cast(state.range(0)); + auto precomputed_vks = precompute_verification_keys(ivc, num_circuits); + for (auto _ : state) { BB_REPORT_OP_COUNT_IN_BENCH(state); // Perform a specified number of iterations of function/kernel accumulation - perform_ivc_accumulation_rounds(state, ivc); + perform_ivc_accumulation_rounds(num_circuits, ivc, precomputed_vks); // Construct IVC scheme proof (fold, decider, merge, eccvm, translator) ivc.prove(); @@ -181,11 +173,14 @@ BENCHMARK_DEFINE_F(ClientIVCBench, FullStructured)(benchmark::State& state) { ClientIVC ivc; ivc.structured_flag = true; - ivc.precompute_folding_verification_keys(); + + auto num_circuits = static_cast(state.range(0)); + auto precomputed_vks = precompute_verification_keys(ivc, num_circuits); + for (auto _ : state) { BB_REPORT_OP_COUNT_IN_BENCH(state); // Perform a specified number of iterations of function/kernel accumulation - perform_ivc_accumulation_rounds(state, ivc); + perform_ivc_accumulation_rounds(num_circuits, ivc, precomputed_vks); // Construct IVC scheme proof (fold, decider, merge, eccvm, translator) ivc.prove(); @@ -199,11 +194,14 @@ BENCHMARK_DEFINE_F(ClientIVCBench, FullStructured)(benchmark::State& state) BENCHMARK_DEFINE_F(ClientIVCBench, Accumulate)(benchmark::State& state) { ClientIVC ivc; - ivc.precompute_folding_verification_keys(); + + auto num_circuits = static_cast(state.range(0)); + auto precomputed_vks = precompute_verification_keys(ivc, num_circuits); + // Perform a specified number of iterations of function/kernel accumulation for (auto _ : state) { BB_REPORT_OP_COUNT_IN_BENCH(state); - perform_ivc_accumulation_rounds(state, ivc); + perform_ivc_accumulation_rounds(num_circuits, ivc, precomputed_vks); } } @@ -214,8 +212,12 @@ BENCHMARK_DEFINE_F(ClientIVCBench, Accumulate)(benchmark::State& state) BENCHMARK_DEFINE_F(ClientIVCBench, Decide)(benchmark::State& state) { ClientIVC ivc; + + auto num_circuits = static_cast(state.range(0)); + auto precomputed_vks = precompute_verification_keys(ivc, num_circuits); + // Perform a specified number of iterations of function/kernel accumulation - perform_ivc_accumulation_rounds(state, ivc); + perform_ivc_accumulation_rounds(num_circuits, ivc, precomputed_vks); // Construct eccvm proof, measure only translator proof construction for (auto _ : state) { @@ -232,8 +234,11 @@ BENCHMARK_DEFINE_F(ClientIVCBench, ECCVM)(benchmark::State& state) { ClientIVC ivc; + auto num_circuits = static_cast(state.range(0)); + auto precomputed_vks = precompute_verification_keys(ivc, num_circuits); + // Perform a specified number of iterations of function/kernel accumulation - perform_ivc_accumulation_rounds(state, ivc); + perform_ivc_accumulation_rounds(num_circuits, ivc, precomputed_vks); // Construct and measure eccvm only for (auto _ : state) { @@ -249,10 +254,15 @@ BENCHMARK_DEFINE_F(ClientIVCBench, ECCVM)(benchmark::State& state) BENCHMARK_DEFINE_F(ClientIVCBench, Translator)(benchmark::State& state) { ClientIVC ivc; - ivc.precompute_folding_verification_keys(); + auto num_circuits = static_cast(state.range(0)); + auto precomputed_vks = precompute_verification_keys(ivc, num_circuits); + + // Perform a specified number of iterations of function/kernel accumulation + perform_ivc_accumulation_rounds(num_circuits, ivc, precomputed_vks); + BB_REPORT_OP_COUNT_IN_BENCH(state); // Perform a specified number of iterations of function/kernel accumulation - perform_ivc_accumulation_rounds(state, ivc); + perform_ivc_accumulation_rounds(num_circuits, ivc, precomputed_vks); // Construct eccvm proof, measure only translator proof construction ivc.goblin.prove_eccvm(); diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp index 758a313658b..71b2ef0f9dd 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp @@ -2,32 +2,48 @@ namespace bb { -/** - * @brief Initialize the IVC with a first circuit - * @details Initializes the accumulator and performs the initial goblin merge - * - * @param circuit - */ -void ClientIVC::initialize(ClientCircuit& circuit) -{ - goblin.merge(circuit); // Construct new merge proof - prover_fold_output.accumulator = std::make_shared(circuit, structured_flag); -} - /** * @brief Accumulate a circuit into the IVC scheme - * @details Performs goblin merge, generates circuit instance, folds into accumulator and constructs a folding proof + * @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. If a previous fold proof exists, a recursive folding + * verification is appended to the provided circuit prior to its accumulation. Similarly, if a merge proof exists, a + * recursive merge verifier is appended. * * @param circuit Circuit to be accumulated/folded - * @return FoldProof + * @param precomputed_vk Optional precomputed VK (otherwise will be computed herein) */ -ClientIVC::FoldProof ClientIVC::accumulate(ClientCircuit& circuit) +void ClientIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr& precomputed_vk) { - goblin.merge(circuit); // Add recursive merge verifier and construct new merge proof + // If a previous fold proof exists, add a recursive folding verification to the circuit + if (!fold_output.proof.empty()) { + BB_OP_COUNT_TIME_NAME("construct_circuits"); + FoldingRecursiveVerifier verifier{ &circuit, verifier_accumulator, { instance_vk } }; + auto verifier_accum = verifier.verify_folding_proof(fold_output.proof); + verifier_accumulator = std::make_shared(verifier_accum->get_value()); + } + + // Construct a merge proof (and add a recursive merge verifier to the circuit if a previous merge proof exists) + goblin.merge(circuit); + + // Construct the prover instance for circuit prover_instance = std::make_shared(circuit, structured_flag); - FoldingProver folding_prover({ prover_fold_output.accumulator, prover_instance }); - prover_fold_output = folding_prover.fold_instances(); - return prover_fold_output.folding_data; + + // Set the instance verification key from precomputed if available, else compute it + if (precomputed_vk) { + instance_vk = precomputed_vk; + } else { + instance_vk = std::make_shared(prover_instance->proving_key); + } + + // If the IVC is uninitialized, simply initialize the prover and verifier accumulator instances + 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(); + } } /** @@ -37,7 +53,7 @@ ClientIVC::FoldProof ClientIVC::accumulate(ClientCircuit& circuit) */ ClientIVC::Proof ClientIVC::prove() { - return { prover_fold_output.folding_data, decider_prove(), goblin.prove() }; + return { fold_output.proof, decider_prove(), goblin.prove() }; } /** @@ -46,7 +62,7 @@ ClientIVC::Proof ClientIVC::prove() * @param proof * @return bool */ -bool ClientIVC::verify(Proof& proof, const std::vector& verifier_instances) +bool ClientIVC::verify(Proof& proof, const std::vector>& verifier_instances) { // Goblin verification (merge, eccvm, translator) bool goblin_verified = goblin.verify(proof.goblin_proof); @@ -67,63 +83,38 @@ bool ClientIVC::verify(Proof& proof, const std::vector& ver */ HonkProof ClientIVC::decider_prove() const { - GoblinUltraDeciderProver decider_prover(prover_fold_output.accumulator); + GoblinUltraDeciderProver decider_prover(fold_output.accumulator); return decider_prover.construct_proof(); } /** - * @brief Precompute the array of verification keys by simulating folding. There will be 4 different verification keys: - * initial function verification key (without recursive merge verifier), subsequent function verification key (with - * recursive merge verifier), initial kernel verification key (with recursive merge verifier appended, no previous - * kernel to fold), "full" kernel verification key( two recursive folding verifiers and merge verifier). + * @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. * - * TODO(https://github.com/AztecProtocol/barretenberg/issues/904): This function should ultimately be moved outside of - * this class since it's used only for testing and benchmarking purposes and it requires us to clear state afterwards. - * (e.g. in the Goblin object) + * @param circuits A copy of the circuits to be accumulated (passing by reference would alter the original circuits) + * @return std::vector> */ -void ClientIVC::precompute_folding_verification_keys() +std::vector> ClientIVC::precompute_folding_verification_keys( + std::vector circuits) { - using VerifierInstance = VerifierInstance_; - using VerificationKey = Flavor::VerificationKey; - - ClientCircuit initial_function_circuit{ goblin.op_queue }; - GoblinMockCircuits::construct_mock_function_circuit(initial_function_circuit); - - // Initialise both the first prover and verifier accumulator from the inital function circuit - initialize(initial_function_circuit); - vks.first_func_vk = std::make_shared(prover_fold_output.accumulator->proving_key); - auto initial_verifier_acc = std::make_shared(vks.first_func_vk); - - // Accumulate the next function circuit - ClientCircuit function_circuit{ goblin.op_queue }; - GoblinMockCircuits::construct_mock_function_circuit(function_circuit); - auto function_fold_proof = accumulate(function_circuit); - - // Create its verification key (we have called accumulate so it includes the recursive merge verifier) - vks.func_vk = std::make_shared(prover_instance->proving_key); - - // Create the initial kernel iteration and precompute its verification key - ClientCircuit kernel_circuit{ goblin.op_queue }; - auto kernel_acc = GoblinMockCircuits::construct_mock_folding_kernel( - kernel_circuit, { function_fold_proof, vks.func_vk }, {}, initial_verifier_acc); - auto kernel_fold_proof = accumulate(kernel_circuit); - vks.first_kernel_vk = std::make_shared(prover_instance->proving_key); - - // Create another mock function circuit to run the full kernel - function_circuit = ClientCircuit{ goblin.op_queue }; - GoblinMockCircuits::construct_mock_function_circuit(function_circuit); - function_fold_proof = accumulate(function_circuit); - - // Create the full kernel circuit and compute verification key - kernel_circuit = GoblinUltraCircuitBuilder{ goblin.op_queue }; - kernel_acc = GoblinMockCircuits::construct_mock_folding_kernel( - kernel_circuit, { function_fold_proof, vks.func_vk }, { kernel_fold_proof, vks.first_kernel_vk }, kernel_acc); - kernel_fold_proof = accumulate(kernel_circuit); - - vks.kernel_vk = std::make_shared(prover_instance->proving_key); - - // Clean the Goblin state (reinitialise op_queue with mocking and clear merge proofs) - goblin = Goblin(); + 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 structured trace flag as is + bool structured = structured_flag; + *this = ClientIVC(); + this->structured_flag = structured; + + return vkeys; } } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp index eafc0f72aef..d44b02f6740 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp @@ -23,8 +23,6 @@ class ClientIVC { using VerificationKey = Flavor::VerificationKey; using FF = Flavor::FF; using FoldProof = std::vector; - using ProverAccumulator = std::shared_ptr>; - using VerifierAccumulator = std::shared_ptr>; using ProverInstance = ProverInstance_; using VerifierInstance = VerifierInstance_; using ClientCircuit = GoblinUltraCircuitBuilder; // can only be GoblinUltra @@ -35,6 +33,11 @@ class ClientIVC { using VerifierInstances = VerifierInstances_; using FoldingVerifier = ProtoGalaxyVerifier_; + using GURecursiveFlavor = GoblinUltraRecursiveFlavor_; + using RecursiveVerifierInstances = bb::stdlib::recursion::honk::RecursiveVerifierInstances_; + using FoldingRecursiveVerifier = + bb::stdlib::recursion::honk::ProtoGalaxyRecursiveVerifier_; + // A full proof for the IVC scheme struct Proof { FoldProof folding_proof; // final fold proof @@ -57,13 +60,6 @@ class ClientIVC { } }; - struct PrecomputedVerificationKeys { - std::shared_ptr first_func_vk; - std::shared_ptr func_vk; - std::shared_ptr first_kernel_vk; - std::shared_ptr kernel_vk; - }; - private: using ProverFoldOutput = FoldingResult; // Note: We need to save the last instance that was folded in order to compute its verification key, this will not @@ -71,28 +67,28 @@ class ClientIVC { public: Goblin goblin; - ProverFoldOutput prover_fold_output; - ProverAccumulator prover_accumulator; - PrecomputedVerificationKeys vks; + ProverFoldOutput fold_output; + std::shared_ptr prover_accumulator; + std::shared_ptr verifier_accumulator; // Note: We need to save the last instance that was folded in order to compute its verification key, this will not // be needed in the real IVC as they are provided as inputs std::shared_ptr prover_instance; + std::shared_ptr instance_vk; // A flag indicating whether or not to construct a structured trace in the ProverInstance bool structured_flag = false; - void initialize(ClientCircuit& circuit); + // A flag indicating whether the IVC has been initialized with an initial instance + bool initialized = false; - FoldProof accumulate(ClientCircuit& circuit); + void accumulate(ClientCircuit& circuit, const std::shared_ptr& precomputed_vk = nullptr); Proof prove(); - bool verify(Proof& proof, const std::vector& verifier_instances); + bool verify(Proof& proof, const std::vector>& verifier_instances); HonkProof decider_prove() const; - void decider_prove_and_verify(const VerifierAccumulator&) const; - - void precompute_folding_verification_keys(); + std::vector> precompute_folding_verification_keys(std::vector); }; } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp index 0dd189112b8..6a1676c4883 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp @@ -17,18 +17,11 @@ class ClientIVCTests : public ::testing::Test { using Flavor = ClientIVC::Flavor; using FF = typename Flavor::FF; + using VerificationKey = Flavor::VerificationKey; using Builder = ClientIVC::ClientCircuit; - using ProverAccumulator = ClientIVC::ProverAccumulator; - using VerifierAccumulator = ClientIVC::VerifierAccumulator; + using ProverInstance = ClientIVC::ProverInstance; using VerifierInstance = ClientIVC::VerifierInstance; using FoldProof = ClientIVC::FoldProof; - using VerifierFoldData = GoblinMockCircuits::VerifierFoldData; - using GURecursiveFlavor = GoblinUltraRecursiveFlavor_; - using RecursiveVerifierInstance = ::bb::stdlib::recursion::honk::RecursiveVerifierInstance_; - using RecursiveVerifierAccumulator = std::shared_ptr; - using RecursiveVerifierInstances = ::bb::stdlib::recursion::honk::RecursiveVerifierInstances_; - using FoldingRecursiveVerifier = - bb::stdlib::recursion::honk::ProtoGalaxyRecursiveVerifier_; using DeciderProver = ClientIVC::DeciderProver; using DeciderVerifier = ClientIVC::DeciderVerifier; using ProverInstances = ProverInstances_; @@ -36,6 +29,20 @@ class ClientIVCTests : public ::testing::Test { using VerifierInstances = VerifierInstances_; using FoldingVerifier = ProtoGalaxyVerifier_; + /** + * @brief Prove and verify the IVC scheme + * @details Constructs four proofs: merge, eccvm, translator, decider; Verifies these four plus the final folding + * proof constructed on the last accumulation round + * + */ + static bool prove_and_verify(ClientIVC& ivc) + { + auto proof = ivc.prove(); + + auto verifier_inst = std::make_shared(ivc.instance_vk); + return ivc.verify(proof, { ivc.verifier_accumulator, verifier_inst }); + } + /** * @brief Construct mock circuit with arithmetic gates and goblin ops * @details Currently default sized to 2^16 to match kernel. (Note: dummy op gates added to avoid non-zero @@ -55,111 +62,153 @@ class ClientIVCTests : public ::testing::Test { MockCircuits::construct_goblin_ecc_op_circuit(circuit); return circuit; } +}; - /** - * @brief Construct mock kernel consisting of two recursive folding verifiers to verify the folding of the previous - * function circuit and kernel circuit. - * - * @param builder - * @param func_accum contains the folding proof for the function circuit and the corresponsing function - * verifier instance - * @param kernel_accum contains the folding proof for the kernel circuit and the corresponding kernel verifier - * instance - * @returns the updated verifier accumulator - */ - static VerifierAccumulator construct_mock_folding_kernel(Builder& builder, - VerifierFoldData& func_accum, - VerifierFoldData& kernel_accum, - VerifierAccumulator& prev_kernel_accum) - { +/** + * @brief A simple-as-possible test demonstrating IVC for two mock circuits + * + */ +TEST_F(ClientIVCTests, Basic) +{ + ClientIVC ivc; + + // Initialize the IVC with an arbitrary circuit + Builder circuit_0 = create_mock_circuit(ivc); + ivc.accumulate(circuit_0); - FoldingRecursiveVerifier verifier_1{ &builder, prev_kernel_accum, { func_accum.inst_vk } }; - auto fctn_verifier_accum = verifier_1.verify_folding_proof(func_accum.fold_proof); - auto native_acc = std::make_shared(fctn_verifier_accum->get_value()); - FoldingRecursiveVerifier verifier_2{ &builder, native_acc, { kernel_accum.inst_vk } }; - auto kernel_verifier_accum = verifier_2.verify_folding_proof(kernel_accum.fold_proof); - return std::make_shared(kernel_verifier_accum->get_value()); + // Create another circuit and accumulate + Builder circuit_1 = create_mock_circuit(ivc); + ivc.accumulate(circuit_1); + + EXPECT_TRUE(prove_and_verify(ivc)); +}; + +/** + * @brief Check that the IVC fails to verify if an intermediate fold proof is invalid + * + */ +TEST_F(ClientIVCTests, BasicFailure) +{ + ClientIVC ivc; + + // Initialize the IVC with an arbitrary circuit + Builder circuit_0 = create_mock_circuit(ivc); + ivc.accumulate(circuit_0); + + // Create another circuit and accumulate + Builder circuit_1 = create_mock_circuit(ivc); + ivc.accumulate(circuit_1); + + // Tamper with the fold proof just created in the last accumulation step + for (auto& val : ivc.fold_output.proof) { + if (val > 0) { // tamper by finding the first non-zero value and incrementing it by 1 + val += 1; + break; + } } - /** - * @brief Perform native fold verification and run decider prover/verifier - * - */ - static VerifierAccumulator update_accumulator_and_decide_native( - const ProverAccumulator& prover_accumulator, - const FoldProof& fold_proof, - const VerifierAccumulator& prev_verifier_accumulator, - const std::shared_ptr& verifier_inst_vk) - { - // Verify fold proof - auto new_verifier_inst = std::make_shared(verifier_inst_vk); - FoldingVerifier folding_verifier({ prev_verifier_accumulator, new_verifier_inst }); - auto verifier_accumulator = folding_verifier.verify_folding_proof(fold_proof); - - // Run decider - DeciderProver decider_prover(prover_accumulator); - DeciderVerifier decider_verifier(verifier_accumulator); - auto decider_proof = decider_prover.construct_proof(); - bool decision = decider_verifier.verify_proof(decider_proof); - EXPECT_TRUE(decision); - - return verifier_accumulator; + // Accumulate another circuit; this involves recursive folding verification of the bad proof + Builder circuit_2 = create_mock_circuit(ivc); + ivc.accumulate(circuit_2); + + // The bad fold proof should result in an invalid witness in the final circuit and the IVC should fail to verify + EXPECT_FALSE(prove_and_verify(ivc)); +}; + +/** + * @brief Prove and verify accumulation of an arbitrary set of circuits + * + */ +TEST_F(ClientIVCTests, BasicLarge) +{ + ClientIVC ivc; + + // Construct a set of arbitrary circuits + size_t NUM_CIRCUITS = 5; + 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); } + + EXPECT_TRUE(prove_and_verify(ivc)); }; /** - * @brief A full Goblin test using PG that mimicks the basic aztec client architecture + * @brief Using a structured trace allows for the accumulation of circuits of varying size * */ -// TODO fix with https://github.com/AztecProtocol/barretenberg/issues/930 -// intermittent failures, presumably due to uninitialized memory -TEST_F(ClientIVCTests, DISABLED_Full) +TEST_F(ClientIVCTests, BasicStructured) { - using VerificationKey = Flavor::VerificationKey; + ClientIVC ivc; + ivc.structured_flag = true; + + // 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=*/10); + Builder circuit_2 = create_mock_circuit(ivc, /*log2_num_gates=*/15); + + // The circuits can be accumulated as normal due to the structured trace + ivc.accumulate(circuit_0); + ivc.accumulate(circuit_1); + ivc.accumulate(circuit_2); + + EXPECT_TRUE(prove_and_verify(ivc)); +}; +/** + * @brief Prove and verify accumulation of an arbitrary set of circuits using precomputed verification keys + * + */ +TEST_F(ClientIVCTests, PrecomputedVerificationKeys) +{ ClientIVC ivc; - // Initialize IVC with function circuit - Builder function_circuit = create_mock_circuit(ivc); - ivc.initialize(function_circuit); - - auto function_vk = std::make_shared(ivc.prover_fold_output.accumulator->proving_key); - auto foo_verifier_instance = std::make_shared(function_vk); - // Accumulate kernel circuit (first kernel mocked as simple circuit since no folding proofs yet) - Builder kernel_circuit = create_mock_circuit(ivc); - FoldProof kernel_fold_proof = ivc.accumulate(kernel_circuit); - // This will have a different verification key because we added the recursive merge verification to the circuit - auto function_vk_with_merge = std::make_shared(ivc.prover_instance->proving_key); - auto kernel_vk = function_vk_with_merge; - auto intermediary_acc = update_accumulator_and_decide_native( - ivc.prover_fold_output.accumulator, kernel_fold_proof, foo_verifier_instance, kernel_vk); - - VerifierFoldData kernel_fold_output = { kernel_fold_proof, function_vk_with_merge }; - size_t NUM_CIRCUITS = 1; - for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { - // Accumulate function circuit - Builder function_circuit = create_mock_circuit(ivc); - FoldProof function_fold_proof = ivc.accumulate(function_circuit); - - intermediary_acc = update_accumulator_and_decide_native( - ivc.prover_fold_output.accumulator, function_fold_proof, intermediary_acc, function_vk_with_merge); - - VerifierFoldData function_fold_output = { function_fold_proof, function_vk_with_merge }; - // Accumulate kernel circuit - Builder kernel_circuit{ ivc.goblin.op_queue }; - foo_verifier_instance = construct_mock_folding_kernel( - kernel_circuit, kernel_fold_output, function_fold_output, foo_verifier_instance); - FoldProof kernel_fold_proof = ivc.accumulate(kernel_circuit); - kernel_vk = std::make_shared(ivc.prover_instance->proving_key); - - intermediary_acc = update_accumulator_and_decide_native( - ivc.prover_fold_output.accumulator, kernel_fold_proof, intermediary_acc, kernel_vk); - - VerifierFoldData kernel_fold_output = { kernel_fold_proof, kernel_vk }; + + // Construct a set of arbitrary circuits + size_t NUM_CIRCUITS = 3; + 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); + + // Accumulate each circuit using the precomputed VKs + for (auto [circuit, precomputed_vk] : zip_view(circuits, precomputed_vkeys)) { + ivc.accumulate(circuit, precomputed_vk); + } + + EXPECT_TRUE(prove_and_verify(ivc)); +}; + +/** + * @brief Perform accumulation with a structured trace and precomputed verification keys + * + */ +TEST_F(ClientIVCTests, StructuredPrecomputedVKs) +{ + ClientIVC ivc; + ivc.structured_flag = true; + + // Construct a set of arbitrary circuits + size_t NUM_CIRCUITS = 3; + std::vector circuits; + for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { + circuits.emplace_back(create_mock_circuit(ivc)); } - // Constuct four proofs: merge, eccvm, translator, decider - auto proof = ivc.prove(); - auto inst = std::make_shared(kernel_vk); - // Verify all four proofs - EXPECT_TRUE(ivc.verify(proof, { foo_verifier_instance, inst })); -}; \ No newline at end of file + // Precompute the (structured) verification keys that will be needed for the IVC + auto precomputed_vkeys = ivc.precompute_folding_verification_keys(circuits); + + // Accumulate each circuit + for (auto [circuit, precomputed_vk] : zip_view(circuits, precomputed_vkeys)) { + ivc.accumulate(circuit, precomputed_vk); + } + + EXPECT_TRUE(prove_and_verify(ivc)); +}; diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/mock_kernel_pinning.test.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/mock_kernel_pinning.test.cpp index d4d06758e2f..bb11e2cc670 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/mock_kernel_pinning.test.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/mock_kernel_pinning.test.cpp @@ -12,6 +12,9 @@ using namespace bb; * */ class MockKernelTest : public ::testing::Test { + public: + using Builder = GoblinUltraCircuitBuilder; + protected: static void SetUpTestSuite() { srs::init_crs_factory("../srs_db/ignition"); } }; @@ -19,38 +22,20 @@ class MockKernelTest : public ::testing::Test { TEST_F(MockKernelTest, PinFoldingKernelSizes) { ClientIVC ivc; - ivc.precompute_folding_verification_keys(); - // Accumulate three circuits to generate two folding proofs for input to folding kernel - GoblinUltraCircuitBuilder circuit_1{ ivc.goblin.op_queue }; - GoblinMockCircuits::construct_mock_function_circuit(circuit_1); - ivc.initialize(circuit_1); - auto kernel_acc = std::make_shared(ivc.vks.first_func_vk); - kernel_acc->verification_key = ivc.vks.first_func_vk; - EXPECT_EQ(ivc.prover_instance->proving_key.log_circuit_size, 17); - GoblinUltraCircuitBuilder circuit_2{ ivc.goblin.op_queue }; - GoblinMockCircuits::construct_mock_function_circuit(circuit_2); - auto func_fold_proof = ivc.accumulate(circuit_2); - EXPECT_EQ(ivc.prover_instance->proving_key.log_circuit_size, 17); + // Construct two function circuits and a kernel circuit + Builder circuit_1{ ivc.goblin.op_queue }; + Builder circuit_2{ ivc.goblin.op_queue }; + Builder kernel_circuit{ ivc.goblin.op_queue }; - // Construct kernel circuit - GoblinUltraCircuitBuilder kernel_circuit{ ivc.goblin.op_queue }; - kernel_acc = GoblinMockCircuits::construct_mock_folding_kernel( - kernel_circuit, { func_fold_proof, ivc.vks.func_vk }, {}, kernel_acc); + GoblinMockCircuits::construct_mock_function_circuit(circuit_1); + GoblinMockCircuits::construct_mock_function_circuit(circuit_2); + GoblinMockCircuits::construct_mock_folding_kernel(kernel_circuit); - auto kernel_fold_proof = ivc.accumulate(kernel_circuit); - EXPECT_EQ(ivc.prover_instance->proving_key.log_circuit_size, 17); + // Accumulate all three; The kernel will contain a single recursive folding verifier + ivc.accumulate(circuit_1); + ivc.accumulate(circuit_2); + ivc.accumulate(kernel_circuit); - GoblinUltraCircuitBuilder circuit_3{ ivc.goblin.op_queue }; - GoblinMockCircuits::construct_mock_function_circuit(circuit_3); - func_fold_proof = ivc.accumulate(circuit_3); EXPECT_EQ(ivc.prover_instance->proving_key.log_circuit_size, 17); - - kernel_circuit = GoblinUltraCircuitBuilder{ ivc.goblin.op_queue }; - kernel_acc = GoblinMockCircuits::construct_mock_folding_kernel(kernel_circuit, - { kernel_fold_proof, ivc.vks.first_kernel_vk }, - { func_fold_proof, ivc.vks.func_vk }, - kernel_acc); - auto instance = std::make_shared(kernel_circuit); - EXPECT_EQ(instance->proving_key.log_circuit_size, 17); } \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp index 46586ff58da..a3234dbc984 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp @@ -41,15 +41,6 @@ class GoblinMockCircuits { std::shared_ptr verification_key; }; - /** - * @brief Information required by the verifier to verify a folding round besides the previous accumulator. - */ - struct VerifierFoldData { - std::vector fold_proof; // folding proof - std::shared_ptr - inst_vk; // Verification key of the instance to be folded (note: this would be a vector if k > 1 ) - }; - /** * @brief Populate a builder with some arbitrary but nontrivial constraints * @details Although the details of the circuit constructed here are arbitrary, the intent is to mock something a @@ -64,12 +55,16 @@ class GoblinMockCircuits { // Determine number of times to execute the below operations that constitute the mock circuit logic. Note that // the circuit size does not scale linearly with number of iterations due to e.g. amortization of lookup costs const size_t NUM_ITERATIONS_LARGE = 12; // results in circuit size 2^19 (502238 gates) - const size_t NUM_ITERATIONS_MEDIUM = 3; // results in circuit size 2^17 (124843 gates) - const size_t NUM_ITERATIONS = large ? NUM_ITERATIONS_LARGE : NUM_ITERATIONS_MEDIUM; - stdlib::generate_sha256_test_circuit(builder, NUM_ITERATIONS); // min gates: ~39k - stdlib::generate_ecdsa_verification_test_circuit(builder, NUM_ITERATIONS); // min gates: ~41k - stdlib::generate_merkle_membership_test_circuit(builder, NUM_ITERATIONS); // min gates: ~29k + if (large) { + stdlib::generate_sha256_test_circuit(builder, NUM_ITERATIONS_LARGE); + stdlib::generate_ecdsa_verification_test_circuit(builder, NUM_ITERATIONS_LARGE); + stdlib::generate_merkle_membership_test_circuit(builder, NUM_ITERATIONS_LARGE); + } else { // Results in circuit size 2^17 when accumulated via ClientIvc + stdlib::generate_sha256_test_circuit(builder, 5); + stdlib::generate_ecdsa_verification_test_circuit(builder, 2); + stdlib::generate_merkle_membership_test_circuit(builder, 10); + } // TODO(https://github.com/AztecProtocol/barretenberg/issues/911): We require goblin ops to be added to the // function circuit because we cannot support zero commtiments. While the builder handles this at @@ -177,50 +172,24 @@ class GoblinMockCircuits { } /** - * @brief Construct a mock kernel circuit based on folding - * @details This circuit contains (1) some arbitrary operations representing general kernel logic, (2) recursive - * folding verification of a function circuit folding proof, and (3) recursive folding verification of a previous - * kernel circuit folding proof. The arbitrary kernel logic is structured to bring the final dyadic circuit size of - * the kernel to 2^17. + * @brief Construct a mock kernel circuit + * @details Construct an arbitrary circuit meant to represent the aztec private function execution kernel. Recursive + * folding verification is handled internally by ClientIvc, not in the kernel. * * @param builder * @param function_fold_proof * @param kernel_fold_proof */ - static std::shared_ptr construct_mock_folding_kernel( - GoblinUltraBuilder& builder, - const VerifierFoldData& func, - const VerifierFoldData& kernel, - std::shared_ptr& prev_kernel_accum) + static void construct_mock_folding_kernel(GoblinUltraBuilder& builder) { - using GURecursiveFlavor = GoblinUltraRecursiveFlavor_; - using RecursiveVerifierInstances = - bb::stdlib::recursion::honk::RecursiveVerifierInstances_; - using FoldingRecursiveVerifier = - bb::stdlib::recursion::honk::ProtoGalaxyRecursiveVerifier_; - // Add operations representing general kernel logic e.g. state updates. Note: these are structured to make - // the kernel "full" within the dyadic size 2^17 (130914 gates) - const size_t NUM_MERKLE_CHECKS = 25; - const size_t NUM_ECDSA_VERIFICATIONS = 1; + // the kernel "full" within the dyadic size 2^17 + const size_t NUM_MERKLE_CHECKS = 20; + const size_t NUM_ECDSA_VERIFICATIONS = 2; const size_t NUM_SHA_HASHES = 1; stdlib::generate_merkle_membership_test_circuit(builder, NUM_MERKLE_CHECKS); stdlib::generate_ecdsa_verification_test_circuit(builder, NUM_ECDSA_VERIFICATIONS); stdlib::generate_sha256_test_circuit(builder, NUM_SHA_HASHES); - - // Initial kernel iteration does not have a previous kernel to fold - if (kernel.fold_proof.empty()) { - FoldingRecursiveVerifier verifier_1{ &builder, prev_kernel_accum, { func.inst_vk } }; - auto fctn_verifier_accum = verifier_1.verify_folding_proof(func.fold_proof); - return std::make_shared(fctn_verifier_accum->get_value()); - } - - FoldingRecursiveVerifier verifier_2{ &builder, prev_kernel_accum, { kernel.inst_vk } }; - auto kernel_verifier_accum = verifier_2.verify_folding_proof(kernel.fold_proof); - auto native_acc = std::make_shared(kernel_verifier_accum->get_value()); - FoldingRecursiveVerifier verifier_1{ &builder, native_acc, { func.inst_vk } }; - auto fctn_verifier_accum = verifier_1.verify_folding_proof(func.fold_proof); - return std::make_shared(fctn_verifier_accum->get_value()); } /** diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/folding_result.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/folding_result.hpp index 4bf3eb88fff..61d6fdf3dd3 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/folding_result.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/folding_result.hpp @@ -13,6 +13,6 @@ template struct FoldingResult { public: std::shared_ptr> accumulator; // TODO(https://github.com/AztecProtocol/barretenberg/issues/656): turn folding data into a struct - std::vector folding_data; + std::vector proof; }; } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp index 6d67ab10576..e003418a524 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp @@ -192,7 +192,7 @@ template void ProtoGalaxyProver_::accum FF combiner_challenge = transcript->template get_challenge("combiner_quotient_challenge"); std::shared_ptr next_accumulator = compute_next_accumulator(instances, state.combiner_quotient, combiner_challenge, state.compressed_perturbator); - state.result.folding_data = transcript->proof_data; + state.result.proof = transcript->proof_data; state.result.accumulator = next_accumulator; }; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/protogalaxy_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/protogalaxy_recursive_verifier.test.cpp index 673d7234df9..786d75545fc 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/protogalaxy_recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/protogalaxy_recursive_verifier.test.cpp @@ -195,14 +195,14 @@ template class ProtoGalaxyRecursiveTests : public tes OuterBuilder folding_circuit; auto verifier = FoldingRecursiveVerifier(&folding_circuit, verifier_instance_1, { verifier_instance_2->verification_key }); - verifier.verify_folding_proof(folding_proof.folding_data); + verifier.verify_folding_proof(folding_proof.proof); info("Folding Recursive Verifier: num gates = ", folding_circuit.num_gates); EXPECT_EQ(folding_circuit.failed(), false) << folding_circuit.err(); // Perform native folding verification and ensure it returns the same result (either true or false) as // calling check_circuit on the recursive folding verifier InnerFoldingVerifier native_folding_verifier({ verifier_instance_1, verifier_instance_2 }); - native_folding_verifier.verify_folding_proof(folding_proof.folding_data); + native_folding_verifier.verify_folding_proof(folding_proof.proof); // Ensure that the underlying native and recursive folding verification algorithms agree by ensuring the // manifestsproduced by each agree. @@ -259,7 +259,7 @@ template class ProtoGalaxyRecursiveTests : public tes OuterBuilder folding_circuit; auto verifier = FoldingRecursiveVerifier(&folding_circuit, verifier_instance_1, { verifier_instance_2->verification_key }); - auto recursive_verifier_accumulator = verifier.verify_folding_proof(folding_proof.folding_data); + auto recursive_verifier_accumulator = verifier.verify_folding_proof(folding_proof.proof); auto native_verifier_acc = std::make_shared(recursive_verifier_accumulator->get_value()); info("Folding Recursive Verifier: num gates = ", folding_circuit.num_gates); @@ -269,7 +269,7 @@ template class ProtoGalaxyRecursiveTests : public tes // Perform native folding verification and ensure it returns the same result (either true or false) as // calling check_circuit on the recursive folding verifier InnerFoldingVerifier native_folding_verifier({ verifier_instance_1, verifier_instance_2 }); - auto verifier_accumulator = native_folding_verifier.verify_folding_proof(folding_proof.folding_data); + auto verifier_accumulator = native_folding_verifier.verify_folding_proof(folding_proof.proof); // Ensure that the underlying native and recursive folding verification algorithms agree by ensuring the // manifestsproduced by each agree. @@ -359,7 +359,7 @@ template class ProtoGalaxyRecursiveTests : public tes FoldingRecursiveVerifier verifier{ &folding_circuit, verifier_accumulator, { verifier_inst->verification_key } }; - auto recursive_verifier_acc = verifier.verify_folding_proof(folding_proof.folding_data); + auto recursive_verifier_acc = verifier.verify_folding_proof(folding_proof.proof); // Validate that the target sum between prover and verifier is now different EXPECT_FALSE(folding_proof.accumulator->target_sum == recursive_verifier_acc->target_sum.get_value()); }; From af9fea4bbafe1a41b09d9351a34a896db2c8ab7d Mon Sep 17 00:00:00 2001 From: Lucas Xia Date: Fri, 17 May 2024 18:39:32 -0400 Subject: [PATCH 3/9] feat: laying out a new recursion constraint for honk (#6489) Create new recursion constraint for honk, which is the same as the plonk recursion constraint, but with a default aggregation object. The default aggregation object work was moved from https://github.com/AztecProtocol/aztec-packages/pull/6087. This PR is mostly for setup for https://github.com/AztecProtocol/barretenberg/issues/933. --- barretenberg/acir_tests/reset_acir_tests.sh | 7 + .../dsl/acir_format/acir_format.cpp | 137 ++++++- .../dsl/acir_format/acir_format.hpp | 15 +- .../dsl/acir_format/acir_format.test.cpp | 6 + .../acir_format/bigint_constraint.test.cpp | 5 + .../dsl/acir_format/block_constraint.test.cpp | 1 + .../dsl/acir_format/ec_operations.test.cpp | 2 + .../dsl/acir_format/ecdsa_secp256k1.test.cpp | 3 + .../dsl/acir_format/ecdsa_secp256r1.test.cpp | 4 + .../acir_format/honk_recursion_constraint.cpp | 370 ++++++++++++++++++ .../acir_format/honk_recursion_constraint.hpp | 76 ++++ .../honk_recursion_constraint.test.cpp | 365 +++++++++++++++++ .../acir_format/poseidon2_constraint.test.cpp | 1 + .../acir_format/recursion_constraint.test.cpp | 39 +- .../acir_format/sha256_constraint.test.cpp | 1 + .../stdlib/primitives/bigfield/bigfield.hpp | 16 +- 16 files changed, 1011 insertions(+), 37 deletions(-) create mode 100644 barretenberg/acir_tests/reset_acir_tests.sh create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.hpp create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp diff --git a/barretenberg/acir_tests/reset_acir_tests.sh b/barretenberg/acir_tests/reset_acir_tests.sh new file mode 100644 index 00000000000..e83bea9189e --- /dev/null +++ b/barretenberg/acir_tests/reset_acir_tests.sh @@ -0,0 +1,7 @@ +cd ~/aztec-packages/noir/noir-repo +cargo clean +noirup -p . +cd test_programs && ./rebuild.sh + +cd ~/aztec-packages/barretenberg/acir_tests +rm -rf acir_tests 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 f74228a115f..3dc7fc15e6c 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -10,7 +10,10 @@ template class DSLBigInts; template class DSLBigInts; template -void build_constraints(Builder& builder, AcirFormat const& constraint_system, bool has_valid_witness_assignments) +void build_constraints(Builder& builder, + AcirFormat const& constraint_system, + bool has_valid_witness_assignments, + bool honk_recursion) { // Add arithmetic gates for (const auto& constraint : constraint_system.poly_triple_constraints) { @@ -118,6 +121,7 @@ void build_constraints(Builder& builder, AcirFormat const& constraint_system, bo create_bigint_to_le_bytes_constraint(builder, constraint, dsl_bigints); } + // RecursionConstraint // TODO(https://github.com/AztecProtocol/barretenberg/issues/817): disable these for UGH for now since we're not yet // dealing with proper recursion if constexpr (IsGoblinUltraBuilder) { @@ -161,7 +165,7 @@ void build_constraints(Builder& builder, AcirFormat const& constraint_system, bo auto error_string = format( "Public inputs are always stripped from proofs unless we have a recursive proof.\n" "Thus, public inputs attached to a proof must match the recursive aggregation object in size " - "which is {}\n", + "which is ", RecursionConstraint::AGGREGATION_OBJECT_SIZE); throw_or_abort(error_string); } @@ -206,6 +210,121 @@ void build_constraints(Builder& builder, AcirFormat const& constraint_system, bo builder.set_recursive_proof(proof_output_witness_indices); } } + + // HonkRecursionConstraint + // TODO(https://github.com/AztecProtocol/barretenberg/issues/817): disable these for UGH for now since we're not yet + // dealing with proper recursion + if constexpr (IsGoblinUltraBuilder) { + if (!constraint_system.honk_recursion_constraints.empty()) { + info("WARNING: this circuit contains honk_recursion_constraints!"); + } + } else { + // These are set and modified whenever we encounter a recursion opcode + // + // These should not be set by the caller + // TODO(maxim): Check if this is always the case. ie I won't receive a proof that will set the first + // TODO(maxim): input_aggregation_object to be non-zero. + // TODO(maxim): if not, we can add input_aggregation_object to the proof too for all recursive proofs + // TODO(maxim): This might be the case for proof trees where the proofs are created on different machines + std::array current_aggregation_object = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + // Get the size of proof with no public inputs prepended to it + // This is used while processing recursion constraints to determine whether + // the proof we are verifying contains a recursive proof itself + auto proof_size_no_pub_inputs = recursion_honk_proof_size_without_public_inputs(); + + // Add recursion constraints + for (auto constraint : constraint_system.honk_recursion_constraints) { + // A proof passed into the constraint should be stripped of its public inputs, except in the case where a + // proof contains an aggregation object itself. We refer to this as the `nested_aggregation_object`. The + // verifier circuit requires that the indices to a nested proof aggregation state are a circuit constant. + // The user tells us they how they want these constants set by keeping the nested aggregation object + // attached to the proof as public inputs. As this is the only object that can prepended to the proof if the + // proof is above the expected size (with public inputs stripped) + std::array nested_aggregation_object = {}; + // If the proof has public inputs attached to it, we should handle setting the nested aggregation object + // The public inputs attached to a proof should match the aggregation object in size + if (constraint.proof.size() - proof_size_no_pub_inputs != + HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE) { + auto error_string = format( + "Public inputs are always stripped from proofs unless we have a recursive proof.\n" + "Thus, public inputs attached to a proof must match the recursive aggregation object in size " + "which is ", + HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE); + throw_or_abort(error_string); + } + for (size_t i = 0; i < HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) { + // Set the nested aggregation object indices to the current size of the public inputs + // This way we know that the nested aggregation object indices will always be the last + // indices of the public inputs + nested_aggregation_object[i] = static_cast(constraint.public_inputs.size()); + // Attach the nested aggregation object to the end of the public inputs to fill in + // the slot where the nested aggregation object index will point into + constraint.public_inputs.emplace_back(constraint.proof[i]); + } + // Remove the aggregation object so that they can be handled as normal public inputs + // in they way taht the recursion constraint expects + constraint.proof.erase(constraint.proof.begin(), + constraint.proof.begin() + + static_cast(HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE)); + current_aggregation_object = create_honk_recursion_constraints(builder, + constraint, + current_aggregation_object, + nested_aggregation_object, + has_valid_witness_assignments); + } + + // Now that the circuit has been completely built, we add the output aggregation as public + // inputs. + if (!constraint_system.honk_recursion_constraints.empty()) { + + // First add the output aggregation object as public inputs + // Set the indices as public inputs because they are no longer being + // created in ACIR + for (const auto& idx : current_aggregation_object) { + builder.set_public_input(idx); + } + + // Make sure the verification key records the public input indices of the + // final recursion output. + std::vector proof_output_witness_indices(current_aggregation_object.begin(), + current_aggregation_object.end()); + builder.set_recursive_proof(proof_output_witness_indices); + } else if (honk_recursion && + builder.is_recursive_circuit) { // Set a default aggregation object if we don't have one. + // TODO(https://github.com/AztecProtocol/barretenberg/issues/911): These are pairing points extracted from + // a valid proof. This is a workaround because we can't represent the point at infinity in biggroup yet. + fq x0("0x031e97a575e9d05a107acb64952ecab75c020998797da7842ab5d6d1986846cf"); + fq y0("0x178cbf4206471d722669117f9758a4c410db10a01750aebb5666547acf8bd5a4"); + + fq x1("0x0f94656a2ca489889939f81e9c74027fd51009034b3357f0e91b8a11e7842c38"); + fq y1("0x1b52c2020d7464a0c80c0da527a08193fe27776f50224bd6fb128b46c1ddb67f"); + std::vector aggregation_object_fq_values = { x0, y0, x1, y1 }; + size_t agg_obj_indices_idx = 0; + for (fq val : aggregation_object_fq_values) { + const uint256_t x = val; + std::array val_limbs = { + x.slice(0, fq_ct::NUM_LIMB_BITS), + x.slice(fq_ct::NUM_LIMB_BITS, fq_ct::NUM_LIMB_BITS * 2), + x.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 3), + x.slice(fq_ct::NUM_LIMB_BITS * 3, bb::stdlib::field_conversion::TOTAL_BITS) + }; + for (size_t i = 0; i < fq_ct::NUM_LIMBS; ++i) { + uint32_t idx = builder.add_variable(val_limbs[i]); + builder.set_public_input(idx); + current_aggregation_object[agg_obj_indices_idx] = idx; + agg_obj_indices_idx++; + } + } + // Make sure the verification key records the public input indices of the + // final recursion output. + std::vector proof_output_witness_indices(current_aggregation_object.begin(), + current_aggregation_object.end()); + builder.set_recursive_proof(proof_output_witness_indices); + } + } } /** @@ -218,14 +337,17 @@ void build_constraints(Builder& builder, AcirFormat const& constraint_system, bo * @return Builder */ template <> -UltraCircuitBuilder create_circuit(const AcirFormat& constraint_system, size_t size_hint, WitnessVector const& witness) +UltraCircuitBuilder create_circuit(const AcirFormat& constraint_system, + size_t size_hint, + WitnessVector const& witness, + bool honk_recursion) { Builder builder{ size_hint, witness, constraint_system.public_inputs, constraint_system.varnum, constraint_system.recursive }; bool has_valid_witness_assignments = !witness.empty(); - build_constraints(builder, constraint_system, has_valid_witness_assignments); + build_constraints(builder, constraint_system, has_valid_witness_assignments, honk_recursion); builder.finalize_circuit(); @@ -244,7 +366,8 @@ UltraCircuitBuilder create_circuit(const AcirFormat& constraint_system, size_t s template <> GoblinUltraCircuitBuilder create_circuit(const AcirFormat& constraint_system, [[maybe_unused]] size_t size_hint, - WitnessVector const& witness) + WitnessVector const& witness, + bool honk_recursion) { // Construct a builder using the witness and public input data from acir and with the goblin-owned op_queue auto op_queue = std::make_shared(); // instantiate empty op_queue @@ -253,13 +376,13 @@ GoblinUltraCircuitBuilder create_circuit(const AcirFormat& constraint_system, // Populate constraints in the builder via the data in constraint_system bool has_valid_witness_assignments = !witness.empty(); - acir_format::build_constraints(builder, constraint_system, has_valid_witness_assignments); + acir_format::build_constraints(builder, constraint_system, has_valid_witness_assignments, honk_recursion); builder.finalize_circuit(); return builder; }; -template void build_constraints(GoblinUltraCircuitBuilder&, AcirFormat const&, bool); +template void build_constraints(GoblinUltraCircuitBuilder&, AcirFormat const&, bool, bool); } // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp index 9add17a1451..35d4d1fe6c1 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -9,6 +9,7 @@ #include "ec_operations.hpp" #include "ecdsa_secp256k1.hpp" #include "ecdsa_secp256r1.hpp" +#include "honk_recursion_constraint.hpp" #include "keccak_constraint.hpp" #include "logic_constraint.hpp" #include "multi_scalar_mul.hpp" @@ -54,6 +55,7 @@ struct AcirFormat { std::vector multi_scalar_mul_constraints; std::vector ec_add_constraints; std::vector recursion_constraints; + std::vector honk_recursion_constraints; std::vector bigint_from_le_bytes_constraints; std::vector bigint_to_le_bytes_constraints; std::vector bigint_operations; @@ -89,6 +91,7 @@ struct AcirFormat { multi_scalar_mul_constraints, ec_add_constraints, recursion_constraints, + honk_recursion_constraints, poly_triple_constraints, block_constraints, bigint_from_le_bytes_constraints, @@ -102,9 +105,17 @@ using WitnessVector = std::vector>; using WitnessVectorStack = std::vector>; template -Builder create_circuit(const AcirFormat& constraint_system, size_t size_hint = 0, WitnessVector const& witness = {}); +Builder create_circuit(const AcirFormat& constraint_system, + size_t size_hint = 0, + WitnessVector const& witness = {}, + bool honk_recursion = false); template -void build_constraints(Builder& builder, AcirFormat const& constraint_system, bool has_valid_witness_assignments); +void build_constraints(Builder& builder, + AcirFormat const& constraint_system, + bool has_valid_witness_assignments, + bool honk_recursion = false); // honk_recursion means we will honk to recursively verify this + // circuit. This distinction is needed to not add the default + // aggregation object when we're not using the honk RV. } // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index 038db2a28f9..cf6df51ea90 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp @@ -52,6 +52,7 @@ TEST_F(AcirFormatTests, TestASingleConstraintNoPubInputs) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -170,6 +171,7 @@ TEST_F(AcirFormatTests, TestLogicGateFromNoirCircuit) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -240,6 +242,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifyPass) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -337,6 +340,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifySmallRange) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -453,6 +457,7 @@ TEST_F(AcirFormatTests, TestVarKeccak) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -502,6 +507,7 @@ TEST_F(AcirFormatTests, TestKeccakPermutation) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp index 1cc86262bd1..923d496fd8d 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp @@ -189,6 +189,7 @@ TEST_F(BigIntTests, TestBigIntConstraintMultiple) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -259,6 +260,7 @@ TEST_F(BigIntTests, TestBigIntConstraintSimple) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = { from_le_bytes_constraint_bigint1 }, .bigint_to_le_bytes_constraints = { result2_to_le_bytes }, .bigint_operations = { add_constraint }, @@ -314,6 +316,7 @@ TEST_F(BigIntTests, TestBigIntConstraintReuse) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -373,6 +376,7 @@ TEST_F(BigIntTests, TestBigIntConstraintReuse2) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -453,6 +457,7 @@ TEST_F(BigIntTests, TestBigIntDIV) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = { from_le_bytes_constraint_bigint1, from_le_bytes_constraint_bigint2 }, .bigint_to_le_bytes_constraints = { result3_to_le_bytes }, .bigint_operations = { div_constraint }, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp index 5d649d8feb3..eb377b5b532 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp @@ -131,6 +131,7 @@ TEST_F(UltraPlonkRAM, TestBlockConstraint) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp index 59333ddc0d4..1cd34c076f8 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp @@ -76,6 +76,7 @@ TEST_F(EcOperations, TestECOperations) .multi_scalar_mul_constraints = {}, .ec_add_constraints = { ec_add_constraint }, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -161,6 +162,7 @@ TEST_F(EcOperations, TestECMultiScalarMul) .multi_scalar_mul_constraints = { msm_constrain }, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp index 61782002c85..90a5546068d 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp @@ -111,6 +111,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintSucceed) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -162,6 +163,7 @@ TEST_F(ECDSASecp256k1, TestECDSACompilesForVerifier) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -208,6 +210,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintFail) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp index de1d0931d8c..257bcf4a2e2 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp @@ -145,6 +145,7 @@ TEST(ECDSASecp256r1, test_hardcoded) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -198,6 +199,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintSucceed) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -249,6 +251,7 @@ TEST(ECDSASecp256r1, TestECDSACompilesForVerifier) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -295,6 +298,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintFail) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp new file mode 100644 index 00000000000..fb46384ce01 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp @@ -0,0 +1,370 @@ +#include "honk_recursion_constraint.hpp" +#include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" +#include "barretenberg/plonk/transcript/transcript_wrappers.hpp" +#include "barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp" +#include "barretenberg/stdlib/plonk_recursion/verifier/verifier.hpp" +#include "barretenberg/stdlib/primitives/bigfield/constants.hpp" +#include "recursion_constraint.hpp" + +namespace acir_format { + +using namespace bb::plonk; + +/** + * @brief Add constraints required to recursively verify an UltraPlonk proof + * + * @param builder + * @param input + * @tparam has_valid_witness_assignment. Do we have witnesses or are we just generating keys? + * @tparam inner_proof_contains_recursive_proof. Do we expect the inner proof to also have performed recursive + * verification? We need to know this at circuit-compile time. + * + * @note We currently only support HonkRecursionConstraint where inner_proof_contains_recursive_proof = false. + * We would either need a separate ACIR opcode where inner_proof_contains_recursive_proof = true, + * or we need non-witness data to be provided as metadata in the ACIR opcode + */ +std::array create_honk_recursion_constraints( + Builder& builder, + const HonkRecursionConstraint& input, + std::array input_aggregation_object, + std::array nested_aggregation_object, + bool has_valid_witness_assignments) +{ + const auto& nested_aggregation_indices = nested_aggregation_object; + const bool inner_proof_contains_recursive_proof = true; + + // If we do not have a witness, we must ensure that our dummy witness will not trigger + // on-curve errors and inverting-zero errors + { + // get a fake key/proof that satisfies on-curve + inversion-zero checks + const std::vector dummy_key = export_dummy_honk_key_in_recursion_format( + PolynomialManifest(Builder::CIRCUIT_TYPE), inner_proof_contains_recursive_proof); + const auto manifest = Composer::create_manifest(input.public_inputs.size()); + std::vector dummy_proof = + export_dummy_honk_transcript_in_recursion_format(manifest, inner_proof_contains_recursive_proof); + + for (size_t i = 0; i < input.public_inputs.size(); ++i) { + const auto public_input_idx = input.public_inputs[i]; + // if we do NOT have a witness assignment (i.e. are just building the proving/verification keys), + // we add our dummy public input values as Builder variables. + // if we DO have a valid witness assignment, we use the real witness assignment + bb::fr dummy_field = + has_valid_witness_assignments ? builder.get_variable(public_input_idx) : dummy_proof[i]; + // Create a copy constraint between our dummy field and the witness index provided by + // HonkRecursionConstraint. This will make the HonkRecursionConstraint idx equal to `dummy_field`. In the + // case of a valid witness assignment, this does nothing (as dummy_field = real value) In the case of no + // valid witness assignment, this makes sure that the HonkRecursionConstraint witness indices will not + // trigger basic errors (check inputs are on-curve, check we are not inverting 0) + // + // Failing to do these copy constraints on public inputs will trigger these basic errors + // in the case of a nested proof, as an aggregation object is expected to be two G1 points even + // in the case of no valid witness assignments. + builder.assert_equal(builder.add_variable(dummy_field), public_input_idx); + } + // Remove the public inputs from the dummy proof + // The proof supplied to the recursion constraint will already be stripped of public inputs + // while the barretenberg API works with public inputs prepended to the proof. + dummy_proof.erase(dummy_proof.begin(), + dummy_proof.begin() + static_cast(input.public_inputs.size())); + for (size_t i = 0; i < input.proof.size(); ++i) { + const auto proof_field_idx = input.proof[i]; + bb::fr dummy_field = has_valid_witness_assignments ? builder.get_variable(proof_field_idx) : dummy_proof[i]; + builder.assert_equal(builder.add_variable(dummy_field), proof_field_idx); + } + for (size_t i = 0; i < input.key.size(); ++i) { + const auto key_field_idx = input.key[i]; + bb::fr dummy_field = has_valid_witness_assignments ? builder.get_variable(key_field_idx) : dummy_key[i]; + builder.assert_equal(builder.add_variable(dummy_field), key_field_idx); + } + } + + // Construct an in-circuit representation of the verification key. + // For now, the v-key is a circuit constant and is fixed for the circuit. + // (We may need a separate recursion opcode for this to vary, or add more config witnesses to this opcode) + const auto& aggregation_input = input_aggregation_object; + aggregation_state_ct previous_aggregation; + + // If we have previously recursively verified proofs, `inner_aggregation_object_nonzero = true` + // For now this is a complile-time constant i.e. whether this is true/false is fixed for the circuit! + bool inner_aggregation_indices_all_zero = true; + for (const auto& idx : aggregation_input) { + inner_aggregation_indices_all_zero &= (idx == 0); + } + + if (!inner_aggregation_indices_all_zero) { + std::array aggregation_elements; + for (size_t i = 0; i < 4; ++i) { + aggregation_elements[i] = + bn254::BaseField(field_ct::from_witness_index(&builder, aggregation_input[4 * i]), + field_ct::from_witness_index(&builder, aggregation_input[4 * i + 1]), + field_ct::from_witness_index(&builder, aggregation_input[4 * i + 2]), + field_ct::from_witness_index(&builder, aggregation_input[4 * i + 3])); + aggregation_elements[i].assert_is_in_field(); + } + // If we have a previous aggregation object, assign it to `previous_aggregation` so that it is included + // in stdlib::recursion::verify_proof + previous_aggregation.P0 = bn254::Group(aggregation_elements[0], aggregation_elements[1]); + previous_aggregation.P1 = bn254::Group(aggregation_elements[2], aggregation_elements[3]); + previous_aggregation.has_data = true; + } else { + previous_aggregation.has_data = false; + } + + transcript::Manifest manifest = Composer::create_manifest(input.public_inputs.size()); + + std::vector key_fields; + key_fields.reserve(input.key.size()); + for (const auto& idx : input.key) { + auto field = field_ct::from_witness_index(&builder, idx); + key_fields.emplace_back(field); + } + + std::vector proof_fields; + // Prepend the public inputs to the proof fields because this is how the + // core barretenberg library processes proofs (with the public inputs first and not separated) + proof_fields.reserve(input.proof.size() + input.public_inputs.size()); + for (const auto& idx : input.public_inputs) { + auto field = field_ct::from_witness_index(&builder, idx); + proof_fields.emplace_back(field); + } + for (const auto& idx : input.proof) { + auto field = field_ct::from_witness_index(&builder, idx); + proof_fields.emplace_back(field); + } + + // recursively verify the proof + std::shared_ptr vkey = verification_key_ct::from_field_elements( + &builder, key_fields, inner_proof_contains_recursive_proof, nested_aggregation_indices); + vkey->program_width = noir_recursive_settings::program_width; + + Transcript_ct transcript(&builder, manifest, proof_fields, input.public_inputs.size()); + aggregation_state_ct result = bb::stdlib::recursion::verify_proof_( + &builder, vkey, transcript, previous_aggregation); + + // Assign correct witness value to the verification key hash + vkey->hash().assert_equal(field_ct::from_witness_index(&builder, input.key_hash)); + + ASSERT(result.public_inputs.size() == input.public_inputs.size()); + + // Assign the `public_input` field to the public input of the inner proof + for (size_t i = 0; i < input.public_inputs.size(); ++i) { + result.public_inputs[i].assert_equal(field_ct::from_witness_index(&builder, input.public_inputs[i])); + } + + // We want to return an array, so just copy the vector into the array + ASSERT(result.proof_witness_indices.size() == HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE); + std::array resulting_output_aggregation_object; + std::copy(result.proof_witness_indices.begin(), + result.proof_witness_indices.begin() + HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE, + resulting_output_aggregation_object.begin()); + + return resulting_output_aggregation_object; +} + +/** + * @brief When recursively verifying proofs, we represent the verification key using field elements. + * This method exports the key formatted in the manner our recursive verifier expects. + * NOTE: only used by the dsl at the moment. Might be cleaner to make this a dsl function? + * + * @return std::vector + */ +std::vector export_honk_key_in_recursion_format(std::shared_ptr const& vkey) +{ + std::vector output; + output.emplace_back(vkey->domain.root); + output.emplace_back(vkey->domain.domain); + output.emplace_back(vkey->domain.generator); + output.emplace_back(vkey->circuit_size); + output.emplace_back(vkey->num_public_inputs); + output.emplace_back(vkey->contains_recursive_proof); + for (size_t i = 0; i < HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) { + if (vkey->recursive_proof_public_input_indices.size() > i) { + output.emplace_back(vkey->recursive_proof_public_input_indices[i]); + } else { + output.emplace_back(0); + ASSERT(vkey->contains_recursive_proof == false); + } + } + for (const auto& descriptor : vkey->polynomial_manifest.get()) { + if (descriptor.source == PolynomialSource::SELECTOR || descriptor.source == PolynomialSource::PERMUTATION) { + const auto element = vkey->commitments.at(std::string(descriptor.commitment_label)); + auto g1_as_fields = export_g1_affine_element_as_fields(element); + output.emplace_back(g1_as_fields.x_lo); + output.emplace_back(g1_as_fields.x_hi); + output.emplace_back(g1_as_fields.y_lo); + output.emplace_back(g1_as_fields.y_hi); + } + } + + verification_key_data vkey_data{ + .circuit_type = static_cast(vkey->circuit_type), + .circuit_size = static_cast(vkey->circuit_size), + .num_public_inputs = static_cast(vkey->num_public_inputs), + .commitments = vkey->commitments, + .contains_recursive_proof = vkey->contains_recursive_proof, + .recursive_proof_public_input_indices = vkey->recursive_proof_public_input_indices, + }; + output.emplace_back(vkey_data.hash_native(0)); // key_hash + return output; +} + +/** + * @brief When recursively verifying proofs, we represent the verification key using field elements. + * This method exports the key formatted in the manner our recursive verifier expects. + * A dummy key is used when building a circuit without a valid witness assignment. + * We want the transcript to contain valid G1 points to prevent on-curve errors being thrown. + * We want a non-zero circuit size as this element will be inverted by the circuit + * and we do not want an "inverting 0" error thrown + * + * @return std::vector + */ +std::vector export_dummy_honk_key_in_recursion_format(const PolynomialManifest& polynomial_manifest, + const bool contains_recursive_proof) +{ + std::vector output; + output.emplace_back(1); // domain.domain (will be inverted) + output.emplace_back(1); // domain.root (will be inverted) + output.emplace_back(1); // domain.generator (will be inverted) + + output.emplace_back(1); // circuit size + output.emplace_back(1); // num public inputs + + output.emplace_back(contains_recursive_proof); // contains_recursive_proof + for (size_t i = 0; i < HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) { + output.emplace_back(0); // recursive_proof_public_input_indices + } + + for (const auto& descriptor : polynomial_manifest.get()) { + if (descriptor.source == PolynomialSource::SELECTOR || descriptor.source == PolynomialSource::PERMUTATION) { + // the std::biggroup class creates unsatisfiable constraints when identical points are added/subtracted. + // (when verifying zk proofs this is acceptable as we make sure verification key points are not identical. + // And prover points should contain randomness for an honest Prover). + // This check can also trigger a runtime error due to causing 0 to be inverted. + // When creating dummy verification key points we must be mindful of the above and make sure that each + // transcript point is unique. + auto scalar = bb::fr::random_element(); + const auto element = bb::g1::affine_element(bb::g1::one * scalar); + auto g1_as_fields = export_g1_affine_element_as_fields(element); + output.emplace_back(g1_as_fields.x_lo); + output.emplace_back(g1_as_fields.x_hi); + output.emplace_back(g1_as_fields.y_lo); + output.emplace_back(g1_as_fields.y_hi); + } + } + + output.emplace_back(0); // key_hash + + return output; +} + +/** + * @brief Returns transcript represented as a vector of bb::fr. + * Used to represent recursive proofs (i.e. proof represented as circuit-native field elements) + * + * @return std::vector + */ +std::vector export_honk_transcript_in_recursion_format(const transcript::StandardTranscript& transcript) +{ + std::vector fields; + const auto num_rounds = transcript.get_manifest().get_num_rounds(); + for (size_t i = 0; i < num_rounds; ++i) { + for (const auto& manifest_element : transcript.get_manifest().get_round_manifest(i).elements) { + if (!manifest_element.derived_by_verifier) { + if (manifest_element.num_bytes == 32 && manifest_element.name != "public_inputs") { + fields.emplace_back(transcript.get_field_element(manifest_element.name)); + } else if (manifest_element.num_bytes == 64 && manifest_element.name != "public_inputs") { + const auto group_element = transcript.get_group_element(manifest_element.name); + auto g1_as_fields = export_g1_affine_element_as_fields(group_element); + fields.emplace_back(g1_as_fields.x_lo); + fields.emplace_back(g1_as_fields.x_hi); + fields.emplace_back(g1_as_fields.y_lo); + fields.emplace_back(g1_as_fields.y_hi); + } else { + ASSERT(manifest_element.name == "public_inputs"); + const auto public_inputs_vector = transcript.get_field_element_vector(manifest_element.name); + for (const auto& ele : public_inputs_vector) { + fields.emplace_back(ele); + } + } + } + } + } + return fields; +} + +/** + * @brief Get a dummy fake proof for recursion. All elliptic curve group elements are still valid points to prevent + * errors being thrown. + * + * @param manifest + * @return std::vector + */ +std::vector export_dummy_honk_transcript_in_recursion_format(const transcript::Manifest& manifest, + const bool contains_recursive_proof) +{ + std::vector fields; + const auto num_rounds = manifest.get_num_rounds(); + for (size_t i = 0; i < num_rounds; ++i) { + for (const auto& manifest_element : manifest.get_round_manifest(i).elements) { + if (!manifest_element.derived_by_verifier) { + if (manifest_element.num_bytes == 32 && manifest_element.name != "public_inputs") { + // auto scalar = bb::fr::random_element(); + fields.emplace_back(0); + } else if (manifest_element.num_bytes == 64 && manifest_element.name != "public_inputs") { + // the std::biggroup class creates unsatisfiable constraints when identical points are + // added/subtracted. + // (when verifying zk proofs this is acceptable as we make sure verification key points are not + // identical. And prover points should contain randomness for an honest Prover). This check can + // also trigger a runtime error due to causing 0 to be inverted. When creating dummy proof + // points we must be mindful of the above and make sure that each point is unique. + auto scalar = bb::fr::random_element(); + const auto group_element = bb::g1::affine_element(bb::g1::one * scalar); + auto g1_as_fields = export_g1_affine_element_as_fields(group_element); + fields.emplace_back(g1_as_fields.x_lo); + fields.emplace_back(g1_as_fields.x_hi); + fields.emplace_back(g1_as_fields.y_lo); + fields.emplace_back(g1_as_fields.y_hi); + } else { + ASSERT(manifest_element.name == "public_inputs"); + const size_t num_public_inputs = manifest_element.num_bytes / 32; + // If we have a recursive proofs the public inputs must describe an aggregation object that + // is composed of two valid G1 points on the curve. Without this conditional we will get a + // runtime error that we are attempting to invert 0. + if (contains_recursive_proof) { + // When setting up the ACIR we emplace back the nested aggregation object + // fetched from the proof onto the public inputs. Thus, we can expect the + // nested aggregation object to always be at the end of the public inputs. + for (size_t k = 0; k < num_public_inputs - HonkRecursionConstraint::AGGREGATION_OBJECT_SIZE; + ++k) { + fields.emplace_back(0); + } + for (size_t k = 0; k < HonkRecursionConstraint::NUM_AGGREGATION_ELEMENTS; ++k) { + auto scalar = bb::fr::random_element(); + const auto group_element = bb::g1::affine_element(bb::g1::one * scalar); + auto g1_as_fields = export_g1_affine_element_as_fields(group_element); + fields.emplace_back(g1_as_fields.x_lo); + fields.emplace_back(g1_as_fields.x_hi); + fields.emplace_back(g1_as_fields.y_lo); + fields.emplace_back(g1_as_fields.y_hi); + } + } else { + for (size_t j = 0; j < num_public_inputs; ++j) { + // auto scalar = bb::fr::random_element(); + fields.emplace_back(0); + } + } + } + } + } + } + return fields; +} + +size_t recursion_honk_proof_size_without_public_inputs() +{ + const auto manifest = Composer::create_manifest(0); + auto dummy_transcript = export_dummy_honk_transcript_in_recursion_format(manifest, false); + return dummy_transcript.size(); +} + +} // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.hpp new file mode 100644 index 00000000000..8696e9ef073 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.hpp @@ -0,0 +1,76 @@ +#pragma once +#include "barretenberg/dsl/types.hpp" +#include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" +#include + +namespace acir_format { + +using namespace bb::plonk; + +/** + * @brief HonkRecursionConstraint struct contains information required to recursively verify a proof! + * + * @details The recursive verifier algorithm produces an 'aggregation object' representing 2 G1 points, expressed as 16 + * witness values. The smart contract Verifier must be aware of this aggregation object in order to complete the full + * recursive verification. If the circuit verifies more than 1 proof, the recursion algorithm will update a pre-existing + * aggregation object (`input_aggregation_object`). + * + * @details We currently require that the inner circuit being verified only has a single public input. If more are + * required, the outer circuit can hash them down to 1 input. + * + * @param verification_key_data The inner circuit vkey. Is converted into circuit witness values (internal to the + * backend) + * @param proof The plonk proof. Is converted into circuit witness values (internal to the backend) + * @param is_aggregation_object_nonzero A flag to tell us whether the circuit has already recursively verified proofs + * (and therefore an aggregation object is present) + * @param public_input The index of the single public input + * @param input_aggregation_object Witness indices of pre-existing aggregation object (if it exists) + * @param output_aggregation_object Witness indices of the aggregation object produced by recursive verification + * @param nested_aggregation_object Public input indices of an aggregation object inside the proof. + * + * @note If input_aggregation_object witness indices are all zero, we interpret this to mean that the inner proof does + * NOT contain a previously recursively verified proof + * @note nested_aggregation_object is used for cases where the proof being verified contains an aggregation object in + * its public inputs! If this is the case, we record the public input locations in `nested_aggregation_object`. If the + * inner proof is of a circuit that does not have a nested aggregation object, these values are all zero. + * + * To outline the interaction between the input_aggergation_object and the nested_aggregation_object take the following + * example: If we have a circuit that verifies 2 proofs A and B, the recursion constraint for B will have an + * input_aggregation_object that points to the aggregation output produced by verifying A. If circuit B also verifies a + * proof, in the above example the recursion constraint for verifying B will have a nested object that describes the + * aggregation object in B’s public inputs as well as an input aggregation object that points to the object produced by + * the previous recursion constraint in the circuit (the one that verifies A) + * + */ +struct HonkRecursionConstraint { + // An aggregation state is represented by two G1 affine elements. Each G1 point has + // two field element coordinates (x, y). Thus, four field elements + static constexpr size_t NUM_AGGREGATION_ELEMENTS = 4; + // Four limbs are used when simulating a non-native field using the bigfield class + static constexpr size_t AGGREGATION_OBJECT_SIZE = + NUM_AGGREGATION_ELEMENTS * NUM_QUOTIENT_PARTS; // 16 field elements + std::vector key; + std::vector proof; + std::vector public_inputs; + uint32_t key_hash; + + friend bool operator==(HonkRecursionConstraint const& lhs, HonkRecursionConstraint const& rhs) = default; +}; + +std::array create_honk_recursion_constraints( + Builder& builder, + const HonkRecursionConstraint& input, + std::array input_aggregation_object, + std::array nested_aggregation_object, + bool has_valid_witness_assignments = false); + +std::vector export_honk_key_in_recursion_format(std::shared_ptr const& vkey); +std::vector export_dummy_honk_key_in_recursion_format(const PolynomialManifest& polynomial_manifest, + bool contains_recursive_proof = 0); + +std::vector export_honk_transcript_in_recursion_format(const transcript::StandardTranscript& transcript); +std::vector export_dummy_honk_transcript_in_recursion_format(const transcript::Manifest& manifest, + const bool contains_recursive_proof); +size_t recursion_honk_proof_size_without_public_inputs(); + +} // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp new file mode 100644 index 00000000000..7ac1fc58eb0 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp @@ -0,0 +1,365 @@ +#include "honk_recursion_constraint.hpp" +#include "acir_format.hpp" +#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" + +#include +#include + +using namespace acir_format; +using namespace bb::plonk; + +class AcirHonkRecursionConstraint : public ::testing::Test { + public: + Builder create_inner_circuit() + { + /** + * constraints produced by Noir program: + * fn main(x : u32, y : pub u32) { + * let z = x ^ y; + * + * constrain z != 10; + * } + **/ + RangeConstraint range_a{ + .witness = 0, + .num_bits = 32, + }; + RangeConstraint range_b{ + .witness = 1, + .num_bits = 32, + }; + + LogicConstraint logic_constraint{ + .a = 0, + .b = 1, + .result = 2, + .num_bits = 32, + .is_xor_gate = 1, + }; + poly_triple expr_a{ + .a = 2, + .b = 3, + .c = 0, + .q_m = 0, + .q_l = 1, + .q_r = -1, + .q_o = 0, + .q_c = -10, + }; + poly_triple expr_b{ + .a = 3, + .b = 4, + .c = 5, + .q_m = 1, + .q_l = 0, + .q_r = 0, + .q_o = -1, + .q_c = 0, + }; + poly_triple expr_c{ + .a = 3, + .b = 5, + .c = 3, + .q_m = 1, + .q_l = 0, + .q_r = 0, + .q_o = -1, + .q_c = 0, + + }; + poly_triple expr_d{ + .a = 5, + .b = 0, + .c = 0, + .q_m = 0, + .q_l = -1, + .q_r = 0, + .q_o = 0, + .q_c = 1, + }; + + AcirFormat constraint_system{ .varnum = 6, + .recursive = true, + .num_acir_opcodes = 7, + .public_inputs = { 1, 2 }, + .logic_constraints = { logic_constraint }, + .range_constraints = { range_a, range_b }, + .aes128_constraints = {}, + .sha256_constraints = {}, + .sha256_compression = {}, + .schnorr_constraints = {}, + .ecdsa_k1_constraints = {}, + .ecdsa_r1_constraints = {}, + .blake2s_constraints = {}, + .blake3_constraints = {}, + .keccak_constraints = {}, + .keccak_permutations = {}, + .pedersen_constraints = {}, + .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, + .multi_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .recursion_constraints = {}, + .honk_recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_to_le_bytes_constraints = {}, + .bigint_operations = {}, + .poly_triple_constraints = { expr_a, expr_b, expr_c, expr_d }, + .quad_constraints = {}, + .block_constraints = {} }; + + uint256_t inverse_of_five = fr(5).invert(); + WitnessVector witness{ + 5, 10, 15, 5, inverse_of_five, 1, + }; + auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness, /*honk recursion*/ true); + + return builder; + } + + /** + * @brief Create a circuit that recursively verifies one or more inner circuits + * + * @param inner_circuits + * @return Composer + */ + Builder create_outer_circuit(std::vector& inner_circuits) + { + std::vector honk_recursion_constraints; + + size_t witness_offset = 0; + std::vector> witness; + + for (auto& inner_circuit : inner_circuits) { + + auto inner_composer = Composer(); + auto inner_prover = inner_composer.create_prover(inner_circuit); + auto inner_proof = inner_prover.construct_proof(); + auto inner_verifier = inner_composer.create_verifier(inner_circuit); + + const size_t num_inner_public_inputs = inner_circuit.get_public_inputs().size(); + transcript::StandardTranscript transcript(inner_proof.proof_data, + Composer::create_manifest(num_inner_public_inputs), + transcript::HashType::PedersenBlake3s, + 16); + + std::vector proof_witnesses = export_honk_transcript_in_recursion_format(transcript); + // - Save the public inputs so that we can set their values. + // - Then truncate them from the proof because the ACIR API expects proofs without public inputs + std::vector inner_public_input_values( + proof_witnesses.begin(), + proof_witnesses.begin() + static_cast(num_inner_public_inputs - + RecursionConstraint::AGGREGATION_OBJECT_SIZE)); + + // We want to make sure that we do not remove the nested aggregation object. + proof_witnesses.erase(proof_witnesses.begin(), + proof_witnesses.begin() + + static_cast(num_inner_public_inputs - + RecursionConstraint::AGGREGATION_OBJECT_SIZE)); + + std::vector key_witnesses = export_honk_key_in_recursion_format(inner_verifier.key); + bb::fr key_hash = key_witnesses.back(); + key_witnesses.pop_back(); + + const uint32_t key_hash_start_idx = static_cast(witness_offset); + const uint32_t public_input_start_idx = key_hash_start_idx + 1; + const uint32_t proof_indices_start_idx = static_cast( + public_input_start_idx + num_inner_public_inputs - RecursionConstraint::AGGREGATION_OBJECT_SIZE); + const uint32_t key_indices_start_idx = + static_cast(proof_indices_start_idx + proof_witnesses.size()); + + std::vector proof_indices; + std::vector key_indices; + std::vector inner_public_inputs; + for (size_t i = 0; i < proof_witnesses.size(); ++i) { + proof_indices.emplace_back(static_cast(i + proof_indices_start_idx)); + } + const size_t key_size = key_witnesses.size(); + for (size_t i = 0; i < key_size; ++i) { + key_indices.emplace_back(static_cast(i + key_indices_start_idx)); + } + // We keep the nested aggregation object attached to the proof, + // thus we do not explicitly have to keep the public inputs while setting up the initial recursion + // constraint. They will later be attached as public inputs when creating the circuit. + for (size_t i = 0; i < num_inner_public_inputs - RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) { + inner_public_inputs.push_back(static_cast(i + public_input_start_idx)); + } + + HonkRecursionConstraint honk_recursion_constraint{ + .key = key_indices, + .proof = proof_indices, + .public_inputs = inner_public_inputs, + .key_hash = key_hash_start_idx, + }; + honk_recursion_constraints.push_back(honk_recursion_constraint); + + witness.emplace_back(key_hash); + for (size_t i = 0; i < proof_indices_start_idx - public_input_start_idx; ++i) { + witness.emplace_back(0); + } + for (const auto& wit : proof_witnesses) { + witness.emplace_back(wit); + } + + for (const auto& wit : key_witnesses) { + witness.emplace_back(wit); + } + + // Set the values for the inner public inputs + // TODO(maxim): check this is wrong I think + // Note: this is confusing, but we minus one here due to the fact that the + // witness values have not taken into account that zero is taken up by the zero_idx + // + // We once again have to check whether we have a nested proof, because if we do have one + // then we could get a segmentation fault as `inner_public_inputs` was never filled with values. + for (size_t i = 0; i < num_inner_public_inputs - RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) { + witness[inner_public_inputs[i]] = inner_public_input_values[i]; + } + + witness_offset = key_indices_start_idx + key_witnesses.size(); + } + + AcirFormat constraint_system{ .varnum = static_cast(witness.size()), + .recursive = false, + .num_acir_opcodes = static_cast(honk_recursion_constraints.size()), + .public_inputs = {}, + .logic_constraints = {}, + .range_constraints = {}, + .aes128_constraints = {}, + .sha256_constraints = {}, + .sha256_compression = {}, + .schnorr_constraints = {}, + .ecdsa_k1_constraints = {}, + .ecdsa_r1_constraints = {}, + .blake2s_constraints = {}, + .blake3_constraints = {}, + .keccak_constraints = {}, + .keccak_permutations = {}, + .pedersen_constraints = {}, + .pedersen_hash_constraints = {}, + .poseidon2_constraints = {}, + .multi_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .recursion_constraints = {}, + .honk_recursion_constraints = honk_recursion_constraints, + .bigint_from_le_bytes_constraints = {}, + .bigint_to_le_bytes_constraints = {}, + .bigint_operations = {}, + .poly_triple_constraints = {}, + .quad_constraints = {}, + .block_constraints = {} }; + + auto outer_circuit = create_circuit(constraint_system, /*size_hint*/ 0, witness, /*honk recursion*/ true); + + return outer_circuit; + } + + protected: + static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } +}; + +TEST_F(AcirHonkRecursionConstraint, TestBasicDoubleHonkRecursionConstraints) +{ + std::vector layer_1_circuits; + layer_1_circuits.push_back(create_inner_circuit()); + + layer_1_circuits.push_back(create_inner_circuit()); + + auto layer_2_circuit = create_outer_circuit(layer_1_circuits); + + info("circuit gates = ", layer_2_circuit.get_num_gates()); + + auto layer_2_composer = Composer(); + auto prover = layer_2_composer.create_ultra_with_keccak_prover(layer_2_circuit); + info("prover gates = ", prover.circuit_size); + auto proof = prover.construct_proof(); + auto verifier = layer_2_composer.create_ultra_with_keccak_verifier(layer_2_circuit); + EXPECT_EQ(verifier.verify_proof(proof), true); +} + +TEST_F(AcirHonkRecursionConstraint, TestOneOuterRecursiveCircuit) +{ + /** + * We want to test the following: + * 1. circuit that verifies a proof of another circuit + * 2. the above, but the inner circuit contains a recursive proof output that we have to aggregate + * 3. the above, but the outer circuit verifies 2 proofs, the aggregation outputs from the 2 proofs (+ the recursive + * proof output from 2) are aggregated together + * + * A = basic circuit + * B = circuit that verifies proof of A + * C = circuit that verifies proof of B and a proof of A + * + * Layer 1 = proof of A + * Layer 2 = verifies proof of A and proof of B + * Layer 3 = verifies proof of C + * + * Attempt at a visual graphic + * =========================== + * + * C + * ^ + * | + * | - B + * ^ ^ + * | | + * | -A + * | + * - A + * + * =========================== + * + * Final aggregation object contains aggregated proofs for 2 instances of A and 1 instance of B + */ + std::vector layer_1_circuits; + layer_1_circuits.push_back(create_inner_circuit()); + info("created first inner circuit"); + + std::vector layer_2_circuits; + layer_2_circuits.push_back(create_inner_circuit()); + info("created second inner circuit"); + + layer_2_circuits.push_back(create_outer_circuit(layer_1_circuits)); + info("created first outer circuit"); + + auto layer_3_circuit = create_outer_circuit(layer_2_circuits); + info("created second outer circuit"); + info("number of gates in layer 3 = ", layer_3_circuit.get_num_gates()); + + auto layer_3_composer = Composer(); + auto prover = layer_3_composer.create_ultra_with_keccak_prover(layer_3_circuit); + info("prover gates = ", prover.circuit_size); + auto proof = prover.construct_proof(); + auto verifier = layer_3_composer.create_ultra_with_keccak_verifier(layer_3_circuit); + EXPECT_EQ(verifier.verify_proof(proof), true); +} + +TEST_F(AcirHonkRecursionConstraint, TestFullRecursiveComposition) +{ + std::vector layer_b_1_circuits; + layer_b_1_circuits.push_back(create_inner_circuit()); + info("created first inner circuit"); + + std::vector layer_b_2_circuits; + layer_b_2_circuits.push_back(create_inner_circuit()); + info("created second inner circuit"); + + std::vector layer_2_circuits; + layer_2_circuits.push_back(create_outer_circuit(layer_b_1_circuits)); + info("created first outer circuit"); + + layer_2_circuits.push_back(create_outer_circuit(layer_b_2_circuits)); + info("created second outer circuit"); + + auto layer_3_circuit = create_outer_circuit(layer_2_circuits); + info("created third outer circuit"); + info("number of gates in layer 3 circuit = ", layer_3_circuit.get_num_gates()); + + auto layer_3_composer = Composer(); + auto prover = layer_3_composer.create_ultra_with_keccak_prover(layer_3_circuit); + info("prover gates = ", prover.circuit_size); + auto proof = prover.construct_proof(); + auto verifier = layer_3_composer.create_ultra_with_keccak_verifier(layer_3_circuit); + EXPECT_EQ(verifier.verify_proof(proof), true); +} diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp index 4922c63cd69..71e2cc2160f 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp @@ -51,6 +51,7 @@ TEST_F(Poseidon2Tests, TestPoseidon2Permutation) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index b837f94ba2a..65e049fe0ca 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -103,6 +103,7 @@ Builder create_inner_circuit() .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -130,13 +131,9 @@ Builder create_outer_circuit(std::vector& inner_circuits) std::vector recursion_constraints; size_t witness_offset = 0; - std::array output_aggregation_object; std::vector> witness; - size_t circuit_idx = 0; for (auto& inner_circuit : inner_circuits) { - const bool has_input_aggregation_object = circuit_idx > 0; - auto inner_composer = Composer(); auto inner_prover = inner_composer.create_prover(inner_circuit); auto inner_proof = inner_prover.construct_proof(); @@ -162,33 +159,25 @@ Builder create_outer_circuit(std::vector& inner_circuits) if (!has_nested_proof) { proof_witnesses.erase(proof_witnesses.begin(), proof_witnesses.begin() + static_cast(num_inner_public_inputs)); + } else { + proof_witnesses.erase(proof_witnesses.begin(), + proof_witnesses.begin() + + static_cast(num_inner_public_inputs - + RecursionConstraint::AGGREGATION_OBJECT_SIZE)); } const std::vector key_witnesses = export_key_in_recursion_format(inner_verifier.key); const uint32_t key_hash_start_idx = static_cast(witness_offset); const uint32_t public_input_start_idx = key_hash_start_idx + 1; - const uint32_t output_aggregation_object_start_idx = - static_cast(public_input_start_idx + num_inner_public_inputs + (has_nested_proof ? 16 : 0)); - const uint32_t proof_indices_start_idx = output_aggregation_object_start_idx + 16; + const uint32_t proof_indices_start_idx = + static_cast(public_input_start_idx + num_inner_public_inputs - + (has_nested_proof ? RecursionConstraint::AGGREGATION_OBJECT_SIZE : 0)); const uint32_t key_indices_start_idx = static_cast(proof_indices_start_idx + proof_witnesses.size()); std::vector proof_indices; std::vector key_indices; std::vector inner_public_inputs; - std::array input_aggregation_object = {}; - std::array nested_aggregation_object = {}; - if (has_input_aggregation_object) { - input_aggregation_object = output_aggregation_object; - } - for (size_t i = 0; i < 16; ++i) { - output_aggregation_object[i] = (static_cast(i + output_aggregation_object_start_idx)); - } - if (has_nested_proof) { - for (size_t i = 0; i < 16; ++i) { - nested_aggregation_object[i] = inner_circuit.recursive_proof_public_input_indices[i]; - } - } for (size_t i = 0; i < proof_witnesses.size(); ++i) { proof_indices.emplace_back(static_cast(i + proof_indices_start_idx)); } @@ -203,6 +192,10 @@ Builder create_outer_circuit(std::vector& inner_circuits) for (size_t i = 0; i < num_inner_public_inputs; ++i) { inner_public_inputs.push_back(static_cast(i + public_input_start_idx)); } + } else { + for (size_t i = 0; i < num_inner_public_inputs - RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) { + inner_public_inputs.push_back(static_cast(i + public_input_start_idx)); + } } RecursionConstraint recursion_constraint{ @@ -234,10 +227,13 @@ Builder create_outer_circuit(std::vector& inner_circuits) for (size_t i = 0; i < num_inner_public_inputs; ++i) { witness[inner_public_inputs[i]] = inner_public_input_values[i]; } + } else { + for (size_t i = 0; i < num_inner_public_inputs - RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) { + witness[inner_public_inputs[i]] = inner_public_input_values[i]; + } } witness_offset = key_indices_start_idx + key_witnesses.size(); - circuit_idx++; } AcirFormat constraint_system{ .varnum = static_cast(witness.size()), @@ -262,6 +258,7 @@ Builder create_outer_circuit(std::vector& inner_circuits) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = recursion_constraints, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp index 5af032bedd1..82fda6f8d81 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp @@ -53,6 +53,7 @@ TEST_F(Sha256Tests, TestSha256Compression) .multi_scalar_mul_constraints = {}, .ec_add_constraints = {}, .recursion_constraints = {}, + .honk_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp index 2fc3572cec3..e015988c5c3 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp @@ -48,6 +48,11 @@ template class bigfield { field_t element; uint256_t maximum_value; }; + static constexpr size_t NUM_LIMBS = 4; + + Builder* context; + mutable Limb binary_basis_limbs[NUM_LIMBS]; + mutable field_t prime_basis_limb; bigfield(const field_t& low_bits, const field_t& high_bits, @@ -121,11 +126,11 @@ template class bigfield { static constexpr uint256_t DEFAULT_MAXIMUM_LIMB = (uint256_t(1) << NUM_LIMB_BITS) - uint256_t(1); static constexpr uint256_t DEFAULT_MAXIMUM_MOST_SIGNIFICANT_LIMB = (uint256_t(1) << NUM_LAST_LIMB_BITS) - uint256_t(1); - static constexpr uint64_t LOG2_BINARY_MODULUS = NUM_LIMB_BITS * 4; + static constexpr uint64_t LOG2_BINARY_MODULUS = NUM_LIMB_BITS * NUM_LIMBS; static constexpr bool is_composite = true; // false only when fr is native static constexpr uint256_t prime_basis_maximum_limb = - uint256_t(modulus_u512.slice(NUM_LIMB_BITS * 3, NUM_LIMB_BITS * 4)); + uint256_t(modulus_u512.slice(NUM_LIMB_BITS * (NUM_LIMBS - 1), NUM_LIMB_BITS* NUM_LIMBS)); static constexpr Basis prime_basis{ uint512_t(bb::fr::modulus), bb::fr::modulus.get_msb() + 1 }; static constexpr Basis binary_basis{ uint512_t(1) << LOG2_BINARY_MODULUS, LOG2_BINARY_MODULUS }; static constexpr Basis target_basis{ modulus_u512, modulus_u512.get_msb() + 1 }; @@ -136,13 +141,13 @@ template class bigfield { static constexpr bb::fr shift_right_2 = bb::fr(1) / shift_2; static constexpr bb::fr negative_prime_modulus_mod_binary_basis = -bb::fr(uint256_t(modulus_u512)); static constexpr uint512_t negative_prime_modulus = binary_basis.modulus - target_basis.modulus; - static constexpr uint256_t neg_modulus_limbs_u256[4]{ + static constexpr uint256_t neg_modulus_limbs_u256[NUM_LIMBS]{ uint256_t(negative_prime_modulus.slice(0, NUM_LIMB_BITS).lo), uint256_t(negative_prime_modulus.slice(NUM_LIMB_BITS, NUM_LIMB_BITS * 2).lo), uint256_t(negative_prime_modulus.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 3).lo), uint256_t(negative_prime_modulus.slice(NUM_LIMB_BITS * 3, NUM_LIMB_BITS * 4).lo), }; - static constexpr bb::fr neg_modulus_limbs[4]{ + static constexpr bb::fr neg_modulus_limbs[NUM_LIMBS]{ bb::fr(negative_prime_modulus.slice(0, NUM_LIMB_BITS).lo), bb::fr(negative_prime_modulus.slice(NUM_LIMB_BITS, NUM_LIMB_BITS * 2).lo), bb::fr(negative_prime_modulus.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 3).lo), @@ -428,9 +433,6 @@ template class bigfield { static constexpr uint256_t get_maximum_unreduced_limb_value() { return uint256_t(1) << MAX_UNREDUCED_LIMB_SIZE; } static_assert(MAX_UNREDUCED_LIMB_SIZE < (NUM_LIMB_BITS * 2)); - Builder* context; - mutable Limb binary_basis_limbs[4]; - mutable field_t prime_basis_limb; private: static std::pair compute_quotient_remainder_values(const bigfield& a, From d1095f60bbd05d35748dc9b0188ad0c5f87390f5 Mon Sep 17 00:00:00 2001 From: ludamad Date: Fri, 17 May 2024 22:06:16 -0400 Subject: [PATCH 4/9] fix(ci): ARM (#6521) Accidentally had it still targeting personal x86 --- .github/workflows/ci-arm.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-arm.yml b/.github/workflows/ci-arm.yml index 2828c3d3289..6ddc01acf9a 100644 --- a/.github/workflows/ci-arm.yml +++ b/.github/workflows/ci-arm.yml @@ -1,7 +1,9 @@ name: CI (ARM) on: push: - branches: [disabled] + branches: + - master + - '*/*arm-build' workflow_dispatch: inputs: {} concurrency: @@ -12,7 +14,7 @@ env: DOCKERHUB_PASSWORD: "${{ secrets.DOCKERHUB_PASSWORD }}" RUN_ID: ${{ github.run_id }} RUN_ATTEMPT: ${{ github.run_attempt }} - USERNAME: ${{ github.event.pull_request.user.login || github.actor }} + USERNAME: master GITHUB_TOKEN: ${{ github.token }} GH_SELF_HOSTED_RUNNER_TOKEN: ${{ secrets.GH_SELF_HOSTED_RUNNER_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} @@ -31,7 +33,7 @@ jobs: timeout-minutes: 40 uses: ./.github/ensure-builder with: - runner_type: builder-x86 + runner_type: builder-arm run: | set -eux git submodule update --init --recursive --recommend-shallow @@ -46,11 +48,12 @@ jobs: - uses: actions/checkout@v4 with: { ref: "${{ github.event.pull_request.head.sha }}" } - name: "Test" - timeout-minutes: 15 + timeout-minutes: 25 uses: ./.github/ensure-builder with: - runner_type: builder-x86 + runner_type: builder-arm run: | + sudo shutdown -P 25 # hack until core part of the scripts set -eux echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u aztecprotocolci --password-stdin scripts/earthly-ci -P --no-output ./yarn-project/end-to-end/+uniswap-trade-on-l1-from-l2 From 90ad7b21736dde8428de19612e1a075ce233ea18 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Sat, 18 May 2024 02:12:33 +0000 Subject: [PATCH 5/9] git subrepo push --branch=master barretenberg subrepo: subdir: "barretenberg" merged: "89a3cfe088" upstream: origin: "https://github.com/AztecProtocol/barretenberg" branch: "master" commit: "89a3cfe088" git-subrepo: version: "0.4.6" origin: "???" commit: "???" [skip ci] --- barretenberg/.gitrepo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/barretenberg/.gitrepo b/barretenberg/.gitrepo index 99d7eb63fb1..19b56482828 100644 --- a/barretenberg/.gitrepo +++ b/barretenberg/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/AztecProtocol/barretenberg branch = master - commit = af5ae45393b9dba4c77626c5f9591d53897ae6f2 - parent = 1fa59637a0829208d382d1dded36df33f4d61582 + commit = 89a3cfe088ed6be4e6dbe66870000d3ddc369daf + parent = d1095f60bbd05d35748dc9b0188ad0c5f87390f5 method = merge cmdver = 0.4.6 From 9f04bfea51a0c4fe980bbbcde5867089e8f5d8a5 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Sat, 18 May 2024 02:13:01 +0000 Subject: [PATCH 6/9] chore: replace relative paths to noir-protocol-circuits --- noir-projects/aztec-nr/aztec/Nargo.toml | 2 +- noir-projects/aztec-nr/tests/Nargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/Nargo.toml b/noir-projects/aztec-nr/aztec/Nargo.toml index 7a1f1af5863..834682e92cf 100644 --- a/noir-projects/aztec-nr/aztec/Nargo.toml +++ b/noir-projects/aztec-nr/aztec/Nargo.toml @@ -5,4 +5,4 @@ compiler_version = ">=0.18.0" type = "lib" [dependencies] -protocol_types = { path = "../../noir-protocol-circuits/crates/types" } +protocol_types = { git="https://github.com/AztecProtocol/aztec-packages", tag="aztec-packages-v0.40.1", directory="noir-projects/noir-protocol-circuits/crates/types" } diff --git a/noir-projects/aztec-nr/tests/Nargo.toml b/noir-projects/aztec-nr/tests/Nargo.toml index 13404b37324..312114ad930 100644 --- a/noir-projects/aztec-nr/tests/Nargo.toml +++ b/noir-projects/aztec-nr/tests/Nargo.toml @@ -6,4 +6,4 @@ type = "lib" [dependencies] aztec = { path = "../aztec" } -protocol_types = { path = "../../noir-protocol-circuits/crates/types" } +protocol_types = { git="https://github.com/AztecProtocol/aztec-packages", tag="aztec-packages-v0.40.1", directory="noir-projects/noir-protocol-circuits/crates/types" } From 79afaa4b3cc2e35cb8b0c49d3ca85f34434d766e Mon Sep 17 00:00:00 2001 From: AztecBot Date: Sat, 18 May 2024 02:13:01 +0000 Subject: [PATCH 7/9] git_subrepo.sh: Fix parent in .gitrepo file. [skip ci] --- noir-projects/aztec-nr/.gitrepo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir-projects/aztec-nr/.gitrepo b/noir-projects/aztec-nr/.gitrepo index 543deac1913..3d3a19ef2f1 100644 --- a/noir-projects/aztec-nr/.gitrepo +++ b/noir-projects/aztec-nr/.gitrepo @@ -9,4 +9,4 @@ commit = 5a57900bc92e9fa056db8a689d5622e86a15211b method = merge cmdver = 0.4.6 - parent = 3809e3d58a55d189de14f2c002bb1e0c985cb111 + parent = 8f75b8460048dfadf0097aaf323c047be9e88966 From 2202e8d170b26950c83be6e3952f44cb4eb58533 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Sat, 18 May 2024 02:13:04 +0000 Subject: [PATCH 8/9] git subrepo push --branch=master noir-projects/aztec-nr subrepo: subdir: "noir-projects/aztec-nr" merged: "440a187b5" upstream: origin: "https://github.com/AztecProtocol/aztec-nr" branch: "master" commit: "440a187b5" git-subrepo: version: "0.4.6" origin: "???" commit: "???" [skip ci] --- noir-projects/aztec-nr/.gitrepo | 4 ++-- noir-projects/aztec-nr/aztec/Nargo.toml | 2 +- noir-projects/aztec-nr/tests/Nargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/noir-projects/aztec-nr/.gitrepo b/noir-projects/aztec-nr/.gitrepo index 3d3a19ef2f1..7ab6c0eb1c9 100644 --- a/noir-projects/aztec-nr/.gitrepo +++ b/noir-projects/aztec-nr/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/AztecProtocol/aztec-nr branch = master - commit = 5a57900bc92e9fa056db8a689d5622e86a15211b + commit = 440a187b5bb985f3da4ff563d6c4943c88556fa7 method = merge cmdver = 0.4.6 - parent = 8f75b8460048dfadf0097aaf323c047be9e88966 + parent = 18bb55229dd973f6a93a4314f7519d45d3b21b1b diff --git a/noir-projects/aztec-nr/aztec/Nargo.toml b/noir-projects/aztec-nr/aztec/Nargo.toml index 834682e92cf..7a1f1af5863 100644 --- a/noir-projects/aztec-nr/aztec/Nargo.toml +++ b/noir-projects/aztec-nr/aztec/Nargo.toml @@ -5,4 +5,4 @@ compiler_version = ">=0.18.0" type = "lib" [dependencies] -protocol_types = { git="https://github.com/AztecProtocol/aztec-packages", tag="aztec-packages-v0.40.1", directory="noir-projects/noir-protocol-circuits/crates/types" } +protocol_types = { path = "../../noir-protocol-circuits/crates/types" } diff --git a/noir-projects/aztec-nr/tests/Nargo.toml b/noir-projects/aztec-nr/tests/Nargo.toml index 312114ad930..13404b37324 100644 --- a/noir-projects/aztec-nr/tests/Nargo.toml +++ b/noir-projects/aztec-nr/tests/Nargo.toml @@ -6,4 +6,4 @@ type = "lib" [dependencies] aztec = { path = "../aztec" } -protocol_types = { git="https://github.com/AztecProtocol/aztec-packages", tag="aztec-packages-v0.40.1", directory="noir-projects/noir-protocol-circuits/crates/types" } +protocol_types = { path = "../../noir-protocol-circuits/crates/types" } From e5cc9dccb6c36159ad90068d41786c8715af66da Mon Sep 17 00:00:00 2001 From: Lasse Herskind <16536249+LHerskind@users.noreply.github.com> Date: Sat, 18 May 2024 22:27:44 +0100 Subject: [PATCH 9/9] feat: Update the encrypted note log format (#6411) Fixes #5901. Uses the new format to match the keys and logs spec. The current implementation here is still using a typescript implementation to encrypt the data, mainly using the new flow. The intention is that #6408 and #1139 can be addresses separately and than we can just replace the import to use the constrained version instead of the oracle at that point. Note, that the outgoing logs are currently less than meaningful, as some of the infrastructure is not yet in place to handle those nicely, see #6410 for more on that. What was called the encrypted_log_payload in #6348 have been moved into the l1_payload, to better integrate with the rest of the setup. --- l1-contracts/test/fixtures/empty_block_0.json | 10 +- l1-contracts/test/fixtures/empty_block_1.json | 14 +- l1-contracts/test/fixtures/mixed_block_0.json | 10 +- l1-contracts/test/fixtures/mixed_block_1.json | 14 +- .../aztec-nr/address-note/src/address_note.nr | 2 +- .../aztec/src/context/private_context.nr | 69 +++--- noir-projects/aztec-nr/aztec/src/hash.nr | 56 ----- .../aztec-nr/aztec/src/oracle/logs.nr | 46 ++-- .../aztec-nr/aztec/src/oracle/logs_traits.nr | 52 ++--- .../aztec-nr/value-note/src/value_note.nr | 2 +- .../src/subscription_note.nr | 2 +- .../src/types/card_note.nr | 2 +- .../src/ecdsa_public_key_note.nr | 4 +- .../src/public_key_note.nr | 2 +- .../src/types/token_note.nr | 2 +- .../token_contract/src/types/token_note.nr | 2 +- .../src/logs/encrypted_log_payload.test.ts | 42 ---- .../src/logs/encrypted_log_payload.ts | 209 ------------------ yarn-project/circuit-types/src/logs/index.ts | 6 +- .../l1_note_payload/encrypt_buffer.test.ts | 3 +- .../logs/l1_note_payload/encrypt_buffer.ts | 24 +- .../encrypted_log_header.test.ts | 0 .../encrypted_log_header.ts | 2 +- .../encrypted_log_incoming_body.test.ts | 2 +- .../encrypted_log_incoming_body.ts | 3 +- .../encrypted_log_outgoing_body.test.ts | 0 .../encrypted_log_outgoing_body.ts | 0 .../logs/l1_note_payload/encryption_utils.ts | 24 ++ .../l1_note_payload/l1_note_payload.test.ts | 49 ++-- .../logs/l1_note_payload/l1_note_payload.ts | 167 ++++++++++++-- .../logs/l1_note_payload/tagged_note.test.ts | 54 +++-- .../src/logs/l1_note_payload/tagged_note.ts | 77 ++++--- .../src/benchmarks/bench_tx_size_fees.test.ts | 12 +- .../end-to-end/src/e2e_block_building.test.ts | 2 +- .../src/orchestrator/orchestrator.ts | 1 + .../src/note_processor/note_processor.test.ts | 12 +- .../pxe/src/note_processor/note_processor.ts | 3 +- .../simulator/src/acvm/oracle/oracle.ts | 36 +-- .../simulator/src/acvm/oracle/typed_oracle.ts | 15 +- .../src/client/client_execution_context.ts | 81 +++---- 40 files changed, 505 insertions(+), 608 deletions(-) delete mode 100644 yarn-project/circuit-types/src/logs/encrypted_log_payload.test.ts delete mode 100644 yarn-project/circuit-types/src/logs/encrypted_log_payload.ts rename yarn-project/circuit-types/src/logs/{ => l1_note_payload}/encrypted_log_header.test.ts (100%) rename yarn-project/circuit-types/src/logs/{ => l1_note_payload}/encrypted_log_header.ts (96%) rename yarn-project/circuit-types/src/logs/{ => l1_note_payload}/encrypted_log_incoming_body.test.ts (97%) rename yarn-project/circuit-types/src/logs/{ => l1_note_payload}/encrypted_log_incoming_body.ts (97%) rename yarn-project/circuit-types/src/logs/{ => l1_note_payload}/encrypted_log_outgoing_body.test.ts (100%) rename yarn-project/circuit-types/src/logs/{ => l1_note_payload}/encrypted_log_outgoing_body.ts (100%) create mode 100644 yarn-project/circuit-types/src/logs/l1_note_payload/encryption_utils.ts diff --git a/l1-contracts/test/fixtures/empty_block_0.json b/l1-contracts/test/fixtures/empty_block_0.json index aacfd5c955c..91216c12952 100644 --- a/l1-contracts/test/fixtures/empty_block_0.json +++ b/l1-contracts/test/fixtures/empty_block_0.json @@ -8,7 +8,7 @@ "l2ToL1Messages": [] }, "block": { - "archive": "0x12a6236f076e51298ca7c5c4d0c9898239c5f829e1f2673a18a922d5ee50a4fd", + "archive": "0x0ed815d35918f1aabf391fa174a1d95476da157e40b7fc581b2c8f4cfd23e6b3", "body": "0x00000000", "txsEffectsHash": "0x002676dbd818b1ba16e11597cb5c07b06aa7771127b02a77d0c3a6039bb9fef1", "decodedHeader": { @@ -23,8 +23,8 @@ "chainId": 31337, "timestamp": 0, "version": 1, - "coinbase": "0xf98794b6b717c6c7d6806a8ebb8cb1327144f0c7", - "feeRecipient": "0x1eece2f228c0b199fee7bb461e152e69a6ddd096573bd8ea45a7df0e105439a4", + "coinbase": "0xbe70f89d75a00bd140342ebc63beb517cf9735bc", + "feeRecipient": "0x08eb6120958820f4b4fd61a9bcaa32c33349663034e1db315ba57c67d155b172", "gasFees": { "feePerDaGas": 0, "feePerL2Gas": 0 @@ -55,8 +55,8 @@ } } }, - "header": "0x067a48e3140b6f15d71751ededfa0cccde3d436bb71aa7fec226b0bfe51dc5cf000000010000000000000000000000000000000000000000000000000000000000000001002676dbd818b1ba16e11597cb5c07b06aa7771127b02a77d0c3a6039bb9fef100089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c0007638bb56b6dda2b64b8f76841114ac3a87a1820030e2e16772c4d294879c31864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f800000001016642d9ccd8346c403aa4c3fa451178b22534a27035cdaa6ec34ae53b29c50cb000000800bcfa3e9f1a8922ee92c6dc964d6595907c1804a86753774322b468f69d4f278000001000572c8db882674dd026b8877fbba1b700a4407da3ae9ce5fa43215a28163362b000000800000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000f98794b6b717c6c7d6806a8ebb8cb1327144f0c71eece2f228c0b199fee7bb461e152e69a6ddd096573bd8ea45a7df0e105439a400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "publicInputsHash": "0x0081ff51b8e6caf79d8d0616b407a5cf7c3d939bf568a94100d6e7b5dbaf2cff", + "header": "0x067a48e3140b6f15d71751ededfa0cccde3d436bb71aa7fec226b0bfe51dc5cf000000010000000000000000000000000000000000000000000000000000000000000001002676dbd818b1ba16e11597cb5c07b06aa7771127b02a77d0c3a6039bb9fef100089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c0007638bb56b6dda2b64b8f76841114ac3a87a1820030e2e16772c4d294879c31864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f800000001016642d9ccd8346c403aa4c3fa451178b22534a27035cdaa6ec34ae53b29c50cb000000800bcfa3e9f1a8922ee92c6dc964d6595907c1804a86753774322b468f69d4f278000001000572c8db882674dd026b8877fbba1b700a4407da3ae9ce5fa43215a28163362b000000800000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000be70f89d75a00bd140342ebc63beb517cf9735bc08eb6120958820f4b4fd61a9bcaa32c33349663034e1db315ba57c67d155b17200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "publicInputsHash": "0x00e9c95b88dbc49351e6a0a9b660918147d3c69410df4fba105c047a4f253a47", "numTxs": 0 } } \ No newline at end of file diff --git a/l1-contracts/test/fixtures/empty_block_1.json b/l1-contracts/test/fixtures/empty_block_1.json index c2c09d34ff9..0596ade5a0a 100644 --- a/l1-contracts/test/fixtures/empty_block_1.json +++ b/l1-contracts/test/fixtures/empty_block_1.json @@ -8,7 +8,7 @@ "l2ToL1Messages": [] }, "block": { - "archive": "0x19d445841fdaa62cfa9752aae068322e538729535b9fc4e195fd4e7b010f2e91", + "archive": "0x18c171439670152671eb523cdf11eb61a45c27b7685ad86a7229fbe635e9ea18", "body": "0x00000000", "txsEffectsHash": "0x002676dbd818b1ba16e11597cb5c07b06aa7771127b02a77d0c3a6039bb9fef1", "decodedHeader": { @@ -21,10 +21,10 @@ "globalVariables": { "blockNumber": 2, "chainId": 31337, - "timestamp": 1715940661, + "timestamp": 1716042415, "version": 1, - "coinbase": "0xf98794b6b717c6c7d6806a8ebb8cb1327144f0c7", - "feeRecipient": "0x1eece2f228c0b199fee7bb461e152e69a6ddd096573bd8ea45a7df0e105439a4", + "coinbase": "0xbe70f89d75a00bd140342ebc63beb517cf9735bc", + "feeRecipient": "0x08eb6120958820f4b4fd61a9bcaa32c33349663034e1db315ba57c67d155b172", "gasFees": { "feePerDaGas": 0, "feePerL2Gas": 0 @@ -32,7 +32,7 @@ }, "lastArchive": { "nextAvailableLeafIndex": 2, - "root": "0x12a6236f076e51298ca7c5c4d0c9898239c5f829e1f2673a18a922d5ee50a4fd" + "root": "0x0ed815d35918f1aabf391fa174a1d95476da157e40b7fc581b2c8f4cfd23e6b3" }, "stateReference": { "l1ToL2MessageTree": { @@ -55,8 +55,8 @@ } } }, - "header": "0x12a6236f076e51298ca7c5c4d0c9898239c5f829e1f2673a18a922d5ee50a4fd000000020000000000000000000000000000000000000000000000000000000000000001002676dbd818b1ba16e11597cb5c07b06aa7771127b02a77d0c3a6039bb9fef100089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c0007638bb56b6dda2b64b8f76841114ac3a87a1820030e2e16772c4d294879c31864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f800000002016642d9ccd8346c403aa4c3fa451178b22534a27035cdaa6ec34ae53b29c50cb000001000bcfa3e9f1a8922ee92c6dc964d6595907c1804a86753774322b468f69d4f278000001800572c8db882674dd026b8877fbba1b700a4407da3ae9ce5fa43215a28163362b000000c00000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000066472d35f98794b6b717c6c7d6806a8ebb8cb1327144f0c71eece2f228c0b199fee7bb461e152e69a6ddd096573bd8ea45a7df0e105439a400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "publicInputsHash": "0x008568ca7fc6464f9cb6588e7215e14e0eb49c96dd210a849a0d3369d185c261", + "header": "0x0ed815d35918f1aabf391fa174a1d95476da157e40b7fc581b2c8f4cfd23e6b3000000020000000000000000000000000000000000000000000000000000000000000001002676dbd818b1ba16e11597cb5c07b06aa7771127b02a77d0c3a6039bb9fef100089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c0007638bb56b6dda2b64b8f76841114ac3a87a1820030e2e16772c4d294879c31864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f800000002016642d9ccd8346c403aa4c3fa451178b22534a27035cdaa6ec34ae53b29c50cb000001000bcfa3e9f1a8922ee92c6dc964d6595907c1804a86753774322b468f69d4f278000001800572c8db882674dd026b8877fbba1b700a4407da3ae9ce5fa43215a28163362b000000c00000000000000000000000000000000000000000000000000000000000007a6900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000006648baafbe70f89d75a00bd140342ebc63beb517cf9735bc08eb6120958820f4b4fd61a9bcaa32c33349663034e1db315ba57c67d155b17200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "publicInputsHash": "0x0033d4785f37dafeeb7e50eeef42002f0af7d1d62913119754b81cfe7ebc5217", "numTxs": 0 } } \ No newline at end of file diff --git a/l1-contracts/test/fixtures/mixed_block_0.json b/l1-contracts/test/fixtures/mixed_block_0.json index b2d7765f5a4..48f9c97bf8f 100644 --- a/l1-contracts/test/fixtures/mixed_block_0.json +++ b/l1-contracts/test/fixtures/mixed_block_0.json @@ -34,7 +34,7 @@ ] }, "block": { - "archive": "0x1da7e3994972b8e4d8f2dffeb084976254aebcca1a429e576eea74eae6ae20c4", + "archive": "0x21a504c1644ee56efe1d5c7690d6edc47b21a389e718f2202bcdc6ea4f879b0e", "body": "0x00000004000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014100000000000000000000000000000000000000000000000000000000000001420000000000000000000000000000000000000000000000000000000000000143000000000000000000000000000000000000000000000000000000000000014400000000000000000000000000000000000000000000000000000000000001450000000000000000000000000000000000000000000000000000000000000146000000000000000000000000000000000000000000000000000000000000014700000000000000000000000000000000000000000000000000000000000001480000000000000000000000000000000000000000000000000000000000000149000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014b000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000014d000000000000000000000000000000000000000000000000000000000000014e000000000000000000000000000000000000000000000000000000000000014f0000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000015100000000000000000000000000000000000000000000000000000000000001520000000000000000000000000000000000000000000000000000000000000153000000000000000000000000000000000000000000000000000000000000015400000000000000000000000000000000000000000000000000000000000001550000000000000000000000000000000000000000000000000000000000000156000000000000000000000000000000000000000000000000000000000000015700000000000000000000000000000000000000000000000000000000000001580000000000000000000000000000000000000000000000000000000000000159000000000000000000000000000000000000000000000000000000000000015a000000000000000000000000000000000000000000000000000000000000015b000000000000000000000000000000000000000000000000000000000000015c000000000000000000000000000000000000000000000000000000000000015d000000000000000000000000000000000000000000000000000000000000015e000000000000000000000000000000000000000000000000000000000000015f0000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000001620000000000000000000000000000000000000000000000000000000000000163000000000000000000000000000000000000000000000000000000000000016400000000000000000000000000000000000000000000000000000000000001650000000000000000000000000000000000000000000000000000000000000166000000000000000000000000000000000000000000000000000000000000016700000000000000000000000000000000000000000000000000000000000001680000000000000000000000000000000000000000000000000000000000000169000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016b000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000000016d000000000000000000000000000000000000000000000000000000000000016e000000000000000000000000000000000000000000000000000000000000016f0000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000017100000000000000000000000000000000000000000000000000000000000001720000000000000000000000000000000000000000000000000000000000000173000000000000000000000000000000000000000000000000000000000000017400000000000000000000000000000000000000000000000000000000000001750000000000000000000000000000000000000000000000000000000000000176000000000000000000000000000000000000000000000000000000000000017700000000000000000000000000000000000000000000000000000000000001780000000000000000000000000000000000000000000000000000000000000179000000000000000000000000000000000000000000000000000000000000017a000000000000000000000000000000000000000000000000000000000000017b000000000000000000000000000000000000000000000000000000000000017c000000000000000000000000000000000000000000000000000000000000017d000000000000000000000000000000000000000000000000000000000000017e000000000000000000000000000000000000000000000000000000000000017f3f0000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000024100000000000000000000000000000000000000000000000000000000000002420000000000000000000000000000000000000000000000000000000000000243000000000000000000000000000000000000000000000000000000000000024400000000000000000000000000000000000000000000000000000000000002450000000000000000000000000000000000000000000000000000000000000246000000000000000000000000000000000000000000000000000000000000024700000000000000000000000000000000000000000000000000000000000002480000000000000000000000000000000000000000000000000000000000000249000000000000000000000000000000000000000000000000000000000000024a000000000000000000000000000000000000000000000000000000000000024b000000000000000000000000000000000000000000000000000000000000024c000000000000000000000000000000000000000000000000000000000000024d000000000000000000000000000000000000000000000000000000000000024e000000000000000000000000000000000000000000000000000000000000024f0000000000000000000000000000000000000000000000000000000000000250000000000000000000000000000000000000000000000000000000000000025100000000000000000000000000000000000000000000000000000000000002520000000000000000000000000000000000000000000000000000000000000253000000000000000000000000000000000000000000000000000000000000025400000000000000000000000000000000000000000000000000000000000002550000000000000000000000000000000000000000000000000000000000000256000000000000000000000000000000000000000000000000000000000000025700000000000000000000000000000000000000000000000000000000000002580000000000000000000000000000000000000000000000000000000000000259000000000000000000000000000000000000000000000000000000000000025a000000000000000000000000000000000000000000000000000000000000025b000000000000000000000000000000000000000000000000000000000000025c000000000000000000000000000000000000000000000000000000000000025d000000000000000000000000000000000000000000000000000000000000025e000000000000000000000000000000000000000000000000000000000000025f0000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000026100000000000000000000000000000000000000000000000000000000000002620000000000000000000000000000000000000000000000000000000000000263000000000000000000000000000000000000000000000000000000000000026400000000000000000000000000000000000000000000000000000000000002650000000000000000000000000000000000000000000000000000000000000266000000000000000000000000000000000000000000000000000000000000026700000000000000000000000000000000000000000000000000000000000002680000000000000000000000000000000000000000000000000000000000000269000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000026b000000000000000000000000000000000000000000000000000000000000026c000000000000000000000000000000000000000000000000000000000000026d000000000000000000000000000000000000000000000000000000000000026e000000000000000000000000000000000000000000000000000000000000026f0000000000000000000000000000000000000000000000000000000000000270000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000002720000000000000000000000000000000000000000000000000000000000000273000000000000000000000000000000000000000000000000000000000000027400000000000000000000000000000000000000000000000000000000000002750000000000000000000000000000000000000000000000000000000000000276000000000000000000000000000000000000000000000000000000000000027700000000000000000000000000000000000000000000000000000000000002780000000000000000000000000000000000000000000000000000000000000279000000000000000000000000000000000000000000000000000000000000027a000000000000000000000000000000000000000000000000000000000000027b000000000000000000000000000000000000000000000000000000000000027c000000000000000000000000000000000000000000000000000000000000027d000000000000000000000000000000000000000000000000000000000000027e0200000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000341200000000000000000000000000000000000000000000000000000000000000540000000000000000000000000000000000000000000000000000000000000054a0000000000000000000000000000000000000000000000000000000000000541000000000000000000000000000000000000000000000000000000000000054b0000000000000000000000000000000000000000000000000000000000000542000000000000000000000000000000000000000000000000000000000000054c0000000000000000000000000000000000000000000000000000000000000543000000000000000000000000000000000000000000000000000000000000054d0000000000000000000000000000000000000000000000000000000000000544000000000000000000000000000000000000000000000000000000000000054e0000000000000000000000000000000000000000000000000000000000000545000000000000000000000000000000000000000000000000000000000000054f00000000000000000000000000000000000000000000000000000000000005460000000000000000000000000000000000000000000000000000000000000550000000000000000000000000000000000000000000000000000000000000054700000000000000000000000000000000000000000000000000000000000005510000000000000000000000000000000000000000000000000000000000000548000000000000000000000000000000000000000000000000000000000000055200000000000000000000000000000000000000000000000000000000000005490000000000000000000000000000000000000000000000000000000000000553000000000000000000000000000000000000000000000000000000000000054a0000000000000000000000000000000000000000000000000000000000000554000000000000000000000000000000000000000000000000000000000000054b0000000000000000000000000000000000000000000000000000000000000555000000000000000000000000000000000000000000000000000000000000054c0000000000000000000000000000000000000000000000000000000000000556000000000000000000000000000000000000000000000000000000000000054d0000000000000000000000000000000000000000000000000000000000000557000000000000000000000000000000000000000000000000000000000000054e0000000000000000000000000000000000000000000000000000000000000558000000000000000000000000000000000000000000000000000000000000054f00000000000000000000000000000000000000000000000000000000000005590000000000000000000000000000000000000000000000000000000000000550000000000000000000000000000000000000000000000000000000000000055a0000000000000000000000000000000000000000000000000000000000000551000000000000000000000000000000000000000000000000000000000000055b0000000000000000000000000000000000000000000000000000000000000552000000000000000000000000000000000000000000000000000000000000055c0000000000000000000000000000000000000000000000000000000000000553000000000000000000000000000000000000000000000000000000000000055d0000000000000000000000000000000000000000000000000000000000000554000000000000000000000000000000000000000000000000000000000000055e0000000000000000000000000000000000000000000000000000000000000555000000000000000000000000000000000000000000000000000000000000055f00000000000000000000000000000000000000000000000000000000000005560000000000000000000000000000000000000000000000000000000000000560000000000000000000000000000000000000000000000000000000000000055700000000000000000000000000000000000000000000000000000000000005610000000000000000000000000000000000000000000000000000000000000558000000000000000000000000000000000000000000000000000000000000056200000000000000000000000000000000000000000000000000000000000005590000000000000000000000000000000000000000000000000000000000000563000000000000000000000000000000000000000000000000000000000000055a0000000000000000000000000000000000000000000000000000000000000564000000000000000000000000000000000000000000000000000000000000055b0000000000000000000000000000000000000000000000000000000000000565000000000000000000000000000000000000000000000000000000000000055c0000000000000000000000000000000000000000000000000000000000000566000000000000000000000000000000000000000000000000000000000000055d0000000000000000000000000000000000000000000000000000000000000567000000000000000000000000000000000000000000000000000000000000055e0000000000000000000000000000000000000000000000000000000000000568000000000000000000000000000000000000000000000000000000000000055f000000000000000000000000000000000000000000000000000000000000056900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000018100000000000000000000000000000000000000000000000000000000000001820000000000000000000000000000000000000000000000000000000000000183000000000000000000000000000000000000000000000000000000000000018400000000000000000000000000000000000000000000000000000000000001850000000000000000000000000000000000000000000000000000000000000186000000000000000000000000000000000000000000000000000000000000018700000000000000000000000000000000000000000000000000000000000001880000000000000000000000000000000000000000000000000000000000000189000000000000000000000000000000000000000000000000000000000000018a000000000000000000000000000000000000000000000000000000000000018b000000000000000000000000000000000000000000000000000000000000018c000000000000000000000000000000000000000000000000000000000000018d000000000000000000000000000000000000000000000000000000000000018e000000000000000000000000000000000000000000000000000000000000018f0000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000000019100000000000000000000000000000000000000000000000000000000000001920000000000000000000000000000000000000000000000000000000000000193000000000000000000000000000000000000000000000000000000000000019400000000000000000000000000000000000000000000000000000000000001950000000000000000000000000000000000000000000000000000000000000196000000000000000000000000000000000000000000000000000000000000019700000000000000000000000000000000000000000000000000000000000001980000000000000000000000000000000000000000000000000000000000000199000000000000000000000000000000000000000000000000000000000000019a000000000000000000000000000000000000000000000000000000000000019b000000000000000000000000000000000000000000000000000000000000019c000000000000000000000000000000000000000000000000000000000000019d000000000000000000000000000000000000000000000000000000000000019e000000000000000000000000000000000000000000000000000000000000019f00000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001a100000000000000000000000000000000000000000000000000000000000001a200000000000000000000000000000000000000000000000000000000000001a300000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000000001a500000000000000000000000000000000000000000000000000000000000001a600000000000000000000000000000000000000000000000000000000000001a700000000000000000000000000000000000000000000000000000000000001a800000000000000000000000000000000000000000000000000000000000001a900000000000000000000000000000000000000000000000000000000000001aa00000000000000000000000000000000000000000000000000000000000001ab00000000000000000000000000000000000000000000000000000000000001ac00000000000000000000000000000000000000000000000000000000000001ad00000000000000000000000000000000000000000000000000000000000001ae00000000000000000000000000000000000000000000000000000000000001af00000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b100000000000000000000000000000000000000000000000000000000000001b200000000000000000000000000000000000000000000000000000000000001b300000000000000000000000000000000000000000000000000000000000001b400000000000000000000000000000000000000000000000000000000000001b500000000000000000000000000000000000000000000000000000000000001b600000000000000000000000000000000000000000000000000000000000001b700000000000000000000000000000000000000000000000000000000000001b800000000000000000000000000000000000000000000000000000000000001b900000000000000000000000000000000000000000000000000000000000001ba00000000000000000000000000000000000000000000000000000000000001bb00000000000000000000000000000000000000000000000000000000000001bc00000000000000000000000000000000000000000000000000000000000001bd00000000000000000000000000000000000000000000000000000000000001be00000000000000000000000000000000000000000000000000000000000001bf3f0000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000028100000000000000000000000000000000000000000000000000000000000002820000000000000000000000000000000000000000000000000000000000000283000000000000000000000000000000000000000000000000000000000000028400000000000000000000000000000000000000000000000000000000000002850000000000000000000000000000000000000000000000000000000000000286000000000000000000000000000000000000000000000000000000000000028700000000000000000000000000000000000000000000000000000000000002880000000000000000000000000000000000000000000000000000000000000289000000000000000000000000000000000000000000000000000000000000028a000000000000000000000000000000000000000000000000000000000000028b000000000000000000000000000000000000000000000000000000000000028c000000000000000000000000000000000000000000000000000000000000028d000000000000000000000000000000000000000000000000000000000000028e000000000000000000000000000000000000000000000000000000000000028f0000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000000000000000000000000000000029100000000000000000000000000000000000000000000000000000000000002920000000000000000000000000000000000000000000000000000000000000293000000000000000000000000000000000000000000000000000000000000029400000000000000000000000000000000000000000000000000000000000002950000000000000000000000000000000000000000000000000000000000000296000000000000000000000000000000000000000000000000000000000000029700000000000000000000000000000000000000000000000000000000000002980000000000000000000000000000000000000000000000000000000000000299000000000000000000000000000000000000000000000000000000000000029a000000000000000000000000000000000000000000000000000000000000029b000000000000000000000000000000000000000000000000000000000000029c000000000000000000000000000000000000000000000000000000000000029d000000000000000000000000000000000000000000000000000000000000029e000000000000000000000000000000000000000000000000000000000000029f00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002a100000000000000000000000000000000000000000000000000000000000002a200000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000002a400000000000000000000000000000000000000000000000000000000000002a500000000000000000000000000000000000000000000000000000000000002a600000000000000000000000000000000000000000000000000000000000002a700000000000000000000000000000000000000000000000000000000000002a800000000000000000000000000000000000000000000000000000000000002a900000000000000000000000000000000000000000000000000000000000002aa00000000000000000000000000000000000000000000000000000000000002ab00000000000000000000000000000000000000000000000000000000000002ac00000000000000000000000000000000000000000000000000000000000002ad00000000000000000000000000000000000000000000000000000000000002ae00000000000000000000000000000000000000000000000000000000000002af00000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002b100000000000000000000000000000000000000000000000000000000000002b200000000000000000000000000000000000000000000000000000000000002b300000000000000000000000000000000000000000000000000000000000002b400000000000000000000000000000000000000000000000000000000000002b500000000000000000000000000000000000000000000000000000000000002b600000000000000000000000000000000000000000000000000000000000002b700000000000000000000000000000000000000000000000000000000000002b800000000000000000000000000000000000000000000000000000000000002b900000000000000000000000000000000000000000000000000000000000002ba00000000000000000000000000000000000000000000000000000000000002bb00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bd00000000000000000000000000000000000000000000000000000000000002be0200000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000381200000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000058a0000000000000000000000000000000000000000000000000000000000000581000000000000000000000000000000000000000000000000000000000000058b0000000000000000000000000000000000000000000000000000000000000582000000000000000000000000000000000000000000000000000000000000058c0000000000000000000000000000000000000000000000000000000000000583000000000000000000000000000000000000000000000000000000000000058d0000000000000000000000000000000000000000000000000000000000000584000000000000000000000000000000000000000000000000000000000000058e0000000000000000000000000000000000000000000000000000000000000585000000000000000000000000000000000000000000000000000000000000058f00000000000000000000000000000000000000000000000000000000000005860000000000000000000000000000000000000000000000000000000000000590000000000000000000000000000000000000000000000000000000000000058700000000000000000000000000000000000000000000000000000000000005910000000000000000000000000000000000000000000000000000000000000588000000000000000000000000000000000000000000000000000000000000059200000000000000000000000000000000000000000000000000000000000005890000000000000000000000000000000000000000000000000000000000000593000000000000000000000000000000000000000000000000000000000000058a0000000000000000000000000000000000000000000000000000000000000594000000000000000000000000000000000000000000000000000000000000058b0000000000000000000000000000000000000000000000000000000000000595000000000000000000000000000000000000000000000000000000000000058c0000000000000000000000000000000000000000000000000000000000000596000000000000000000000000000000000000000000000000000000000000058d0000000000000000000000000000000000000000000000000000000000000597000000000000000000000000000000000000000000000000000000000000058e0000000000000000000000000000000000000000000000000000000000000598000000000000000000000000000000000000000000000000000000000000058f00000000000000000000000000000000000000000000000000000000000005990000000000000000000000000000000000000000000000000000000000000590000000000000000000000000000000000000000000000000000000000000059a0000000000000000000000000000000000000000000000000000000000000591000000000000000000000000000000000000000000000000000000000000059b0000000000000000000000000000000000000000000000000000000000000592000000000000000000000000000000000000000000000000000000000000059c0000000000000000000000000000000000000000000000000000000000000593000000000000000000000000000000000000000000000000000000000000059d0000000000000000000000000000000000000000000000000000000000000594000000000000000000000000000000000000000000000000000000000000059e0000000000000000000000000000000000000000000000000000000000000595000000000000000000000000000000000000000000000000000000000000059f000000000000000000000000000000000000000000000000000000000000059600000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000059700000000000000000000000000000000000000000000000000000000000005a1000000000000000000000000000000000000000000000000000000000000059800000000000000000000000000000000000000000000000000000000000005a2000000000000000000000000000000000000000000000000000000000000059900000000000000000000000000000000000000000000000000000000000005a3000000000000000000000000000000000000000000000000000000000000059a00000000000000000000000000000000000000000000000000000000000005a4000000000000000000000000000000000000000000000000000000000000059b00000000000000000000000000000000000000000000000000000000000005a5000000000000000000000000000000000000000000000000000000000000059c00000000000000000000000000000000000000000000000000000000000005a6000000000000000000000000000000000000000000000000000000000000059d00000000000000000000000000000000000000000000000000000000000005a7000000000000000000000000000000000000000000000000000000000000059e00000000000000000000000000000000000000000000000000000000000005a8000000000000000000000000000000000000000000000000000000000000059f00000000000000000000000000000000000000000000000000000000000005a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001c100000000000000000000000000000000000000000000000000000000000001c200000000000000000000000000000000000000000000000000000000000001c300000000000000000000000000000000000000000000000000000000000001c400000000000000000000000000000000000000000000000000000000000001c500000000000000000000000000000000000000000000000000000000000001c600000000000000000000000000000000000000000000000000000000000001c700000000000000000000000000000000000000000000000000000000000001c800000000000000000000000000000000000000000000000000000000000001c900000000000000000000000000000000000000000000000000000000000001ca00000000000000000000000000000000000000000000000000000000000001cb00000000000000000000000000000000000000000000000000000000000001cc00000000000000000000000000000000000000000000000000000000000001cd00000000000000000000000000000000000000000000000000000000000001ce00000000000000000000000000000000000000000000000000000000000001cf00000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001d100000000000000000000000000000000000000000000000000000000000001d200000000000000000000000000000000000000000000000000000000000001d300000000000000000000000000000000000000000000000000000000000001d400000000000000000000000000000000000000000000000000000000000001d500000000000000000000000000000000000000000000000000000000000001d600000000000000000000000000000000000000000000000000000000000001d700000000000000000000000000000000000000000000000000000000000001d800000000000000000000000000000000000000000000000000000000000001d900000000000000000000000000000000000000000000000000000000000001da00000000000000000000000000000000000000000000000000000000000001db00000000000000000000000000000000000000000000000000000000000001dc00000000000000000000000000000000000000000000000000000000000001dd00000000000000000000000000000000000000000000000000000000000001de00000000000000000000000000000000000000000000000000000000000001df00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001e100000000000000000000000000000000000000000000000000000000000001e200000000000000000000000000000000000000000000000000000000000001e300000000000000000000000000000000000000000000000000000000000001e400000000000000000000000000000000000000000000000000000000000001e500000000000000000000000000000000000000000000000000000000000001e600000000000000000000000000000000000000000000000000000000000001e700000000000000000000000000000000000000000000000000000000000001e800000000000000000000000000000000000000000000000000000000000001e900000000000000000000000000000000000000000000000000000000000001ea00000000000000000000000000000000000000000000000000000000000001eb00000000000000000000000000000000000000000000000000000000000001ec00000000000000000000000000000000000000000000000000000000000001ed00000000000000000000000000000000000000000000000000000000000001ee00000000000000000000000000000000000000000000000000000000000001ef00000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000001f100000000000000000000000000000000000000000000000000000000000001f200000000000000000000000000000000000000000000000000000000000001f300000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000001f600000000000000000000000000000000000000000000000000000000000001f700000000000000000000000000000000000000000000000000000000000001f800000000000000000000000000000000000000000000000000000000000001f900000000000000000000000000000000000000000000000000000000000001fa00000000000000000000000000000000000000000000000000000000000001fb00000000000000000000000000000000000000000000000000000000000001fc00000000000000000000000000000000000000000000000000000000000001fd00000000000000000000000000000000000000000000000000000000000001fe00000000000000000000000000000000000000000000000000000000000001ff3f00000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002c100000000000000000000000000000000000000000000000000000000000002c200000000000000000000000000000000000000000000000000000000000002c300000000000000000000000000000000000000000000000000000000000002c400000000000000000000000000000000000000000000000000000000000002c500000000000000000000000000000000000000000000000000000000000002c600000000000000000000000000000000000000000000000000000000000002c700000000000000000000000000000000000000000000000000000000000002c800000000000000000000000000000000000000000000000000000000000002c900000000000000000000000000000000000000000000000000000000000002ca00000000000000000000000000000000000000000000000000000000000002cb00000000000000000000000000000000000000000000000000000000000002cc00000000000000000000000000000000000000000000000000000000000002cd00000000000000000000000000000000000000000000000000000000000002ce00000000000000000000000000000000000000000000000000000000000002cf00000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002d100000000000000000000000000000000000000000000000000000000000002d200000000000000000000000000000000000000000000000000000000000002d300000000000000000000000000000000000000000000000000000000000002d400000000000000000000000000000000000000000000000000000000000002d500000000000000000000000000000000000000000000000000000000000002d600000000000000000000000000000000000000000000000000000000000002d700000000000000000000000000000000000000000000000000000000000002d800000000000000000000000000000000000000000000000000000000000002d900000000000000000000000000000000000000000000000000000000000002da00000000000000000000000000000000000000000000000000000000000002db00000000000000000000000000000000000000000000000000000000000002dc00000000000000000000000000000000000000000000000000000000000002dd00000000000000000000000000000000000000000000000000000000000002de00000000000000000000000000000000000000000000000000000000000002df00000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002e100000000000000000000000000000000000000000000000000000000000002e200000000000000000000000000000000000000000000000000000000000002e300000000000000000000000000000000000000000000000000000000000002e400000000000000000000000000000000000000000000000000000000000002e500000000000000000000000000000000000000000000000000000000000002e600000000000000000000000000000000000000000000000000000000000002e700000000000000000000000000000000000000000000000000000000000002e800000000000000000000000000000000000000000000000000000000000002e900000000000000000000000000000000000000000000000000000000000002ea00000000000000000000000000000000000000000000000000000000000002eb00000000000000000000000000000000000000000000000000000000000002ec00000000000000000000000000000000000000000000000000000000000002ed00000000000000000000000000000000000000000000000000000000000002ee00000000000000000000000000000000000000000000000000000000000002ef00000000000000000000000000000000000000000000000000000000000002f000000000000000000000000000000000000000000000000000000000000002f100000000000000000000000000000000000000000000000000000000000002f200000000000000000000000000000000000000000000000000000000000002f300000000000000000000000000000000000000000000000000000000000002f400000000000000000000000000000000000000000000000000000000000002f500000000000000000000000000000000000000000000000000000000000002f600000000000000000000000000000000000000000000000000000000000002f700000000000000000000000000000000000000000000000000000000000002f800000000000000000000000000000000000000000000000000000000000002f900000000000000000000000000000000000000000000000000000000000002fa00000000000000000000000000000000000000000000000000000000000002fb00000000000000000000000000000000000000000000000000000000000002fc00000000000000000000000000000000000000000000000000000000000002fd00000000000000000000000000000000000000000000000000000000000002fe0200000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003c12000000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000005ca00000000000000000000000000000000000000000000000000000000000005c100000000000000000000000000000000000000000000000000000000000005cb00000000000000000000000000000000000000000000000000000000000005c200000000000000000000000000000000000000000000000000000000000005cc00000000000000000000000000000000000000000000000000000000000005c300000000000000000000000000000000000000000000000000000000000005cd00000000000000000000000000000000000000000000000000000000000005c400000000000000000000000000000000000000000000000000000000000005ce00000000000000000000000000000000000000000000000000000000000005c500000000000000000000000000000000000000000000000000000000000005cf00000000000000000000000000000000000000000000000000000000000005c600000000000000000000000000000000000000000000000000000000000005d000000000000000000000000000000000000000000000000000000000000005c700000000000000000000000000000000000000000000000000000000000005d100000000000000000000000000000000000000000000000000000000000005c800000000000000000000000000000000000000000000000000000000000005d200000000000000000000000000000000000000000000000000000000000005c900000000000000000000000000000000000000000000000000000000000005d300000000000000000000000000000000000000000000000000000000000005ca00000000000000000000000000000000000000000000000000000000000005d400000000000000000000000000000000000000000000000000000000000005cb00000000000000000000000000000000000000000000000000000000000005d500000000000000000000000000000000000000000000000000000000000005cc00000000000000000000000000000000000000000000000000000000000005d600000000000000000000000000000000000000000000000000000000000005cd00000000000000000000000000000000000000000000000000000000000005d700000000000000000000000000000000000000000000000000000000000005ce00000000000000000000000000000000000000000000000000000000000005d800000000000000000000000000000000000000000000000000000000000005cf00000000000000000000000000000000000000000000000000000000000005d900000000000000000000000000000000000000000000000000000000000005d000000000000000000000000000000000000000000000000000000000000005da00000000000000000000000000000000000000000000000000000000000005d100000000000000000000000000000000000000000000000000000000000005db00000000000000000000000000000000000000000000000000000000000005d200000000000000000000000000000000000000000000000000000000000005dc00000000000000000000000000000000000000000000000000000000000005d300000000000000000000000000000000000000000000000000000000000005dd00000000000000000000000000000000000000000000000000000000000005d400000000000000000000000000000000000000000000000000000000000005de00000000000000000000000000000000000000000000000000000000000005d500000000000000000000000000000000000000000000000000000000000005df00000000000000000000000000000000000000000000000000000000000005d600000000000000000000000000000000000000000000000000000000000005e000000000000000000000000000000000000000000000000000000000000005d700000000000000000000000000000000000000000000000000000000000005e100000000000000000000000000000000000000000000000000000000000005d800000000000000000000000000000000000000000000000000000000000005e200000000000000000000000000000000000000000000000000000000000005d900000000000000000000000000000000000000000000000000000000000005e300000000000000000000000000000000000000000000000000000000000005da00000000000000000000000000000000000000000000000000000000000005e400000000000000000000000000000000000000000000000000000000000005db00000000000000000000000000000000000000000000000000000000000005e500000000000000000000000000000000000000000000000000000000000005dc00000000000000000000000000000000000000000000000000000000000005e600000000000000000000000000000000000000000000000000000000000005dd00000000000000000000000000000000000000000000000000000000000005e700000000000000000000000000000000000000000000000000000000000005de00000000000000000000000000000000000000000000000000000000000005e800000000000000000000000000000000000000000000000000000000000005df00000000000000000000000000000000000000000000000000000000000005e900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020100000000000000000000000000000000000000000000000000000000000002020000000000000000000000000000000000000000000000000000000000000203000000000000000000000000000000000000000000000000000000000000020400000000000000000000000000000000000000000000000000000000000002050000000000000000000000000000000000000000000000000000000000000206000000000000000000000000000000000000000000000000000000000000020700000000000000000000000000000000000000000000000000000000000002080000000000000000000000000000000000000000000000000000000000000209000000000000000000000000000000000000000000000000000000000000020a000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000020c000000000000000000000000000000000000000000000000000000000000020d000000000000000000000000000000000000000000000000000000000000020e000000000000000000000000000000000000000000000000000000000000020f0000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000021100000000000000000000000000000000000000000000000000000000000002120000000000000000000000000000000000000000000000000000000000000213000000000000000000000000000000000000000000000000000000000000021400000000000000000000000000000000000000000000000000000000000002150000000000000000000000000000000000000000000000000000000000000216000000000000000000000000000000000000000000000000000000000000021700000000000000000000000000000000000000000000000000000000000002180000000000000000000000000000000000000000000000000000000000000219000000000000000000000000000000000000000000000000000000000000021a000000000000000000000000000000000000000000000000000000000000021b000000000000000000000000000000000000000000000000000000000000021c000000000000000000000000000000000000000000000000000000000000021d000000000000000000000000000000000000000000000000000000000000021e000000000000000000000000000000000000000000000000000000000000021f0000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000022100000000000000000000000000000000000000000000000000000000000002220000000000000000000000000000000000000000000000000000000000000223000000000000000000000000000000000000000000000000000000000000022400000000000000000000000000000000000000000000000000000000000002250000000000000000000000000000000000000000000000000000000000000226000000000000000000000000000000000000000000000000000000000000022700000000000000000000000000000000000000000000000000000000000002280000000000000000000000000000000000000000000000000000000000000229000000000000000000000000000000000000000000000000000000000000022a000000000000000000000000000000000000000000000000000000000000022b000000000000000000000000000000000000000000000000000000000000022c000000000000000000000000000000000000000000000000000000000000022d000000000000000000000000000000000000000000000000000000000000022e000000000000000000000000000000000000000000000000000000000000022f0000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000023100000000000000000000000000000000000000000000000000000000000002320000000000000000000000000000000000000000000000000000000000000233000000000000000000000000000000000000000000000000000000000000023400000000000000000000000000000000000000000000000000000000000002350000000000000000000000000000000000000000000000000000000000000236000000000000000000000000000000000000000000000000000000000000023700000000000000000000000000000000000000000000000000000000000002380000000000000000000000000000000000000000000000000000000000000239000000000000000000000000000000000000000000000000000000000000023a000000000000000000000000000000000000000000000000000000000000023b000000000000000000000000000000000000000000000000000000000000023c000000000000000000000000000000000000000000000000000000000000023d000000000000000000000000000000000000000000000000000000000000023e000000000000000000000000000000000000000000000000000000000000023f3f0000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000030100000000000000000000000000000000000000000000000000000000000003020000000000000000000000000000000000000000000000000000000000000303000000000000000000000000000000000000000000000000000000000000030400000000000000000000000000000000000000000000000000000000000003050000000000000000000000000000000000000000000000000000000000000306000000000000000000000000000000000000000000000000000000000000030700000000000000000000000000000000000000000000000000000000000003080000000000000000000000000000000000000000000000000000000000000309000000000000000000000000000000000000000000000000000000000000030a000000000000000000000000000000000000000000000000000000000000030b000000000000000000000000000000000000000000000000000000000000030c000000000000000000000000000000000000000000000000000000000000030d000000000000000000000000000000000000000000000000000000000000030e000000000000000000000000000000000000000000000000000000000000030f0000000000000000000000000000000000000000000000000000000000000310000000000000000000000000000000000000000000000000000000000000031100000000000000000000000000000000000000000000000000000000000003120000000000000000000000000000000000000000000000000000000000000313000000000000000000000000000000000000000000000000000000000000031400000000000000000000000000000000000000000000000000000000000003150000000000000000000000000000000000000000000000000000000000000316000000000000000000000000000000000000000000000000000000000000031700000000000000000000000000000000000000000000000000000000000003180000000000000000000000000000000000000000000000000000000000000319000000000000000000000000000000000000000000000000000000000000031a000000000000000000000000000000000000000000000000000000000000031b000000000000000000000000000000000000000000000000000000000000031c000000000000000000000000000000000000000000000000000000000000031d000000000000000000000000000000000000000000000000000000000000031e000000000000000000000000000000000000000000000000000000000000031f0000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032100000000000000000000000000000000000000000000000000000000000003220000000000000000000000000000000000000000000000000000000000000323000000000000000000000000000000000000000000000000000000000000032400000000000000000000000000000000000000000000000000000000000003250000000000000000000000000000000000000000000000000000000000000326000000000000000000000000000000000000000000000000000000000000032700000000000000000000000000000000000000000000000000000000000003280000000000000000000000000000000000000000000000000000000000000329000000000000000000000000000000000000000000000000000000000000032a000000000000000000000000000000000000000000000000000000000000032b000000000000000000000000000000000000000000000000000000000000032c000000000000000000000000000000000000000000000000000000000000032d000000000000000000000000000000000000000000000000000000000000032e000000000000000000000000000000000000000000000000000000000000032f0000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000033100000000000000000000000000000000000000000000000000000000000003320000000000000000000000000000000000000000000000000000000000000333000000000000000000000000000000000000000000000000000000000000033400000000000000000000000000000000000000000000000000000000000003350000000000000000000000000000000000000000000000000000000000000336000000000000000000000000000000000000000000000000000000000000033700000000000000000000000000000000000000000000000000000000000003380000000000000000000000000000000000000000000000000000000000000339000000000000000000000000000000000000000000000000000000000000033a000000000000000000000000000000000000000000000000000000000000033b000000000000000000000000000000000000000000000000000000000000033c000000000000000000000000000000000000000000000000000000000000033d000000000000000000000000000000000000000000000000000000000000033e0200000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000401200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060a0000000000000000000000000000000000000000000000000000000000000601000000000000000000000000000000000000000000000000000000000000060b0000000000000000000000000000000000000000000000000000000000000602000000000000000000000000000000000000000000000000000000000000060c0000000000000000000000000000000000000000000000000000000000000603000000000000000000000000000000000000000000000000000000000000060d0000000000000000000000000000000000000000000000000000000000000604000000000000000000000000000000000000000000000000000000000000060e0000000000000000000000000000000000000000000000000000000000000605000000000000000000000000000000000000000000000000000000000000060f00000000000000000000000000000000000000000000000000000000000006060000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000060700000000000000000000000000000000000000000000000000000000000006110000000000000000000000000000000000000000000000000000000000000608000000000000000000000000000000000000000000000000000000000000061200000000000000000000000000000000000000000000000000000000000006090000000000000000000000000000000000000000000000000000000000000613000000000000000000000000000000000000000000000000000000000000060a0000000000000000000000000000000000000000000000000000000000000614000000000000000000000000000000000000000000000000000000000000060b0000000000000000000000000000000000000000000000000000000000000615000000000000000000000000000000000000000000000000000000000000060c0000000000000000000000000000000000000000000000000000000000000616000000000000000000000000000000000000000000000000000000000000060d0000000000000000000000000000000000000000000000000000000000000617000000000000000000000000000000000000000000000000000000000000060e0000000000000000000000000000000000000000000000000000000000000618000000000000000000000000000000000000000000000000000000000000060f00000000000000000000000000000000000000000000000000000000000006190000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000061a0000000000000000000000000000000000000000000000000000000000000611000000000000000000000000000000000000000000000000000000000000061b0000000000000000000000000000000000000000000000000000000000000612000000000000000000000000000000000000000000000000000000000000061c0000000000000000000000000000000000000000000000000000000000000613000000000000000000000000000000000000000000000000000000000000061d0000000000000000000000000000000000000000000000000000000000000614000000000000000000000000000000000000000000000000000000000000061e0000000000000000000000000000000000000000000000000000000000000615000000000000000000000000000000000000000000000000000000000000061f00000000000000000000000000000000000000000000000000000000000006160000000000000000000000000000000000000000000000000000000000000620000000000000000000000000000000000000000000000000000000000000061700000000000000000000000000000000000000000000000000000000000006210000000000000000000000000000000000000000000000000000000000000618000000000000000000000000000000000000000000000000000000000000062200000000000000000000000000000000000000000000000000000000000006190000000000000000000000000000000000000000000000000000000000000623000000000000000000000000000000000000000000000000000000000000061a0000000000000000000000000000000000000000000000000000000000000624000000000000000000000000000000000000000000000000000000000000061b0000000000000000000000000000000000000000000000000000000000000625000000000000000000000000000000000000000000000000000000000000061c0000000000000000000000000000000000000000000000000000000000000626000000000000000000000000000000000000000000000000000000000000061d0000000000000000000000000000000000000000000000000000000000000627000000000000000000000000000000000000000000000000000000000000061e0000000000000000000000000000000000000000000000000000000000000628000000000000000000000000000000000000000000000000000000000000061f000000000000000000000000000000000000000000000000000000000000062900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "txsEffectsHash": "0x0048ce729bd26a2be2b87719b8682891daaf022265be6cb3460d1f654f325dab", "decodedHeader": { @@ -49,8 +49,8 @@ "chainId": 31337, "timestamp": 0, "version": 1, - "coinbase": "0x5e42ecbaebd6cd5f6dd356f51c0fa991be9d3084", - "feeRecipient": "0x1387d4ee7f411ec349f1a71cc34b181667b0fd77ced57b529a06f2ddbf269112", + "coinbase": "0xe19d288dac593449f143689ea3563e9d960e0ae6", + "feeRecipient": "0x20f9afacaabdb5bca38f3586fad6ab2e72f75560e1504f8c3de2061b2283b25a", "gasFees": { "feePerDaGas": 0, "feePerL2Gas": 0 @@ -81,8 +81,8 @@ } } }, - "header": "0x067a48e3140b6f15d71751ededfa0cccde3d436bb71aa7fec226b0bfe51dc5cf0000000100000000000000000000000000000000000000000000000000000000000000020048ce729bd26a2be2b87719b8682891daaf022265be6cb3460d1f654f325dab00089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00198704eb051da0e43ff1a9b3285f168389ba3dd93f8ec1f75f6cafcadbaeb61864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f80000000100d944282e11bdcfa5e8f2b55fe80db4c586087bfc10e0bbba5724d30b8c15e2e0000010001c16141039343d4d403501e66deecff1b024bd76794820a43dc3424087813a20000018028d06967b6a4a1cc3c799fb6f008b63a2ffecd5034b81aa10792a6659f8aca22000000c00000000000000000000000000000000000000000000000000000000000007a690000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000005e42ecbaebd6cd5f6dd356f51c0fa991be9d30841387d4ee7f411ec349f1a71cc34b181667b0fd77ced57b529a06f2ddbf26911200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "publicInputsHash": "0x00ef2466dabd158a72ec68683a56286b12b50ecf9aca3c0849414a7fa63f7a17", + "header": "0x067a48e3140b6f15d71751ededfa0cccde3d436bb71aa7fec226b0bfe51dc5cf0000000100000000000000000000000000000000000000000000000000000000000000020048ce729bd26a2be2b87719b8682891daaf022265be6cb3460d1f654f325dab00089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00198704eb051da0e43ff1a9b3285f168389ba3dd93f8ec1f75f6cafcadbaeb61864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f80000000100d944282e11bdcfa5e8f2b55fe80db4c586087bfc10e0bbba5724d30b8c15e2e0000010001c16141039343d4d403501e66deecff1b024bd76794820a43dc3424087813a20000018028d06967b6a4a1cc3c799fb6f008b63a2ffecd5034b81aa10792a6659f8aca22000000c00000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000e19d288dac593449f143689ea3563e9d960e0ae620f9afacaabdb5bca38f3586fad6ab2e72f75560e1504f8c3de2061b2283b25a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "publicInputsHash": "0x004af29de40359d7c79637dce0ef57c561cb5ff283602607c6f644cb019fa526", "numTxs": 4 } } \ No newline at end of file diff --git a/l1-contracts/test/fixtures/mixed_block_1.json b/l1-contracts/test/fixtures/mixed_block_1.json index 5bc3fb47126..2fe88df3c70 100644 --- a/l1-contracts/test/fixtures/mixed_block_1.json +++ b/l1-contracts/test/fixtures/mixed_block_1.json @@ -34,7 +34,7 @@ ] }, "block": { - "archive": "0x250babc63de7989f6407cfa75b31762bbecdb77b1a2d6f3a8ad2ccfd348e60c6", + "archive": "0x1d882133187105c8d7ed6177b59d8c5462ce1a562537ccec510d9879caf10c7c", "body": "0x00000004000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000024100000000000000000000000000000000000000000000000000000000000002420000000000000000000000000000000000000000000000000000000000000243000000000000000000000000000000000000000000000000000000000000024400000000000000000000000000000000000000000000000000000000000002450000000000000000000000000000000000000000000000000000000000000246000000000000000000000000000000000000000000000000000000000000024700000000000000000000000000000000000000000000000000000000000002480000000000000000000000000000000000000000000000000000000000000249000000000000000000000000000000000000000000000000000000000000024a000000000000000000000000000000000000000000000000000000000000024b000000000000000000000000000000000000000000000000000000000000024c000000000000000000000000000000000000000000000000000000000000024d000000000000000000000000000000000000000000000000000000000000024e000000000000000000000000000000000000000000000000000000000000024f0000000000000000000000000000000000000000000000000000000000000250000000000000000000000000000000000000000000000000000000000000025100000000000000000000000000000000000000000000000000000000000002520000000000000000000000000000000000000000000000000000000000000253000000000000000000000000000000000000000000000000000000000000025400000000000000000000000000000000000000000000000000000000000002550000000000000000000000000000000000000000000000000000000000000256000000000000000000000000000000000000000000000000000000000000025700000000000000000000000000000000000000000000000000000000000002580000000000000000000000000000000000000000000000000000000000000259000000000000000000000000000000000000000000000000000000000000025a000000000000000000000000000000000000000000000000000000000000025b000000000000000000000000000000000000000000000000000000000000025c000000000000000000000000000000000000000000000000000000000000025d000000000000000000000000000000000000000000000000000000000000025e000000000000000000000000000000000000000000000000000000000000025f0000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000026100000000000000000000000000000000000000000000000000000000000002620000000000000000000000000000000000000000000000000000000000000263000000000000000000000000000000000000000000000000000000000000026400000000000000000000000000000000000000000000000000000000000002650000000000000000000000000000000000000000000000000000000000000266000000000000000000000000000000000000000000000000000000000000026700000000000000000000000000000000000000000000000000000000000002680000000000000000000000000000000000000000000000000000000000000269000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000026b000000000000000000000000000000000000000000000000000000000000026c000000000000000000000000000000000000000000000000000000000000026d000000000000000000000000000000000000000000000000000000000000026e000000000000000000000000000000000000000000000000000000000000026f0000000000000000000000000000000000000000000000000000000000000270000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000002720000000000000000000000000000000000000000000000000000000000000273000000000000000000000000000000000000000000000000000000000000027400000000000000000000000000000000000000000000000000000000000002750000000000000000000000000000000000000000000000000000000000000276000000000000000000000000000000000000000000000000000000000000027700000000000000000000000000000000000000000000000000000000000002780000000000000000000000000000000000000000000000000000000000000279000000000000000000000000000000000000000000000000000000000000027a000000000000000000000000000000000000000000000000000000000000027b000000000000000000000000000000000000000000000000000000000000027c000000000000000000000000000000000000000000000000000000000000027d000000000000000000000000000000000000000000000000000000000000027e000000000000000000000000000000000000000000000000000000000000027f3f0000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000003420000000000000000000000000000000000000000000000000000000000000343000000000000000000000000000000000000000000000000000000000000034400000000000000000000000000000000000000000000000000000000000003450000000000000000000000000000000000000000000000000000000000000346000000000000000000000000000000000000000000000000000000000000034700000000000000000000000000000000000000000000000000000000000003480000000000000000000000000000000000000000000000000000000000000349000000000000000000000000000000000000000000000000000000000000034a000000000000000000000000000000000000000000000000000000000000034b000000000000000000000000000000000000000000000000000000000000034c000000000000000000000000000000000000000000000000000000000000034d000000000000000000000000000000000000000000000000000000000000034e000000000000000000000000000000000000000000000000000000000000034f0000000000000000000000000000000000000000000000000000000000000350000000000000000000000000000000000000000000000000000000000000035100000000000000000000000000000000000000000000000000000000000003520000000000000000000000000000000000000000000000000000000000000353000000000000000000000000000000000000000000000000000000000000035400000000000000000000000000000000000000000000000000000000000003550000000000000000000000000000000000000000000000000000000000000356000000000000000000000000000000000000000000000000000000000000035700000000000000000000000000000000000000000000000000000000000003580000000000000000000000000000000000000000000000000000000000000359000000000000000000000000000000000000000000000000000000000000035a000000000000000000000000000000000000000000000000000000000000035b000000000000000000000000000000000000000000000000000000000000035c000000000000000000000000000000000000000000000000000000000000035d000000000000000000000000000000000000000000000000000000000000035e000000000000000000000000000000000000000000000000000000000000035f0000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000036100000000000000000000000000000000000000000000000000000000000003620000000000000000000000000000000000000000000000000000000000000363000000000000000000000000000000000000000000000000000000000000036400000000000000000000000000000000000000000000000000000000000003650000000000000000000000000000000000000000000000000000000000000366000000000000000000000000000000000000000000000000000000000000036700000000000000000000000000000000000000000000000000000000000003680000000000000000000000000000000000000000000000000000000000000369000000000000000000000000000000000000000000000000000000000000036a000000000000000000000000000000000000000000000000000000000000036b000000000000000000000000000000000000000000000000000000000000036c000000000000000000000000000000000000000000000000000000000000036d000000000000000000000000000000000000000000000000000000000000036e000000000000000000000000000000000000000000000000000000000000036f0000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000037100000000000000000000000000000000000000000000000000000000000003720000000000000000000000000000000000000000000000000000000000000373000000000000000000000000000000000000000000000000000000000000037400000000000000000000000000000000000000000000000000000000000003750000000000000000000000000000000000000000000000000000000000000376000000000000000000000000000000000000000000000000000000000000037700000000000000000000000000000000000000000000000000000000000003780000000000000000000000000000000000000000000000000000000000000379000000000000000000000000000000000000000000000000000000000000037a000000000000000000000000000000000000000000000000000000000000037b000000000000000000000000000000000000000000000000000000000000037c000000000000000000000000000000000000000000000000000000000000037d000000000000000000000000000000000000000000000000000000000000037e0200000000000000000000000000000000000000000000000000000000000004400000000000000000000000000000000000000000000000000000000000000441200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064a0000000000000000000000000000000000000000000000000000000000000641000000000000000000000000000000000000000000000000000000000000064b0000000000000000000000000000000000000000000000000000000000000642000000000000000000000000000000000000000000000000000000000000064c0000000000000000000000000000000000000000000000000000000000000643000000000000000000000000000000000000000000000000000000000000064d0000000000000000000000000000000000000000000000000000000000000644000000000000000000000000000000000000000000000000000000000000064e0000000000000000000000000000000000000000000000000000000000000645000000000000000000000000000000000000000000000000000000000000064f00000000000000000000000000000000000000000000000000000000000006460000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000000064700000000000000000000000000000000000000000000000000000000000006510000000000000000000000000000000000000000000000000000000000000648000000000000000000000000000000000000000000000000000000000000065200000000000000000000000000000000000000000000000000000000000006490000000000000000000000000000000000000000000000000000000000000653000000000000000000000000000000000000000000000000000000000000064a0000000000000000000000000000000000000000000000000000000000000654000000000000000000000000000000000000000000000000000000000000064b0000000000000000000000000000000000000000000000000000000000000655000000000000000000000000000000000000000000000000000000000000064c0000000000000000000000000000000000000000000000000000000000000656000000000000000000000000000000000000000000000000000000000000064d0000000000000000000000000000000000000000000000000000000000000657000000000000000000000000000000000000000000000000000000000000064e0000000000000000000000000000000000000000000000000000000000000658000000000000000000000000000000000000000000000000000000000000064f00000000000000000000000000000000000000000000000000000000000006590000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000000065a0000000000000000000000000000000000000000000000000000000000000651000000000000000000000000000000000000000000000000000000000000065b0000000000000000000000000000000000000000000000000000000000000652000000000000000000000000000000000000000000000000000000000000065c0000000000000000000000000000000000000000000000000000000000000653000000000000000000000000000000000000000000000000000000000000065d0000000000000000000000000000000000000000000000000000000000000654000000000000000000000000000000000000000000000000000000000000065e0000000000000000000000000000000000000000000000000000000000000655000000000000000000000000000000000000000000000000000000000000065f00000000000000000000000000000000000000000000000000000000000006560000000000000000000000000000000000000000000000000000000000000660000000000000000000000000000000000000000000000000000000000000065700000000000000000000000000000000000000000000000000000000000006610000000000000000000000000000000000000000000000000000000000000658000000000000000000000000000000000000000000000000000000000000066200000000000000000000000000000000000000000000000000000000000006590000000000000000000000000000000000000000000000000000000000000663000000000000000000000000000000000000000000000000000000000000065a0000000000000000000000000000000000000000000000000000000000000664000000000000000000000000000000000000000000000000000000000000065b0000000000000000000000000000000000000000000000000000000000000665000000000000000000000000000000000000000000000000000000000000065c0000000000000000000000000000000000000000000000000000000000000666000000000000000000000000000000000000000000000000000000000000065d0000000000000000000000000000000000000000000000000000000000000667000000000000000000000000000000000000000000000000000000000000065e0000000000000000000000000000000000000000000000000000000000000668000000000000000000000000000000000000000000000000000000000000065f000000000000000000000000000000000000000000000000000000000000066900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000028100000000000000000000000000000000000000000000000000000000000002820000000000000000000000000000000000000000000000000000000000000283000000000000000000000000000000000000000000000000000000000000028400000000000000000000000000000000000000000000000000000000000002850000000000000000000000000000000000000000000000000000000000000286000000000000000000000000000000000000000000000000000000000000028700000000000000000000000000000000000000000000000000000000000002880000000000000000000000000000000000000000000000000000000000000289000000000000000000000000000000000000000000000000000000000000028a000000000000000000000000000000000000000000000000000000000000028b000000000000000000000000000000000000000000000000000000000000028c000000000000000000000000000000000000000000000000000000000000028d000000000000000000000000000000000000000000000000000000000000028e000000000000000000000000000000000000000000000000000000000000028f0000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000000000000000000000000000000029100000000000000000000000000000000000000000000000000000000000002920000000000000000000000000000000000000000000000000000000000000293000000000000000000000000000000000000000000000000000000000000029400000000000000000000000000000000000000000000000000000000000002950000000000000000000000000000000000000000000000000000000000000296000000000000000000000000000000000000000000000000000000000000029700000000000000000000000000000000000000000000000000000000000002980000000000000000000000000000000000000000000000000000000000000299000000000000000000000000000000000000000000000000000000000000029a000000000000000000000000000000000000000000000000000000000000029b000000000000000000000000000000000000000000000000000000000000029c000000000000000000000000000000000000000000000000000000000000029d000000000000000000000000000000000000000000000000000000000000029e000000000000000000000000000000000000000000000000000000000000029f00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002a100000000000000000000000000000000000000000000000000000000000002a200000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000002a400000000000000000000000000000000000000000000000000000000000002a500000000000000000000000000000000000000000000000000000000000002a600000000000000000000000000000000000000000000000000000000000002a700000000000000000000000000000000000000000000000000000000000002a800000000000000000000000000000000000000000000000000000000000002a900000000000000000000000000000000000000000000000000000000000002aa00000000000000000000000000000000000000000000000000000000000002ab00000000000000000000000000000000000000000000000000000000000002ac00000000000000000000000000000000000000000000000000000000000002ad00000000000000000000000000000000000000000000000000000000000002ae00000000000000000000000000000000000000000000000000000000000002af00000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002b100000000000000000000000000000000000000000000000000000000000002b200000000000000000000000000000000000000000000000000000000000002b300000000000000000000000000000000000000000000000000000000000002b400000000000000000000000000000000000000000000000000000000000002b500000000000000000000000000000000000000000000000000000000000002b600000000000000000000000000000000000000000000000000000000000002b700000000000000000000000000000000000000000000000000000000000002b800000000000000000000000000000000000000000000000000000000000002b900000000000000000000000000000000000000000000000000000000000002ba00000000000000000000000000000000000000000000000000000000000002bb00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bd00000000000000000000000000000000000000000000000000000000000002be00000000000000000000000000000000000000000000000000000000000002bf3f0000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000038100000000000000000000000000000000000000000000000000000000000003820000000000000000000000000000000000000000000000000000000000000383000000000000000000000000000000000000000000000000000000000000038400000000000000000000000000000000000000000000000000000000000003850000000000000000000000000000000000000000000000000000000000000386000000000000000000000000000000000000000000000000000000000000038700000000000000000000000000000000000000000000000000000000000003880000000000000000000000000000000000000000000000000000000000000389000000000000000000000000000000000000000000000000000000000000038a000000000000000000000000000000000000000000000000000000000000038b000000000000000000000000000000000000000000000000000000000000038c000000000000000000000000000000000000000000000000000000000000038d000000000000000000000000000000000000000000000000000000000000038e000000000000000000000000000000000000000000000000000000000000038f0000000000000000000000000000000000000000000000000000000000000390000000000000000000000000000000000000000000000000000000000000039100000000000000000000000000000000000000000000000000000000000003920000000000000000000000000000000000000000000000000000000000000393000000000000000000000000000000000000000000000000000000000000039400000000000000000000000000000000000000000000000000000000000003950000000000000000000000000000000000000000000000000000000000000396000000000000000000000000000000000000000000000000000000000000039700000000000000000000000000000000000000000000000000000000000003980000000000000000000000000000000000000000000000000000000000000399000000000000000000000000000000000000000000000000000000000000039a000000000000000000000000000000000000000000000000000000000000039b000000000000000000000000000000000000000000000000000000000000039c000000000000000000000000000000000000000000000000000000000000039d000000000000000000000000000000000000000000000000000000000000039e000000000000000000000000000000000000000000000000000000000000039f00000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003a100000000000000000000000000000000000000000000000000000000000003a200000000000000000000000000000000000000000000000000000000000003a300000000000000000000000000000000000000000000000000000000000003a400000000000000000000000000000000000000000000000000000000000003a500000000000000000000000000000000000000000000000000000000000003a600000000000000000000000000000000000000000000000000000000000003a700000000000000000000000000000000000000000000000000000000000003a800000000000000000000000000000000000000000000000000000000000003a900000000000000000000000000000000000000000000000000000000000003aa00000000000000000000000000000000000000000000000000000000000003ab00000000000000000000000000000000000000000000000000000000000003ac00000000000000000000000000000000000000000000000000000000000003ad00000000000000000000000000000000000000000000000000000000000003ae00000000000000000000000000000000000000000000000000000000000003af00000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003b100000000000000000000000000000000000000000000000000000000000003b200000000000000000000000000000000000000000000000000000000000003b300000000000000000000000000000000000000000000000000000000000003b400000000000000000000000000000000000000000000000000000000000003b500000000000000000000000000000000000000000000000000000000000003b600000000000000000000000000000000000000000000000000000000000003b700000000000000000000000000000000000000000000000000000000000003b800000000000000000000000000000000000000000000000000000000000003b900000000000000000000000000000000000000000000000000000000000003ba00000000000000000000000000000000000000000000000000000000000003bb00000000000000000000000000000000000000000000000000000000000003bc00000000000000000000000000000000000000000000000000000000000003bd00000000000000000000000000000000000000000000000000000000000003be0200000000000000000000000000000000000000000000000000000000000004800000000000000000000000000000000000000000000000000000000000000481200000000000000000000000000000000000000000000000000000000000000680000000000000000000000000000000000000000000000000000000000000068a0000000000000000000000000000000000000000000000000000000000000681000000000000000000000000000000000000000000000000000000000000068b0000000000000000000000000000000000000000000000000000000000000682000000000000000000000000000000000000000000000000000000000000068c0000000000000000000000000000000000000000000000000000000000000683000000000000000000000000000000000000000000000000000000000000068d0000000000000000000000000000000000000000000000000000000000000684000000000000000000000000000000000000000000000000000000000000068e0000000000000000000000000000000000000000000000000000000000000685000000000000000000000000000000000000000000000000000000000000068f00000000000000000000000000000000000000000000000000000000000006860000000000000000000000000000000000000000000000000000000000000690000000000000000000000000000000000000000000000000000000000000068700000000000000000000000000000000000000000000000000000000000006910000000000000000000000000000000000000000000000000000000000000688000000000000000000000000000000000000000000000000000000000000069200000000000000000000000000000000000000000000000000000000000006890000000000000000000000000000000000000000000000000000000000000693000000000000000000000000000000000000000000000000000000000000068a0000000000000000000000000000000000000000000000000000000000000694000000000000000000000000000000000000000000000000000000000000068b0000000000000000000000000000000000000000000000000000000000000695000000000000000000000000000000000000000000000000000000000000068c0000000000000000000000000000000000000000000000000000000000000696000000000000000000000000000000000000000000000000000000000000068d0000000000000000000000000000000000000000000000000000000000000697000000000000000000000000000000000000000000000000000000000000068e0000000000000000000000000000000000000000000000000000000000000698000000000000000000000000000000000000000000000000000000000000068f00000000000000000000000000000000000000000000000000000000000006990000000000000000000000000000000000000000000000000000000000000690000000000000000000000000000000000000000000000000000000000000069a0000000000000000000000000000000000000000000000000000000000000691000000000000000000000000000000000000000000000000000000000000069b0000000000000000000000000000000000000000000000000000000000000692000000000000000000000000000000000000000000000000000000000000069c0000000000000000000000000000000000000000000000000000000000000693000000000000000000000000000000000000000000000000000000000000069d0000000000000000000000000000000000000000000000000000000000000694000000000000000000000000000000000000000000000000000000000000069e0000000000000000000000000000000000000000000000000000000000000695000000000000000000000000000000000000000000000000000000000000069f000000000000000000000000000000000000000000000000000000000000069600000000000000000000000000000000000000000000000000000000000006a0000000000000000000000000000000000000000000000000000000000000069700000000000000000000000000000000000000000000000000000000000006a1000000000000000000000000000000000000000000000000000000000000069800000000000000000000000000000000000000000000000000000000000006a2000000000000000000000000000000000000000000000000000000000000069900000000000000000000000000000000000000000000000000000000000006a3000000000000000000000000000000000000000000000000000000000000069a00000000000000000000000000000000000000000000000000000000000006a4000000000000000000000000000000000000000000000000000000000000069b00000000000000000000000000000000000000000000000000000000000006a5000000000000000000000000000000000000000000000000000000000000069c00000000000000000000000000000000000000000000000000000000000006a6000000000000000000000000000000000000000000000000000000000000069d00000000000000000000000000000000000000000000000000000000000006a7000000000000000000000000000000000000000000000000000000000000069e00000000000000000000000000000000000000000000000000000000000006a8000000000000000000000000000000000000000000000000000000000000069f00000000000000000000000000000000000000000000000000000000000006a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002c100000000000000000000000000000000000000000000000000000000000002c200000000000000000000000000000000000000000000000000000000000002c300000000000000000000000000000000000000000000000000000000000002c400000000000000000000000000000000000000000000000000000000000002c500000000000000000000000000000000000000000000000000000000000002c600000000000000000000000000000000000000000000000000000000000002c700000000000000000000000000000000000000000000000000000000000002c800000000000000000000000000000000000000000000000000000000000002c900000000000000000000000000000000000000000000000000000000000002ca00000000000000000000000000000000000000000000000000000000000002cb00000000000000000000000000000000000000000000000000000000000002cc00000000000000000000000000000000000000000000000000000000000002cd00000000000000000000000000000000000000000000000000000000000002ce00000000000000000000000000000000000000000000000000000000000002cf00000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002d100000000000000000000000000000000000000000000000000000000000002d200000000000000000000000000000000000000000000000000000000000002d300000000000000000000000000000000000000000000000000000000000002d400000000000000000000000000000000000000000000000000000000000002d500000000000000000000000000000000000000000000000000000000000002d600000000000000000000000000000000000000000000000000000000000002d700000000000000000000000000000000000000000000000000000000000002d800000000000000000000000000000000000000000000000000000000000002d900000000000000000000000000000000000000000000000000000000000002da00000000000000000000000000000000000000000000000000000000000002db00000000000000000000000000000000000000000000000000000000000002dc00000000000000000000000000000000000000000000000000000000000002dd00000000000000000000000000000000000000000000000000000000000002de00000000000000000000000000000000000000000000000000000000000002df00000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002e100000000000000000000000000000000000000000000000000000000000002e200000000000000000000000000000000000000000000000000000000000002e300000000000000000000000000000000000000000000000000000000000002e400000000000000000000000000000000000000000000000000000000000002e500000000000000000000000000000000000000000000000000000000000002e600000000000000000000000000000000000000000000000000000000000002e700000000000000000000000000000000000000000000000000000000000002e800000000000000000000000000000000000000000000000000000000000002e900000000000000000000000000000000000000000000000000000000000002ea00000000000000000000000000000000000000000000000000000000000002eb00000000000000000000000000000000000000000000000000000000000002ec00000000000000000000000000000000000000000000000000000000000002ed00000000000000000000000000000000000000000000000000000000000002ee00000000000000000000000000000000000000000000000000000000000002ef00000000000000000000000000000000000000000000000000000000000002f000000000000000000000000000000000000000000000000000000000000002f100000000000000000000000000000000000000000000000000000000000002f200000000000000000000000000000000000000000000000000000000000002f300000000000000000000000000000000000000000000000000000000000002f400000000000000000000000000000000000000000000000000000000000002f500000000000000000000000000000000000000000000000000000000000002f600000000000000000000000000000000000000000000000000000000000002f700000000000000000000000000000000000000000000000000000000000002f800000000000000000000000000000000000000000000000000000000000002f900000000000000000000000000000000000000000000000000000000000002fa00000000000000000000000000000000000000000000000000000000000002fb00000000000000000000000000000000000000000000000000000000000002fc00000000000000000000000000000000000000000000000000000000000002fd00000000000000000000000000000000000000000000000000000000000002fe00000000000000000000000000000000000000000000000000000000000002ff3f00000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003c100000000000000000000000000000000000000000000000000000000000003c200000000000000000000000000000000000000000000000000000000000003c300000000000000000000000000000000000000000000000000000000000003c400000000000000000000000000000000000000000000000000000000000003c500000000000000000000000000000000000000000000000000000000000003c600000000000000000000000000000000000000000000000000000000000003c700000000000000000000000000000000000000000000000000000000000003c800000000000000000000000000000000000000000000000000000000000003c900000000000000000000000000000000000000000000000000000000000003ca00000000000000000000000000000000000000000000000000000000000003cb00000000000000000000000000000000000000000000000000000000000003cc00000000000000000000000000000000000000000000000000000000000003cd00000000000000000000000000000000000000000000000000000000000003ce00000000000000000000000000000000000000000000000000000000000003cf00000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003d100000000000000000000000000000000000000000000000000000000000003d200000000000000000000000000000000000000000000000000000000000003d300000000000000000000000000000000000000000000000000000000000003d400000000000000000000000000000000000000000000000000000000000003d500000000000000000000000000000000000000000000000000000000000003d600000000000000000000000000000000000000000000000000000000000003d700000000000000000000000000000000000000000000000000000000000003d800000000000000000000000000000000000000000000000000000000000003d900000000000000000000000000000000000000000000000000000000000003da00000000000000000000000000000000000000000000000000000000000003db00000000000000000000000000000000000000000000000000000000000003dc00000000000000000000000000000000000000000000000000000000000003dd00000000000000000000000000000000000000000000000000000000000003de00000000000000000000000000000000000000000000000000000000000003df00000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003e100000000000000000000000000000000000000000000000000000000000003e200000000000000000000000000000000000000000000000000000000000003e300000000000000000000000000000000000000000000000000000000000003e400000000000000000000000000000000000000000000000000000000000003e500000000000000000000000000000000000000000000000000000000000003e600000000000000000000000000000000000000000000000000000000000003e700000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e900000000000000000000000000000000000000000000000000000000000003ea00000000000000000000000000000000000000000000000000000000000003eb00000000000000000000000000000000000000000000000000000000000003ec00000000000000000000000000000000000000000000000000000000000003ed00000000000000000000000000000000000000000000000000000000000003ee00000000000000000000000000000000000000000000000000000000000003ef00000000000000000000000000000000000000000000000000000000000003f000000000000000000000000000000000000000000000000000000000000003f100000000000000000000000000000000000000000000000000000000000003f200000000000000000000000000000000000000000000000000000000000003f300000000000000000000000000000000000000000000000000000000000003f400000000000000000000000000000000000000000000000000000000000003f500000000000000000000000000000000000000000000000000000000000003f600000000000000000000000000000000000000000000000000000000000003f700000000000000000000000000000000000000000000000000000000000003f800000000000000000000000000000000000000000000000000000000000003f900000000000000000000000000000000000000000000000000000000000003fa00000000000000000000000000000000000000000000000000000000000003fb00000000000000000000000000000000000000000000000000000000000003fc00000000000000000000000000000000000000000000000000000000000003fd00000000000000000000000000000000000000000000000000000000000003fe0200000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000004c12000000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000006ca00000000000000000000000000000000000000000000000000000000000006c100000000000000000000000000000000000000000000000000000000000006cb00000000000000000000000000000000000000000000000000000000000006c200000000000000000000000000000000000000000000000000000000000006cc00000000000000000000000000000000000000000000000000000000000006c300000000000000000000000000000000000000000000000000000000000006cd00000000000000000000000000000000000000000000000000000000000006c400000000000000000000000000000000000000000000000000000000000006ce00000000000000000000000000000000000000000000000000000000000006c500000000000000000000000000000000000000000000000000000000000006cf00000000000000000000000000000000000000000000000000000000000006c600000000000000000000000000000000000000000000000000000000000006d000000000000000000000000000000000000000000000000000000000000006c700000000000000000000000000000000000000000000000000000000000006d100000000000000000000000000000000000000000000000000000000000006c800000000000000000000000000000000000000000000000000000000000006d200000000000000000000000000000000000000000000000000000000000006c900000000000000000000000000000000000000000000000000000000000006d300000000000000000000000000000000000000000000000000000000000006ca00000000000000000000000000000000000000000000000000000000000006d400000000000000000000000000000000000000000000000000000000000006cb00000000000000000000000000000000000000000000000000000000000006d500000000000000000000000000000000000000000000000000000000000006cc00000000000000000000000000000000000000000000000000000000000006d600000000000000000000000000000000000000000000000000000000000006cd00000000000000000000000000000000000000000000000000000000000006d700000000000000000000000000000000000000000000000000000000000006ce00000000000000000000000000000000000000000000000000000000000006d800000000000000000000000000000000000000000000000000000000000006cf00000000000000000000000000000000000000000000000000000000000006d900000000000000000000000000000000000000000000000000000000000006d000000000000000000000000000000000000000000000000000000000000006da00000000000000000000000000000000000000000000000000000000000006d100000000000000000000000000000000000000000000000000000000000006db00000000000000000000000000000000000000000000000000000000000006d200000000000000000000000000000000000000000000000000000000000006dc00000000000000000000000000000000000000000000000000000000000006d300000000000000000000000000000000000000000000000000000000000006dd00000000000000000000000000000000000000000000000000000000000006d400000000000000000000000000000000000000000000000000000000000006de00000000000000000000000000000000000000000000000000000000000006d500000000000000000000000000000000000000000000000000000000000006df00000000000000000000000000000000000000000000000000000000000006d600000000000000000000000000000000000000000000000000000000000006e000000000000000000000000000000000000000000000000000000000000006d700000000000000000000000000000000000000000000000000000000000006e100000000000000000000000000000000000000000000000000000000000006d800000000000000000000000000000000000000000000000000000000000006e200000000000000000000000000000000000000000000000000000000000006d900000000000000000000000000000000000000000000000000000000000006e300000000000000000000000000000000000000000000000000000000000006da00000000000000000000000000000000000000000000000000000000000006e400000000000000000000000000000000000000000000000000000000000006db00000000000000000000000000000000000000000000000000000000000006e500000000000000000000000000000000000000000000000000000000000006dc00000000000000000000000000000000000000000000000000000000000006e600000000000000000000000000000000000000000000000000000000000006dd00000000000000000000000000000000000000000000000000000000000006e700000000000000000000000000000000000000000000000000000000000006de00000000000000000000000000000000000000000000000000000000000006e800000000000000000000000000000000000000000000000000000000000006df00000000000000000000000000000000000000000000000000000000000006e900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000030100000000000000000000000000000000000000000000000000000000000003020000000000000000000000000000000000000000000000000000000000000303000000000000000000000000000000000000000000000000000000000000030400000000000000000000000000000000000000000000000000000000000003050000000000000000000000000000000000000000000000000000000000000306000000000000000000000000000000000000000000000000000000000000030700000000000000000000000000000000000000000000000000000000000003080000000000000000000000000000000000000000000000000000000000000309000000000000000000000000000000000000000000000000000000000000030a000000000000000000000000000000000000000000000000000000000000030b000000000000000000000000000000000000000000000000000000000000030c000000000000000000000000000000000000000000000000000000000000030d000000000000000000000000000000000000000000000000000000000000030e000000000000000000000000000000000000000000000000000000000000030f0000000000000000000000000000000000000000000000000000000000000310000000000000000000000000000000000000000000000000000000000000031100000000000000000000000000000000000000000000000000000000000003120000000000000000000000000000000000000000000000000000000000000313000000000000000000000000000000000000000000000000000000000000031400000000000000000000000000000000000000000000000000000000000003150000000000000000000000000000000000000000000000000000000000000316000000000000000000000000000000000000000000000000000000000000031700000000000000000000000000000000000000000000000000000000000003180000000000000000000000000000000000000000000000000000000000000319000000000000000000000000000000000000000000000000000000000000031a000000000000000000000000000000000000000000000000000000000000031b000000000000000000000000000000000000000000000000000000000000031c000000000000000000000000000000000000000000000000000000000000031d000000000000000000000000000000000000000000000000000000000000031e000000000000000000000000000000000000000000000000000000000000031f0000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032100000000000000000000000000000000000000000000000000000000000003220000000000000000000000000000000000000000000000000000000000000323000000000000000000000000000000000000000000000000000000000000032400000000000000000000000000000000000000000000000000000000000003250000000000000000000000000000000000000000000000000000000000000326000000000000000000000000000000000000000000000000000000000000032700000000000000000000000000000000000000000000000000000000000003280000000000000000000000000000000000000000000000000000000000000329000000000000000000000000000000000000000000000000000000000000032a000000000000000000000000000000000000000000000000000000000000032b000000000000000000000000000000000000000000000000000000000000032c000000000000000000000000000000000000000000000000000000000000032d000000000000000000000000000000000000000000000000000000000000032e000000000000000000000000000000000000000000000000000000000000032f0000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000033100000000000000000000000000000000000000000000000000000000000003320000000000000000000000000000000000000000000000000000000000000333000000000000000000000000000000000000000000000000000000000000033400000000000000000000000000000000000000000000000000000000000003350000000000000000000000000000000000000000000000000000000000000336000000000000000000000000000000000000000000000000000000000000033700000000000000000000000000000000000000000000000000000000000003380000000000000000000000000000000000000000000000000000000000000339000000000000000000000000000000000000000000000000000000000000033a000000000000000000000000000000000000000000000000000000000000033b000000000000000000000000000000000000000000000000000000000000033c000000000000000000000000000000000000000000000000000000000000033d000000000000000000000000000000000000000000000000000000000000033e000000000000000000000000000000000000000000000000000000000000033f3f0000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040100000000000000000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000403000000000000000000000000000000000000000000000000000000000000040400000000000000000000000000000000000000000000000000000000000004050000000000000000000000000000000000000000000000000000000000000406000000000000000000000000000000000000000000000000000000000000040700000000000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000409000000000000000000000000000000000000000000000000000000000000040a000000000000000000000000000000000000000000000000000000000000040b000000000000000000000000000000000000000000000000000000000000040c000000000000000000000000000000000000000000000000000000000000040d000000000000000000000000000000000000000000000000000000000000040e000000000000000000000000000000000000000000000000000000000000040f0000000000000000000000000000000000000000000000000000000000000410000000000000000000000000000000000000000000000000000000000000041100000000000000000000000000000000000000000000000000000000000004120000000000000000000000000000000000000000000000000000000000000413000000000000000000000000000000000000000000000000000000000000041400000000000000000000000000000000000000000000000000000000000004150000000000000000000000000000000000000000000000000000000000000416000000000000000000000000000000000000000000000000000000000000041700000000000000000000000000000000000000000000000000000000000004180000000000000000000000000000000000000000000000000000000000000419000000000000000000000000000000000000000000000000000000000000041a000000000000000000000000000000000000000000000000000000000000041b000000000000000000000000000000000000000000000000000000000000041c000000000000000000000000000000000000000000000000000000000000041d000000000000000000000000000000000000000000000000000000000000041e000000000000000000000000000000000000000000000000000000000000041f0000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000042100000000000000000000000000000000000000000000000000000000000004220000000000000000000000000000000000000000000000000000000000000423000000000000000000000000000000000000000000000000000000000000042400000000000000000000000000000000000000000000000000000000000004250000000000000000000000000000000000000000000000000000000000000426000000000000000000000000000000000000000000000000000000000000042700000000000000000000000000000000000000000000000000000000000004280000000000000000000000000000000000000000000000000000000000000429000000000000000000000000000000000000000000000000000000000000042a000000000000000000000000000000000000000000000000000000000000042b000000000000000000000000000000000000000000000000000000000000042c000000000000000000000000000000000000000000000000000000000000042d000000000000000000000000000000000000000000000000000000000000042e000000000000000000000000000000000000000000000000000000000000042f0000000000000000000000000000000000000000000000000000000000000430000000000000000000000000000000000000000000000000000000000000043100000000000000000000000000000000000000000000000000000000000004320000000000000000000000000000000000000000000000000000000000000433000000000000000000000000000000000000000000000000000000000000043400000000000000000000000000000000000000000000000000000000000004350000000000000000000000000000000000000000000000000000000000000436000000000000000000000000000000000000000000000000000000000000043700000000000000000000000000000000000000000000000000000000000004380000000000000000000000000000000000000000000000000000000000000439000000000000000000000000000000000000000000000000000000000000043a000000000000000000000000000000000000000000000000000000000000043b000000000000000000000000000000000000000000000000000000000000043c000000000000000000000000000000000000000000000000000000000000043d000000000000000000000000000000000000000000000000000000000000043e0200000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000501200000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000070a0000000000000000000000000000000000000000000000000000000000000701000000000000000000000000000000000000000000000000000000000000070b0000000000000000000000000000000000000000000000000000000000000702000000000000000000000000000000000000000000000000000000000000070c0000000000000000000000000000000000000000000000000000000000000703000000000000000000000000000000000000000000000000000000000000070d0000000000000000000000000000000000000000000000000000000000000704000000000000000000000000000000000000000000000000000000000000070e0000000000000000000000000000000000000000000000000000000000000705000000000000000000000000000000000000000000000000000000000000070f00000000000000000000000000000000000000000000000000000000000007060000000000000000000000000000000000000000000000000000000000000710000000000000000000000000000000000000000000000000000000000000070700000000000000000000000000000000000000000000000000000000000007110000000000000000000000000000000000000000000000000000000000000708000000000000000000000000000000000000000000000000000000000000071200000000000000000000000000000000000000000000000000000000000007090000000000000000000000000000000000000000000000000000000000000713000000000000000000000000000000000000000000000000000000000000070a0000000000000000000000000000000000000000000000000000000000000714000000000000000000000000000000000000000000000000000000000000070b0000000000000000000000000000000000000000000000000000000000000715000000000000000000000000000000000000000000000000000000000000070c0000000000000000000000000000000000000000000000000000000000000716000000000000000000000000000000000000000000000000000000000000070d0000000000000000000000000000000000000000000000000000000000000717000000000000000000000000000000000000000000000000000000000000070e0000000000000000000000000000000000000000000000000000000000000718000000000000000000000000000000000000000000000000000000000000070f00000000000000000000000000000000000000000000000000000000000007190000000000000000000000000000000000000000000000000000000000000710000000000000000000000000000000000000000000000000000000000000071a0000000000000000000000000000000000000000000000000000000000000711000000000000000000000000000000000000000000000000000000000000071b0000000000000000000000000000000000000000000000000000000000000712000000000000000000000000000000000000000000000000000000000000071c0000000000000000000000000000000000000000000000000000000000000713000000000000000000000000000000000000000000000000000000000000071d0000000000000000000000000000000000000000000000000000000000000714000000000000000000000000000000000000000000000000000000000000071e0000000000000000000000000000000000000000000000000000000000000715000000000000000000000000000000000000000000000000000000000000071f00000000000000000000000000000000000000000000000000000000000007160000000000000000000000000000000000000000000000000000000000000720000000000000000000000000000000000000000000000000000000000000071700000000000000000000000000000000000000000000000000000000000007210000000000000000000000000000000000000000000000000000000000000718000000000000000000000000000000000000000000000000000000000000072200000000000000000000000000000000000000000000000000000000000007190000000000000000000000000000000000000000000000000000000000000723000000000000000000000000000000000000000000000000000000000000071a0000000000000000000000000000000000000000000000000000000000000724000000000000000000000000000000000000000000000000000000000000071b0000000000000000000000000000000000000000000000000000000000000725000000000000000000000000000000000000000000000000000000000000071c0000000000000000000000000000000000000000000000000000000000000726000000000000000000000000000000000000000000000000000000000000071d0000000000000000000000000000000000000000000000000000000000000727000000000000000000000000000000000000000000000000000000000000071e0000000000000000000000000000000000000000000000000000000000000728000000000000000000000000000000000000000000000000000000000000071f000000000000000000000000000000000000000000000000000000000000072900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "txsEffectsHash": "0x00f8afbbf042432cf18d704499f6533cc95ea378b5b2ef1dc75f2438873a62b1", "decodedHeader": { @@ -47,10 +47,10 @@ "globalVariables": { "blockNumber": 2, "chainId": 31337, - "timestamp": 1715940580, + "timestamp": 1716042373, "version": 1, - "coinbase": "0x5e42ecbaebd6cd5f6dd356f51c0fa991be9d3084", - "feeRecipient": "0x1387d4ee7f411ec349f1a71cc34b181667b0fd77ced57b529a06f2ddbf269112", + "coinbase": "0xe19d288dac593449f143689ea3563e9d960e0ae6", + "feeRecipient": "0x20f9afacaabdb5bca38f3586fad6ab2e72f75560e1504f8c3de2061b2283b25a", "gasFees": { "feePerDaGas": 0, "feePerL2Gas": 0 @@ -58,7 +58,7 @@ }, "lastArchive": { "nextAvailableLeafIndex": 2, - "root": "0x1da7e3994972b8e4d8f2dffeb084976254aebcca1a429e576eea74eae6ae20c4" + "root": "0x21a504c1644ee56efe1d5c7690d6edc47b21a389e718f2202bcdc6ea4f879b0e" }, "stateReference": { "l1ToL2MessageTree": { @@ -81,8 +81,8 @@ } } }, - "header": "0x1da7e3994972b8e4d8f2dffeb084976254aebcca1a429e576eea74eae6ae20c400000002000000000000000000000000000000000000000000000000000000000000000200f8afbbf042432cf18d704499f6533cc95ea378b5b2ef1dc75f2438873a62b100212ff46db74e06c26240f9a92fb6fea84709380935d657361bbd5bcb89193700a5a7c9f331ce6832a69dc81873ed87de7ceeaaed2af1d595cb14ca9616eddd2e0232573b292e99cb24c082c3ef340d619341ab76aa1e9dff1ab1914963452d0000002024c6dc6d357aad01e10fe1adb877bb28b1df97375b874116e488086ca76e5f9600000200268020a622156e2beac47431b0cd70e1c81fef9a6aa3c365bfcbed9aa7301c5e000002802ecba8caa69552bb0d9bdf0d13eb328aeb6f166a1509678d9bfa9970971d69ab000001400000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000066472ce45e42ecbaebd6cd5f6dd356f51c0fa991be9d30841387d4ee7f411ec349f1a71cc34b181667b0fd77ced57b529a06f2ddbf26911200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "publicInputsHash": "0x00481dfc48bdb367e200298beada39daf0837e3d33e81257e92e65d48a5e0a81", + "header": "0x21a504c1644ee56efe1d5c7690d6edc47b21a389e718f2202bcdc6ea4f879b0e00000002000000000000000000000000000000000000000000000000000000000000000200f8afbbf042432cf18d704499f6533cc95ea378b5b2ef1dc75f2438873a62b100212ff46db74e06c26240f9a92fb6fea84709380935d657361bbd5bcb89193700a5a7c9f331ce6832a69dc81873ed87de7ceeaaed2af1d595cb14ca9616eddd2e0232573b292e99cb24c082c3ef340d619341ab76aa1e9dff1ab1914963452d0000002024c6dc6d357aad01e10fe1adb877bb28b1df97375b874116e488086ca76e5f9600000200268020a622156e2beac47431b0cd70e1c81fef9a6aa3c365bfcbed9aa7301c5e000002802ecba8caa69552bb0d9bdf0d13eb328aeb6f166a1509678d9bfa9970971d69ab000001400000000000000000000000000000000000000000000000000000000000007a6900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000006648ba85e19d288dac593449f143689ea3563e9d960e0ae620f9afacaabdb5bca38f3586fad6ab2e72f75560e1504f8c3de2061b2283b25a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "publicInputsHash": "0x007402eac386598833154a72f24a89eb5152eec68dfd43c12dd847da5a99289b", "numTxs": 4 } } \ No newline at end of file diff --git a/noir-projects/aztec-nr/address-note/src/address_note.nr b/noir-projects/aztec-nr/address-note/src/address_note.nr index 3d33ecb4a52..df33796ed35 100644 --- a/noir-projects/aztec-nr/address-note/src/address_note.nr +++ b/noir-projects/aztec-nr/address-note/src/address_note.nr @@ -44,7 +44,7 @@ impl NoteInterface for AddressNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field, ivpk_m: GrumpkinPoint) { // docs:start:encrypted - context.emit_note_encrypted_log( + context.encrypt_and_emit_note( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index 869000fac77..9722d8aecbf 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -1,12 +1,13 @@ use crate::{ context::{inputs::PrivateContextInputs, interface::ContextInterface}, messaging::process_l1_to_l2_message, - hash::{hash_args_array, ArgsHasher, compute_encrypted_log_hash, compute_unencrypted_log_hash}, + hash::{hash_args_array, ArgsHasher, compute_unencrypted_log_hash}, note::{note_interface::NoteInterface, utils::compute_note_hash_for_insertion}, oracle::{ nullifier_keys::get_nullifier_key_validation_request, arguments, returns, call_private_function::call_private_function_internal, header::get_header_at, - logs::emit_encrypted_log, logs_traits::{LensForEncryptedLog, ToBytesForUnencryptedLog}, + logs::{emit_encrypted_log, emit_encrypted_note_log, compute_encrypted_log}, + logs_traits::{LensForEncryptedLog, ToBytesForUnencryptedLog}, enqueue_public_function_call::{ enqueue_public_function_call_internal, set_public_teardown_function_call_internal, parse_public_call_stack_item_from_oracle @@ -14,6 +15,7 @@ use crate::{ } }; use dep::protocol_types::{ + hash::sha256_to_field, abis::{ function_selector::FunctionSelector, max_block_number::MaxBlockNumber, nullifier_key_validation_request::NullifierKeyValidationRequest, @@ -281,42 +283,34 @@ impl PrivateContext { let side_effect = LogHash { value: log_hash, counter, length: len }; self.unencrypted_logs_hashes.push(side_effect); } - // TODO(1139): Convert to generic input once we encrypt inside the circuit - pub fn emit_encrypted_log( + + pub fn encrypt_and_emit_log( &mut self, contract_address: AztecAddress, storage_slot: Field, note_type_id: Field, ivpk_m: GrumpkinPoint, preimage: [Field; N] - ) where [Field; N]: LensForEncryptedLog { - // TODO(1139): perform encryption in the circuit - // The oracle call should come last, but we require the encrypted value for now + ) where [Field; N]: LensForEncryptedLog { + // We are currently just encrypting it EXACTLY the same way as if it was a note. let counter = self.next_counter(); - let encrypted_log: [Field; M] = emit_encrypted_log( - contract_address, - storage_slot, - note_type_id, - ivpk_m, - preimage, - counter - ); - // = 32*all fields + bytes for encryption (112) + processed log len (4) - let len = 112 + 32 * (N + 3) + 4; - let log_hash = compute_encrypted_log_hash(encrypted_log); + let encrypted_log: [u8; M] = compute_encrypted_log(contract_address, storage_slot, note_type_id, ivpk_m, preimage); + emit_encrypted_log(encrypted_log, counter); + let len = 32 + 32 + 64 + 48 + 48 + 176 + 64 + (preimage.len() as Field * 32) + 16 + 4; + let log_hash = sha256_to_field(encrypted_log); let side_effect = LogHash { value: log_hash, counter, length: len }; self.encrypted_logs_hashes.push(side_effect); } - pub fn emit_note_encrypted_log( + pub fn encrypt_and_emit_note( &mut self, contract_address: AztecAddress, storage_slot: Field, note_type_id: Field, - encryption_pub_key: GrumpkinPoint, + ivpk_m: GrumpkinPoint, note: Note - ) where Note: NoteInterface, [Field; N]: LensForEncryptedLog { - let note_hash = compute_note_hash_for_insertion(note); + ) where Note: NoteInterface, [Field; N]: LensForEncryptedLog { + let note_hash: Field = compute_note_hash_for_insertion(note); let note_exists_index = find_index( self.new_note_hashes.storage, |n: NoteHash| n.value == note_hash @@ -327,19 +321,24 @@ impl PrivateContext { let note_hash_counter = self.new_note_hashes.storage[note_exists_index].counter; let preimage = note.serialize_content(); let counter = self.next_counter(); - // TODO(1139): perform encryption in the circuit - // The oracle call should come last, but we require the encrypted value for now - let encrypted_log: [Field; M] = emit_encrypted_log( - contract_address, - storage_slot, - note_type_id, - encryption_pub_key, - preimage, - counter - ); - // = 32*all fields + bytes for encryption (112) + processed log len (4) - let len = 112 + 32 * (preimage.len() as Field + 3) + 4; - let log_hash = compute_encrypted_log_hash(encrypted_log); + + // TODO(#1139 | #6408): perform encryption in the circuit + let encrypted_log: [u8; M] = compute_encrypted_log(contract_address, storage_slot, note_type_id, ivpk_m, preimage); + emit_encrypted_note_log(note_hash, encrypted_log, counter); + + // Current unoptimized size of the encrypted log + // incoming_tag (32 bytes) + // outgoing_tag (32 bytes) + // eph_pk (64 bytes) + // incoming_header (48 bytes) + // outgoing_header (48 bytes) + // outgoing_body (176 bytes) + // incoming_body_fixed (64 bytes) + // incoming_body_variable (N * 32 bytes + 16 bytes padding) + // len of processed log (4 bytes) + let len = 32 + 32 + 64 + 48 + 48 + 176 + 64 + (preimage.len() as Field * 32) + 16 + 4; + + let log_hash = sha256_to_field(encrypted_log); let side_effect = NoteLogHash { value: log_hash, counter, length: len, note_hash_counter }; self.note_encrypted_logs_hashes.push(side_effect); } diff --git a/noir-projects/aztec-nr/aztec/src/hash.nr b/noir-projects/aztec-nr/aztec/src/hash.nr index f0abd4912e1..322db82b966 100644 --- a/noir-projects/aztec-nr/aztec/src/hash.nr +++ b/noir-projects/aztec-nr/aztec/src/hash.nr @@ -12,24 +12,6 @@ pub fn compute_secret_hash(secret: Field) -> Field { pedersen_hash([secret], GENERATOR_INDEX__SECRET_HASH) } -pub fn compute_encrypted_log_hash(encrypted_log: [Field; M]) -> Field where [Field; N]: LensForEncryptedLog { - let mut bytes = [0; L]; - // Note that bytes.append(encrypted_log[i].to_be_bytes(31)) results in bound error - for i in 0..M - 1 { - let to_add = encrypted_log[i].to_be_bytes(31); - for j in 0..31 { - bytes[i*31 + j] = to_add[j]; - } - } - // can't assign as L - not in scope error for: L-31*(M-1) - let num_bytes = bytes.len() as u32 - 31 * (M - 1); - let to_add_final = encrypted_log[M - 1].to_be_bytes(num_bytes); - for j in 0..num_bytes { - bytes[(M-1)*31 + j] = to_add_final[j]; - } - sha256_to_field(bytes) -} - pub fn compute_unencrypted_log_hash( contract_address: AztecAddress, event_selector: Field, @@ -167,44 +149,6 @@ fn compute_var_args_hash() { assert(hash == 0x05a1023fef839ac88731f49ae983e172c1b600a3c8f3393ad0ac25d819ac0f0f); } -#[test] -fn compute_enc_log_hash_304() { - let input = [ - 0x0000000000000000000000000000000000000000000000000000000000000000, - 0x0021a0d4aa9989656b592187cf6da1965df53ab2ff2277421e663465cf20d3e9, - 0x00c3969cc350f3474f8187a33ac1317181961f5f94043b07ce888d85a5d20cb5, - 0x0058198041ed1547b056955b5141a5a8a1551b0c8d094255ec9daaf3604d9348, - 0x00247ad96df2e4d984cf795ed7316234743a681f824a45c46253de8bfde48850, - 0x007fc251f4ce44f4e9aba3dbf6567228be28fac85660156f2825ddb0b0577457, - 0x009315851323c6bc2aaa42e23fe5f3be97208f2d8167eafdfc5742d94f2f4dd4, - 0x00b938289e563b0fe01982cd9b8d9e33e3069046768ad01c0fb05e429e7b7909, - 0x00fbcc257a3211f705b471eee763b0f43876a2b2178fab6d2b09bd2b7e086584, - 0x000000000000008c3289b5793b7448f4d45ecde039d004b6f037cad10b5c2336 - ]; - let hash = compute_encrypted_log_hash(input); - assert(hash == 0x001e3c013994947fe28957a876bf1b2c3a69ac69cc92909efd4f2ae9b972f893); -} - -#[test] -fn compute_enc_log_hash_368() { - let input = [ - 0x0000000000000000000000000000000000000000000000000000000000000000, - 0x002190697d2a50e229a7a077e0951073f7d51e46679f10466153c308b63b1ea9, - 0x00543e346facc6799b94514c9d461bcc836c04b083b13c2e4544a39130473c1e, - 0x000df76d59526f8f953bcc7d9f77cdaefd36435931f0d7348f794bc275b42ded, - 0x00a6d390ee1723af7f7ac1ae4fc81a266b2370fe07040a36d06dbe242e02413e, - 0x00acbce15b6af1fbe94bd0f7b70f11768265dff77bfe63398f2a053efdfdf26d, - 0x00b8b131b9f42c689beb095ba4f4a836d4d15c9068d0422e9add6ca82b786329, - 0x00661a6a654b38f0f97d404ef5553e0efea9ed670561ae86685b31bbb2824fac, - 0x00113a6b58edfaec0065b365f66ba8d8aa68254b8690035e8d671a17a843f0a1, - 0x0023f2d2eae8c4449bac8f268a3e62a3faace1fe1401f0efdc8b0ccfbc8fb271, - 0x00cf6603f8c61993dd2f662c719671c61727a2f4e925fb988b23d31feccd77d9, - 0x0000000000a402a84b7294671799c38dd805f6a827a3a12633fdf91a57debe1f - ]; - let hash = compute_encrypted_log_hash(input); - assert(hash == 0x00a0d651ac0cbc01b72430fa6a05d91738595af6e0229347b4c9968223387aeb); -} - #[test] fn compute_unenc_log_hash_array() { let contract_address = AztecAddress::from_field(0x233a3e0df23b2b15b324194cb4a151f26c0b7333250781d34cc269d85dc334c6); diff --git a/noir-projects/aztec-nr/aztec/src/oracle/logs.nr b/noir-projects/aztec-nr/aztec/src/oracle/logs.nr index a1d933915ee..148b12a79b5 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/logs.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/logs.nr @@ -1,32 +1,40 @@ use dep::protocol_types::{address::AztecAddress, grumpkin_point::GrumpkinPoint}; -// TODO(1139): Should take encrypted data. -// Currently returns encrypted data to be hashed -// = 112 + 32 * (N + 3) bytes = N + 7 fields +// = 480 + 32 * N bytes +#[oracle(emitEncryptedNoteLog)] +fn emit_encrypted_note_log_oracle(_note_hash: Field, _encrypted_note: [u8; M], _counter: u32) {} + +unconstrained pub fn emit_encrypted_note_log( + note_hash: Field, + encrypted_note: [u8; M], + counter: u32 +) { + emit_encrypted_note_log_oracle(note_hash, encrypted_note, counter) +} + #[oracle(emitEncryptedLog)] -fn emit_encrypted_log_oracle( +fn emit_encrypted_log_oracle(_encrypted_note: [u8; M], _counter: u32) {} + +unconstrained pub fn emit_encrypted_log(encrypted_note: [u8; M], counter: u32) { + emit_encrypted_log_oracle(encrypted_note, counter) +} + +// = 480 + 32 * N bytes +#[oracle(computeEncryptedLog)] +fn compute_encrypted_log_oracle( _contract_address: AztecAddress, _storage_slot: Field, _note_type_id: Field, _encryption_pub_key: GrumpkinPoint, - _preimage: [Field; N], - _counter: u32 -) -> [Field; M] {} + _preimage: [Field; N] +) -> [u8; M] {} -unconstrained pub fn emit_encrypted_log( +unconstrained pub fn compute_encrypted_log( contract_address: AztecAddress, storage_slot: Field, note_type_id: Field, ivpk_m: GrumpkinPoint, - preimage: [Field; N], - counter: u32 -) -> [Field; M] { - emit_encrypted_log_oracle( - contract_address, - storage_slot, - note_type_id, - ivpk_m, - preimage, - counter - ) + preimage: [Field; N] +) -> [u8; M] { + compute_encrypted_log_oracle(contract_address, storage_slot, note_type_id, ivpk_m, preimage) } diff --git a/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr b/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr index 267979da364..c6632f5a4d3 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr @@ -1,8 +1,7 @@ use dep::protocol_types::address::AztecAddress; -// TODO: this is awful but since we can't have a fn that maps [Field; N] -> [Field; N+7] -// (where N is encrypted log preimage size and N+7 is encryption output size) -// and can't return slices from oracles, this at least compiles and runs +// TODO: this is awful but since we can't have a fn that maps [Field; N] -> [u8; 480 + N * 32] +// (where N is the note pre-image size and 480 + N * 32 is the encryption output size) // The fns for LensForEncryptedLog are never used, it's just to tell the compiler what the lens are // The to_bytes fn for ToBytesForUnencryptedLog is used to allow us to hash some generic T @@ -10,37 +9,38 @@ use dep::protocol_types::address::AztecAddress; // I could have omitted N from the trait, but wanted to keep it strictly for field arrs // TODO(1139): Once we enc inside the circuit, we will no longer need the oracle to return // anything, so we can remove this trait -trait LensForEncryptedLog { +trait LensForEncryptedLog { // N = note preimage input in fields - // M = encryption output len in fields (= N + 7 = N + 3 fields for addr, slot, type + 3.5 fields for AES data) - // L = encryption output len in bytes (= 32*M - 16) - fn output_fields(self: [Field; N]) -> [Field; M]; - fn output_bytes(self: [Field; N]) -> [u8; L]; + // M = encryption output len in bytes (= 480 + N * 32) + fn output_fields(self: [Field; N]) -> [Field; N]; + fn output_bytes(self: [Field; N]) -> [u8; M]; } -impl LensForEncryptedLog<1, 8, 240> for [Field; 1] { - fn output_fields(self) -> [Field; 8] {[self[0]; 8]} - fn output_bytes(self) -> [u8; 240] {[self[0] as u8; 240]} +impl LensForEncryptedLog<1, 512> for [Field; 1] { + fn output_fields(self) -> [Field; 1] {[self[0]; 1]} + fn output_bytes(self) -> [u8; 512] {[self[0] as u8; 512]} } -impl LensForEncryptedLog<2, 9, 272> for [Field; 2] { - fn output_fields(self) -> [Field; 9] {[self[0]; 9]} - fn output_bytes(self) -> [u8; 272] {[self[0] as u8; 272]} +impl LensForEncryptedLog<2, 544> for [Field; 2] { + fn output_fields(self) -> [Field; 2] {[self[0]; 2]} + fn output_bytes(self) -> [u8; 544] {[self[0] as u8; 544]} } -impl LensForEncryptedLog<3, 10, 304> for [Field; 3] { - fn output_fields(self) -> [Field; 10] {[self[0]; 10]} - fn output_bytes(self) -> [u8; 304] {[self[0] as u8; 304]} +impl LensForEncryptedLog<3, 576> for [Field; 3] { + fn output_fields(self) -> [Field; 3] {[self[0]; 3]} + fn output_bytes(self) -> [u8; 576] {[self[0] as u8; 576]} } -impl LensForEncryptedLog<4, 11, 336> for [Field; 4] { - fn output_fields(self) -> [Field; 11] {[self[0]; 11]} - fn output_bytes(self) -> [u8; 336] {[self[0] as u8; 336]} +impl LensForEncryptedLog<4, 608> for [Field; 4] { + fn output_fields(self) -> [Field; 4] {[self[0]; 4]} + fn output_bytes(self) -> [u8; 608] {[self[0] as u8; 608]} + } -impl LensForEncryptedLog<5, 12, 368> for [Field; 5] { - fn output_fields(self) -> [Field; 12] {[self[0]; 12]} - fn output_bytes(self) -> [u8; 368] {[self[0] as u8; 368]} +impl LensForEncryptedLog<5, 640> for [Field; 5] { + fn output_fields(self) -> [Field; 5] {[self[0]; 5]} + fn output_bytes(self) -> [u8; 640] {[self[0] as u8; 640]} } -impl LensForEncryptedLog<6, 13, 400> for [Field; 6] { - fn output_fields(self) -> [Field; 13] {[self[0]; 13]} - fn output_bytes(self) -> [u8; 400] {[self[0] as u8; 400]} +impl LensForEncryptedLog<6, 672> for [Field; 6] { + fn output_fields(self) -> [Field; 6] {[self[0]; 6]} + fn output_bytes(self) -> [u8; 672] {[self[0] as u8; 672]} + } // This trait defines the length of the inputs in bytes to diff --git a/noir-projects/aztec-nr/value-note/src/value_note.nr b/noir-projects/aztec-nr/value-note/src/value_note.nr index 06c01fcbaa4..7e057b8912e 100644 --- a/noir-projects/aztec-nr/value-note/src/value_note.nr +++ b/noir-projects/aztec-nr/value-note/src/value_note.nr @@ -47,7 +47,7 @@ impl NoteInterface for ValueNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field, ivpk_m: GrumpkinPoint) { - context.emit_note_encrypted_log( + context.encrypt_and_emit_note( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr index e8e2dd2f2e5..7de5917b11d 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr @@ -40,7 +40,7 @@ impl NoteInterface for SubscriptionNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field, ivpk_m: GrumpkinPoint) { - context.emit_note_encrypted_log( + context.encrypt_and_emit_note( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr b/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr index 2c3100cbc60..5efd1e93879 100644 --- a/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr +++ b/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr @@ -51,7 +51,7 @@ impl NoteInterface for CardNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field, ivpk_m: GrumpkinPoint) { - context.emit_note_encrypted_log( + context.encrypt_and_emit_note( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr index fa1b79f879d..f716dc5a5f8 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr @@ -24,7 +24,7 @@ impl NoteInterface for EcdsaPublicKeyNote { // [1] = x[31] // [2] = y[0..31] // [3] = y[31] - // [4] = owner + // [4] = npk_m_hash fn serialize_content(self) -> [Field; ECDSA_PUBLIC_KEY_NOTE_LEN] { let mut x: Field = 0; let mut y: Field = 0; @@ -86,7 +86,7 @@ impl NoteInterface for EcdsaPublicKeyNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field, ivpk_m: GrumpkinPoint) { - context.emit_note_encrypted_log( + context.encrypt_and_emit_note( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr index 3b675374f11..df6e72b61b3 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr @@ -39,7 +39,7 @@ impl NoteInterface for PublicKeyNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field, ivpk_m: GrumpkinPoint) { - context.emit_note_encrypted_log( + context.encrypt_and_emit_note( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr index 721dc23ad24..204023a21ab 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr @@ -51,7 +51,7 @@ impl NoteInterface for TokenNote { // We only bother inserting the note if non-empty to save funds on gas. // TODO: (#5901) This will be changed a lot, as it should use the updated encrypted log format if !(self.amount == U128::from_integer(0)) { - context.emit_note_encrypted_log( + context.encrypt_and_emit_note( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr index 721dc23ad24..204023a21ab 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr @@ -51,7 +51,7 @@ impl NoteInterface for TokenNote { // We only bother inserting the note if non-empty to save funds on gas. // TODO: (#5901) This will be changed a lot, as it should use the updated encrypted log format if !(self.amount == U128::from_integer(0)) { - context.emit_note_encrypted_log( + context.encrypt_and_emit_note( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/yarn-project/circuit-types/src/logs/encrypted_log_payload.test.ts b/yarn-project/circuit-types/src/logs/encrypted_log_payload.test.ts deleted file mode 100644 index bb62424faf0..00000000000 --- a/yarn-project/circuit-types/src/logs/encrypted_log_payload.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { AztecAddress, GrumpkinScalar } from '@aztec/circuits.js'; -import { Grumpkin } from '@aztec/circuits.js/barretenberg'; - -import { EncryptedLogPayload } from './encrypted_log_payload.js'; -import { L1NotePayload } from './l1_note_payload/l1_note_payload.js'; - -describe('encrypt and decrypt a full log', () => { - let grumpkin: Grumpkin; - - let ovsk: GrumpkinScalar; - let ivsk: GrumpkinScalar; - - let payload: EncryptedLogPayload; - let encrypted: Buffer; - - beforeAll(() => { - grumpkin = new Grumpkin(); - - ovsk = GrumpkinScalar.random(); - ivsk = GrumpkinScalar.random(); - - const ephSk = GrumpkinScalar.random(); - - const recipientAddress = AztecAddress.random(); - const ivpk = grumpkin.mul(Grumpkin.generator, ivsk); - - payload = EncryptedLogPayload.fromL1NotePayload(L1NotePayload.random()); - encrypted = payload.encrypt(ephSk, recipientAddress, ivpk, ovsk); - }); - - it('decrypt a log as incoming', () => { - const recreated = EncryptedLogPayload.decryptAsIncoming(encrypted, ivsk); - - expect(recreated.toBuffer()).toEqual(payload.toBuffer()); - }); - - it('decrypt a log as outgoing', () => { - const recreated = EncryptedLogPayload.decryptAsOutgoing(encrypted, ovsk); - - expect(recreated.toBuffer()).toEqual(payload.toBuffer()); - }); -}); diff --git a/yarn-project/circuit-types/src/logs/encrypted_log_payload.ts b/yarn-project/circuit-types/src/logs/encrypted_log_payload.ts deleted file mode 100644 index 6ef1cc82add..00000000000 --- a/yarn-project/circuit-types/src/logs/encrypted_log_payload.ts +++ /dev/null @@ -1,209 +0,0 @@ -import { - AztecAddress, - Fr, - type GrumpkinPrivateKey, - Point, - type PublicKey, - computeIvpkApp, - computeIvskApp, - computeOvskApp, - derivePublicKeyFromSecretKey, -} from '@aztec/circuits.js'; -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; - -import { EncryptedLogHeader } from './encrypted_log_header.js'; -import { EncryptedLogIncomingBody } from './encrypted_log_incoming_body.js'; -import { EncryptedLogOutgoingBody } from './encrypted_log_outgoing_body.js'; -import { type L1NotePayload } from './l1_note_payload/l1_note_payload.js'; -import { Note } from './l1_note_payload/note.js'; - -// A placeholder tag until we have a proper tag system in place. -const PLACEHOLDER_TAG = new Fr(33); - -// Both the incoming and the outgoing header are 48 bytes. -// 32 bytes for the address, and 16 bytes padding to follow PKCS#7 -const HEADER_SIZE = 48; - -// The outgoing body is constant size of 176 bytes. -// 160 bytes for the secret key, address, and public key, and 16 bytes padding to follow PKCS#7 -const OUTGOING_BODY_SIZE = 176; - -export class EncryptedLogPayload { - constructor( - /** - * A note as emitted from Noir contract. Can be used along with private key to compute nullifier. - */ - public note: Note, - /** - * Address of the contract this tx is interacting with. - */ - public contractAddress: AztecAddress, - /** - * Storage slot of the underlying note. - */ - public storageSlot: Fr, - /** - * Type identifier for the underlying note, required to determine how to compute its hash and nullifier. - */ - public noteTypeId: Fr, - ) {} - - toBuffer() { - return serializeToBuffer([this.note, this.contractAddress, this.storageSlot, this.noteTypeId]); - } - - static fromBuffer(buffer: Buffer | BufferReader): EncryptedLogPayload { - const reader = BufferReader.asReader(buffer); - return new EncryptedLogPayload( - reader.readObject(Note), - reader.readObject(AztecAddress), - Fr.fromBuffer(reader), - Fr.fromBuffer(reader), - ); - } - - static fromL1NotePayload(l1NotePayload: L1NotePayload) { - return new EncryptedLogPayload( - l1NotePayload.note, - l1NotePayload.contractAddress, - l1NotePayload.storageSlot, - l1NotePayload.noteTypeId, - ); - } - - /** - * Encrypts a note payload for a given recipient and sender. - * Creates an incoming log the the recipient using the recipient's ivsk, and - * an outgoing log for the sender using the sender's ovsk. - * - * @param ephSk - An ephemeral secret key used for the encryption - * @param recipient - The recipient address, retrievable by the sender for his logs - * @param ivpk - The incoming viewing public key of the recipient - * @param ovsk - The outgoing viewing secret key of the sender - * @returns A buffer containing the encrypted log payload - */ - public encrypt(ephSk: GrumpkinPrivateKey, recipient: AztecAddress, ivpk: PublicKey, ovsk: GrumpkinPrivateKey) { - const ephPk = derivePublicKeyFromSecretKey(ephSk); - const ovpk = derivePublicKeyFromSecretKey(ovsk); - - const header = new EncryptedLogHeader(this.contractAddress); - - const incomingHeaderCiphertext = header.computeCiphertext(ephSk, ivpk); - const outgoingHeaderCiphertext = header.computeCiphertext(ephSk, ovpk); - - const ivpkApp = computeIvpkApp(ivpk, this.contractAddress); - - const incomingBodyCiphertext = new EncryptedLogIncomingBody( - this.storageSlot, - this.noteTypeId, - this.note, - ).computeCiphertext(ephSk, ivpkApp); - - const ovskApp = computeOvskApp(ovsk, this.contractAddress); - - const outgoingBodyCiphertext = new EncryptedLogOutgoingBody(ephSk, recipient, ivpkApp).computeCiphertext( - ovskApp, - ephPk, - ); - - return Buffer.concat([ - PLACEHOLDER_TAG.toBuffer(), - PLACEHOLDER_TAG.toBuffer(), - ephPk.toBuffer(), - incomingHeaderCiphertext, - outgoingHeaderCiphertext, - outgoingBodyCiphertext, - incomingBodyCiphertext, - ]); - } - - /** - * Decrypts a ciphertext as an incoming log. - * - * This is executable by the recipient of the note, and uses the ivsk to decrypt the payload. - * The outgoing parts of the log are ignored entirely. - * - * Produces the same output as `decryptAsOutgoing`. - * - * @param ciphertext - The ciphertext for the log - * @param ivsk - The incoming viewing secret key, used to decrypt the logs - * @returns The decrypted log payload - */ - public static decryptAsIncoming(ciphertext: Buffer | bigint[], ivsk: GrumpkinPrivateKey) { - const input = Buffer.isBuffer(ciphertext) ? ciphertext : Buffer.from(ciphertext.map((x: bigint) => Number(x))); - const reader = BufferReader.asReader(input); - - // We don't use the tags as part of the decryption here, we just gotta read to skip them. - reader.readObject(Fr); // incoming tag - reader.readObject(Fr); // outgoing tag - - const ephPk = reader.readObject(Point); - - const incomingHeader = EncryptedLogHeader.fromCiphertext(reader.readBytes(HEADER_SIZE), ivsk, ephPk); - - // Skipping the outgoing header and body - reader.readBytes(HEADER_SIZE); - reader.readBytes(OUTGOING_BODY_SIZE); - - // The incoming can be of variable size, so we read until the end - const incomingBodySlice = reader.readToEnd(); - - const ivskApp = computeIvskApp(ivsk, incomingHeader.address); - const incomingBody = EncryptedLogIncomingBody.fromCiphertext(incomingBodySlice, ivskApp, ephPk); - - return new EncryptedLogPayload( - incomingBody.note, - incomingHeader.address, - incomingBody.storageSlot, - incomingBody.noteTypeId, - ); - } - - /** - * Decrypts a ciphertext as an outgoing log. - * - * This is executable by the sender of the note, and uses the ovsk to decrypt the payload. - * The outgoing parts are decrypted to retrieve information that allows the sender to - * decrypt the incoming log, and learn about the note contents. - * - * Produces the same output as `decryptAsIncoming`. - * - * @param ciphertext - The ciphertext for the log - * @param ovsk - The outgoing viewing secret key, used to decrypt the logs - * @returns The decrypted log payload - */ - public static decryptAsOutgoing(ciphertext: Buffer | bigint[], ovsk: GrumpkinPrivateKey) { - const input = Buffer.isBuffer(ciphertext) ? ciphertext : Buffer.from(ciphertext.map((x: bigint) => Number(x))); - const reader = BufferReader.asReader(input); - - // We don't use the tags as part of the decryption here, we just gotta read to skip them. - reader.readObject(Fr); // incoming tag - reader.readObject(Fr); // outgoing tag - - const ephPk = reader.readObject(Point); - - // Skip the incoming header - reader.readBytes(HEADER_SIZE); - - const outgoingHeader = EncryptedLogHeader.fromCiphertext(reader.readBytes(HEADER_SIZE), ovsk, ephPk); - - const ovskApp = computeOvskApp(ovsk, outgoingHeader.address); - const outgoingBody = EncryptedLogOutgoingBody.fromCiphertext(reader.readBytes(OUTGOING_BODY_SIZE), ovskApp, ephPk); - - // The incoming can be of variable size, so we read until the end - const incomingBodySlice = reader.readToEnd(); - - const incomingBody = EncryptedLogIncomingBody.fromCiphertext( - incomingBodySlice, - outgoingBody.ephSk, - outgoingBody.recipientIvpkApp, - ); - - return new EncryptedLogPayload( - incomingBody.note, - outgoingHeader.address, - incomingBody.storageSlot, - incomingBody.noteTypeId, - ); - } -} diff --git a/yarn-project/circuit-types/src/logs/index.ts b/yarn-project/circuit-types/src/logs/index.ts index 0e4b8200391..6b4a4199c28 100644 --- a/yarn-project/circuit-types/src/logs/index.ts +++ b/yarn-project/circuit-types/src/logs/index.ts @@ -10,6 +10,6 @@ export * from './l1_note_payload/index.js'; export * from './tx_l2_logs.js'; export * from './unencrypted_l2_log.js'; export * from './extended_unencrypted_l2_log.js'; -export * from './encrypted_log_header.js'; -export * from './encrypted_log_incoming_body.js'; -export * from './encrypted_log_outgoing_body.js'; +export * from './l1_note_payload/encrypted_log_header.js'; +export * from './l1_note_payload/encrypted_log_incoming_body.js'; +export * from './l1_note_payload/encrypted_log_outgoing_body.js'; diff --git a/yarn-project/circuit-types/src/logs/l1_note_payload/encrypt_buffer.test.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypt_buffer.test.ts index d8a0c4b9998..3db91fe87a0 100644 --- a/yarn-project/circuit-types/src/logs/l1_note_payload/encrypt_buffer.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypt_buffer.test.ts @@ -3,7 +3,8 @@ import { Grumpkin } from '@aztec/circuits.js/barretenberg'; import { randomBytes } from '@aztec/foundation/crypto'; import { updateInlineTestData } from '@aztec/foundation/testing'; -import { decryptBuffer, deriveAESSecret, encryptBuffer } from './encrypt_buffer.js'; +import { decryptBuffer, encryptBuffer } from './encrypt_buffer.js'; +import { deriveAESSecret } from './encryption_utils.js'; describe('encrypt buffer', () => { let grumpkin: Grumpkin; diff --git a/yarn-project/circuit-types/src/logs/l1_note_payload/encrypt_buffer.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypt_buffer.ts index 28262ff3ba6..dd8e954a7af 100644 --- a/yarn-project/circuit-types/src/logs/l1_note_payload/encrypt_buffer.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypt_buffer.ts @@ -1,30 +1,10 @@ -import { GeneratorIndex, type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js'; +import { type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; -import { sha256 } from '@aztec/foundation/crypto'; import { Point } from '@aztec/foundation/fields'; -import { numToUInt8 } from '@aztec/foundation/serialize'; import { createCipheriv, createDecipheriv } from 'browserify-cipher'; -/** - * Derive an AES secret key using Elliptic Curve Diffie-Hellman (ECDH) and SHA-256. - * The function takes in an ECDH public key, a private key, and a Grumpkin instance to compute - * the shared secret. The shared secret is then hashed using SHA-256 to produce the final - * AES secret key. - * - * @param secretKey - The secret key used to derive shared secret. - * @param publicKey - The public key used to derive shared secret. - * @returns A derived AES secret key. - * TODO(#5726): This function is called point_to_symmetric_key in Noir. I don't like that name much since point is not - * the only input of the function. Unify naming once we have a better name. - */ -export function deriveAESSecret(secretKey: GrumpkinPrivateKey, publicKey: PublicKey): Buffer { - const curve = new Grumpkin(); - const sharedSecret = curve.mul(publicKey, secretKey); - const secretBuffer = Buffer.concat([sharedSecret.toBuffer(), numToUInt8(GeneratorIndex.SYMMETRIC_KEY)]); - const hash = sha256(secretBuffer); - return hash; -} +import { deriveAESSecret } from './encryption_utils.js'; /** * Encrypt a given data buffer using the owner's public key and an ephemeral private key. diff --git a/yarn-project/circuit-types/src/logs/encrypted_log_header.test.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_header.test.ts similarity index 100% rename from yarn-project/circuit-types/src/logs/encrypted_log_header.test.ts rename to yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_header.test.ts diff --git a/yarn-project/circuit-types/src/logs/encrypted_log_header.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_header.ts similarity index 96% rename from yarn-project/circuit-types/src/logs/encrypted_log_header.ts rename to yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_header.ts index baef6cc264b..ac19b40cc3c 100644 --- a/yarn-project/circuit-types/src/logs/encrypted_log_header.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_header.ts @@ -1,7 +1,7 @@ import { AztecAddress, type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js'; import { Aes128 } from '@aztec/circuits.js/barretenberg'; -import { deriveAESSecret } from './l1_note_payload/encrypt_buffer.js'; +import { deriveAESSecret } from './encryption_utils.js'; /** * An encrypted log header, containing the address of the log along with utility diff --git a/yarn-project/circuit-types/src/logs/encrypted_log_incoming_body.test.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_incoming_body.test.ts similarity index 97% rename from yarn-project/circuit-types/src/logs/encrypted_log_incoming_body.test.ts rename to yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_incoming_body.test.ts index bb1ab2c2588..934621b508e 100644 --- a/yarn-project/circuit-types/src/logs/encrypted_log_incoming_body.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_incoming_body.test.ts @@ -3,7 +3,7 @@ import { Grumpkin } from '@aztec/circuits.js/barretenberg'; import { updateInlineTestData } from '@aztec/foundation/testing'; import { EncryptedLogIncomingBody } from './encrypted_log_incoming_body.js'; -import { Note } from './l1_note_payload/note.js'; +import { Note } from './note.js'; describe('encrypt log incoming body', () => { let grumpkin: Grumpkin; diff --git a/yarn-project/circuit-types/src/logs/encrypted_log_incoming_body.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_incoming_body.ts similarity index 97% rename from yarn-project/circuit-types/src/logs/encrypted_log_incoming_body.ts rename to yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_incoming_body.ts index 45e1f5382bf..3001d6963de 100644 --- a/yarn-project/circuit-types/src/logs/encrypted_log_incoming_body.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_incoming_body.ts @@ -2,7 +2,8 @@ import { Fr, type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js' import { Aes128 } from '@aztec/circuits.js/barretenberg'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; -import { Note, deriveAESSecret } from './l1_note_payload/index.js'; +import { deriveAESSecret } from './encryption_utils.js'; +import { Note } from './note.js'; export class EncryptedLogIncomingBody { constructor(public storageSlot: Fr, public noteTypeId: Fr, public note: Note) {} diff --git a/yarn-project/circuit-types/src/logs/encrypted_log_outgoing_body.test.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_outgoing_body.test.ts similarity index 100% rename from yarn-project/circuit-types/src/logs/encrypted_log_outgoing_body.test.ts rename to yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_outgoing_body.test.ts diff --git a/yarn-project/circuit-types/src/logs/encrypted_log_outgoing_body.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_outgoing_body.ts similarity index 100% rename from yarn-project/circuit-types/src/logs/encrypted_log_outgoing_body.ts rename to yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_outgoing_body.ts diff --git a/yarn-project/circuit-types/src/logs/l1_note_payload/encryption_utils.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encryption_utils.ts new file mode 100644 index 00000000000..2673af92c61 --- /dev/null +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/encryption_utils.ts @@ -0,0 +1,24 @@ +import { GeneratorIndex, type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js'; +import { Grumpkin } from '@aztec/circuits.js/barretenberg'; +import { sha256 } from '@aztec/foundation/crypto'; +import { numToUInt8 } from '@aztec/foundation/serialize'; + +/** + * Derive an AES secret key using Elliptic Curve Diffie-Hellman (ECDH) and SHA-256. + * The function takes in an ECDH public key, a private key, and a Grumpkin instance to compute + * the shared secret. The shared secret is then hashed using SHA-256 to produce the final + * AES secret key. + * + * @param secretKey - The secret key used to derive shared secret. + * @param publicKey - The public key used to derive shared secret. + * @returns A derived AES secret key. + * TODO(#5726): This function is called point_to_symmetric_key in Noir. I don't like that name much since point is not + * the only input of the function. Unify naming once we have a better name. + */ +export function deriveAESSecret(secretKey: GrumpkinPrivateKey, publicKey: PublicKey): Buffer { + const curve = new Grumpkin(); + const sharedSecret = curve.mul(publicKey, secretKey); + const secretBuffer = Buffer.concat([sharedSecret.toBuffer(), numToUInt8(GeneratorIndex.SYMMETRIC_KEY)]); + const hash = sha256(secretBuffer); + return hash; +} diff --git a/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.test.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.test.ts index 288e46db36d..321a8e82985 100644 --- a/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.test.ts @@ -1,5 +1,6 @@ +import { AztecAddress } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; -import { GrumpkinScalar, Point } from '@aztec/foundation/fields'; +import { GrumpkinScalar } from '@aztec/foundation/fields'; import { L1NotePayload } from './l1_note_payload.js'; @@ -16,22 +17,36 @@ describe('L1 Note Payload', () => { expect(L1NotePayload.fromBuffer(buf)).toEqual(payload); }); - it('convert to and from encrypted buffer', () => { - const payload = L1NotePayload.random(); - const ownerPrivKey = GrumpkinScalar.random(); - const ownerPubKey = grumpkin.mul(Grumpkin.generator, ownerPrivKey); - const encrypted = payload.toEncryptedBuffer(ownerPubKey); - const decrypted = L1NotePayload.fromEncryptedBuffer(encrypted, ownerPrivKey); - expect(decrypted).not.toBeUndefined(); - expect(decrypted).toEqual(payload); - }); + describe('encrypt and decrypt a full log', () => { + let ovsk: GrumpkinScalar; + let ivsk: GrumpkinScalar; - it('return undefined if unable to decrypt the encrypted buffer', () => { - const payload = L1NotePayload.random(); - const ownerPubKey = Point.random(); - const encrypted = payload.toEncryptedBuffer(ownerPubKey); - const randomPrivKey = GrumpkinScalar.random(); - const decrypted = L1NotePayload.fromEncryptedBuffer(encrypted, randomPrivKey); - expect(decrypted).toBeUndefined(); + let payload: L1NotePayload; + let encrypted: Buffer; + + beforeAll(() => { + ovsk = GrumpkinScalar.random(); + ivsk = GrumpkinScalar.random(); + + const ephSk = GrumpkinScalar.random(); + + const recipientAddress = AztecAddress.random(); + const ivpk = grumpkin.mul(Grumpkin.generator, ivsk); + + payload = L1NotePayload.random(); + encrypted = payload.encrypt(ephSk, recipientAddress, ivpk, ovsk); + }); + + it('decrypt a log as incoming', () => { + const recreated = L1NotePayload.decryptAsIncoming(encrypted, ivsk); + + expect(recreated.toBuffer()).toEqual(payload.toBuffer()); + }); + + it('decrypt a log as outgoing', () => { + const recreated = L1NotePayload.decryptAsOutgoing(encrypted, ovsk); + + expect(recreated.toBuffer()).toEqual(payload.toBuffer()); + }); }); }); diff --git a/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.ts index 463512782a7..285befd192d 100644 --- a/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.ts @@ -1,10 +1,27 @@ -import { AztecAddress, type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js'; -import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; +import { + AztecAddress, + type GrumpkinPrivateKey, + type PublicKey, + computeIvpkApp, + computeIvskApp, + computeOvskApp, + derivePublicKeyFromSecretKey, +} from '@aztec/circuits.js'; +import { Fr, Point } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; -import { decryptBuffer, encryptBuffer } from './encrypt_buffer.js'; +import { EncryptedLogHeader } from './encrypted_log_header.js'; +import { EncryptedLogIncomingBody } from './encrypted_log_incoming_body.js'; +import { EncryptedLogOutgoingBody } from './encrypted_log_outgoing_body.js'; import { Note } from './note.js'; +// Both the incoming and the outgoing header are 48 bytes. +// 32 bytes for the address, and 16 bytes padding to follow PKCS#7 +const HEADER_SIZE = 48; + +// The outgoing body is constant size of 176 bytes. +// 160 bytes for the secret key, address, and public key, and 16 bytes padding to follow PKCS#7 +const OUTGOING_BODY_SIZE = 176; /** * A class which wraps note data which is pushed on L1. * @remarks This data is required to compute a nullifier/to spend a note. Along with that this class contains @@ -21,7 +38,7 @@ export class L1NotePayload { */ public contractAddress: AztecAddress, /** - * Storage slot of the contract this tx is interacting with. + * Storage slot of the underlying note. */ public storageSlot: Fr, /** @@ -54,34 +71,136 @@ export class L1NotePayload { } /** - * Encrypt the L1NotePayload object using the owner's public key and the ephemeral private key. - * @param incomingViewingPubKey - Public key of the owner of the L1NotePayload object. - * @returns The encrypted L1NotePayload object. + * Create a random L1NotePayload object (useful for testing purposes). + * @returns A random L1NotePayload object. */ - public toEncryptedBuffer(incomingViewingPubKey: PublicKey): Buffer { - const ephSecretKey: GrumpkinPrivateKey = GrumpkinScalar.random(); - return encryptBuffer(this.toBuffer(), ephSecretKey, incomingViewingPubKey); + static random() { + return new L1NotePayload(Note.random(), AztecAddress.random(), Fr.random(), Fr.random()); } /** - * Decrypts the L1NotePayload object using the owner's incoming viewing secret key. - * @param data - Encrypted L1NotePayload object. - * @param incomingViewingSecretKey - Incoming viewing secret key of the owner of the L1NotePayload object. - * @returns Instance of L1NotePayload if the decryption was successful, undefined otherwise. + * Encrypts a note payload for a given recipient and sender. + * Creates an incoming log the the recipient using the recipient's ivsk, and + * an outgoing log for the sender using the sender's ovsk. + * + * @param ephSk - An ephemeral secret key used for the encryption + * @param recipient - The recipient address, retrievable by the sender for his logs + * @param ivpk - The incoming viewing public key of the recipient + * @param ovsk - The outgoing viewing secret key of the sender + * @returns A buffer containing the encrypted log payload */ - static fromEncryptedBuffer(data: Buffer, incomingViewingSecretKey: GrumpkinPrivateKey): L1NotePayload | undefined { - const buf = decryptBuffer(data, incomingViewingSecretKey); - if (!buf) { - return; - } - return L1NotePayload.fromBuffer(buf); + public encrypt(ephSk: GrumpkinPrivateKey, recipient: AztecAddress, ivpk: PublicKey, ovsk: GrumpkinPrivateKey) { + const ephPk = derivePublicKeyFromSecretKey(ephSk); + const ovpk = derivePublicKeyFromSecretKey(ovsk); + + const header = new EncryptedLogHeader(this.contractAddress); + + const incomingHeaderCiphertext = header.computeCiphertext(ephSk, ivpk); + const outgoingHeaderCiphertext = header.computeCiphertext(ephSk, ovpk); + + const ivpkApp = computeIvpkApp(ivpk, this.contractAddress); + + const incomingBodyCiphertext = new EncryptedLogIncomingBody( + this.storageSlot, + this.noteTypeId, + this.note, + ).computeCiphertext(ephSk, ivpkApp); + + const ovskApp = computeOvskApp(ovsk, this.contractAddress); + + const outgoingBodyCiphertext = new EncryptedLogOutgoingBody(ephSk, recipient, ivpkApp).computeCiphertext( + ovskApp, + ephPk, + ); + + return Buffer.concat([ + ephPk.toBuffer(), + incomingHeaderCiphertext, + outgoingHeaderCiphertext, + outgoingBodyCiphertext, + incomingBodyCiphertext, + ]); } /** - * Create a random L1NotePayload object (useful for testing purposes). - * @returns A random L1NotePayload object. + * Decrypts a ciphertext as an incoming log. + * + * This is executable by the recipient of the note, and uses the ivsk to decrypt the payload. + * The outgoing parts of the log are ignored entirely. + * + * Produces the same output as `decryptAsOutgoing`. + * + * @param ciphertext - The ciphertext for the log + * @param ivsk - The incoming viewing secret key, used to decrypt the logs + * @returns The decrypted log payload */ - static random() { - return new L1NotePayload(Note.random(), AztecAddress.random(), Fr.random(), Fr.random()); + public static decryptAsIncoming(ciphertext: Buffer | bigint[], ivsk: GrumpkinPrivateKey) { + const input = Buffer.isBuffer(ciphertext) ? ciphertext : Buffer.from(ciphertext.map((x: bigint) => Number(x))); + const reader = BufferReader.asReader(input); + + const ephPk = reader.readObject(Point); + + const incomingHeader = EncryptedLogHeader.fromCiphertext(reader.readBytes(HEADER_SIZE), ivsk, ephPk); + + // Skipping the outgoing header and body + reader.readBytes(HEADER_SIZE); + reader.readBytes(OUTGOING_BODY_SIZE); + + // The incoming can be of variable size, so we read until the end + const incomingBodySlice = reader.readToEnd(); + + const ivskApp = computeIvskApp(ivsk, incomingHeader.address); + const incomingBody = EncryptedLogIncomingBody.fromCiphertext(incomingBodySlice, ivskApp, ephPk); + + return new L1NotePayload( + incomingBody.note, + incomingHeader.address, + incomingBody.storageSlot, + incomingBody.noteTypeId, + ); + } + + /** + * Decrypts a ciphertext as an outgoing log. + * + * This is executable by the sender of the note, and uses the ovsk to decrypt the payload. + * The outgoing parts are decrypted to retrieve information that allows the sender to + * decrypt the incoming log, and learn about the note contents. + * + * Produces the same output as `decryptAsIncoming`. + * + * @param ciphertext - The ciphertext for the log + * @param ovsk - The outgoing viewing secret key, used to decrypt the logs + * @returns The decrypted log payload + */ + public static decryptAsOutgoing(ciphertext: Buffer | bigint[], ovsk: GrumpkinPrivateKey) { + const input = Buffer.isBuffer(ciphertext) ? ciphertext : Buffer.from(ciphertext.map((x: bigint) => Number(x))); + const reader = BufferReader.asReader(input); + + const ephPk = reader.readObject(Point); + + // Skip the incoming header + reader.readBytes(HEADER_SIZE); + + const outgoingHeader = EncryptedLogHeader.fromCiphertext(reader.readBytes(HEADER_SIZE), ovsk, ephPk); + + const ovskApp = computeOvskApp(ovsk, outgoingHeader.address); + const outgoingBody = EncryptedLogOutgoingBody.fromCiphertext(reader.readBytes(OUTGOING_BODY_SIZE), ovskApp, ephPk); + + // The incoming can be of variable size, so we read until the end + const incomingBodySlice = reader.readToEnd(); + + const incomingBody = EncryptedLogIncomingBody.fromCiphertext( + incomingBodySlice, + outgoingBody.ephSk, + outgoingBody.recipientIvpkApp, + ); + + return new L1NotePayload( + incomingBody.note, + outgoingHeader.address, + incomingBody.storageSlot, + incomingBody.noteTypeId, + ); } } diff --git a/yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.test.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.test.ts index bbd171f3702..392291c794b 100644 --- a/yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.test.ts @@ -1,5 +1,6 @@ +import { AztecAddress } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; -import { GrumpkinScalar, Point } from '@aztec/foundation/fields'; +import { GrumpkinScalar } from '@aztec/foundation/fields'; import { L1NotePayload } from './l1_note_payload.js'; import { TaggedNote } from './tagged_note.js'; @@ -18,24 +19,39 @@ describe('L1 Note Payload', () => { expect(TaggedNote.fromBuffer(buf).notePayload).toEqual(taggedNote.notePayload); }); - it('convert to and from encrypted buffer', () => { - const payload = L1NotePayload.random(); - const taggedNote = new TaggedNote(payload); - const ownerPrivKey = GrumpkinScalar.random(); - const ownerPubKey = grumpkin.mul(Grumpkin.generator, ownerPrivKey); - const encrypted = taggedNote.toEncryptedBuffer(ownerPubKey); - const decrypted = TaggedNote.fromEncryptedBuffer(encrypted, ownerPrivKey); - expect(decrypted).not.toBeUndefined(); - expect(decrypted?.notePayload).toEqual(payload); - }); + describe('encrypt and decrypt a full log', () => { + let ovsk: GrumpkinScalar; + let ivsk: GrumpkinScalar; - it('return undefined if unable to decrypt the encrypted buffer', () => { - const payload = L1NotePayload.random(); - const taggedNote = new TaggedNote(payload); - const ownerPubKey = Point.random(); - const encrypted = taggedNote.toEncryptedBuffer(ownerPubKey); - const randomPrivKey = GrumpkinScalar.random(); - const decrypted = TaggedNote.fromEncryptedBuffer(encrypted, randomPrivKey); - expect(decrypted).toBeUndefined(); + let taggedNote: TaggedNote; + let encrypted: Buffer; + + beforeAll(() => { + ovsk = GrumpkinScalar.random(); + ivsk = GrumpkinScalar.random(); + + const ephSk = GrumpkinScalar.random(); + + const recipientAddress = AztecAddress.random(); + const ivpk = grumpkin.mul(Grumpkin.generator, ivsk); + + const payload = L1NotePayload.random(); + + taggedNote = new TaggedNote(payload); + + encrypted = taggedNote.encrypt(ephSk, recipientAddress, ivpk, ovsk); + }); + + it('decrypt a log as incoming', () => { + const recreated = TaggedNote.decryptAsIncoming(encrypted, ivsk); + + expect(recreated?.toBuffer()).toEqual(taggedNote.toBuffer()); + }); + + it('decrypt a log as outgoing', () => { + const recreated = TaggedNote.decryptAsOutgoing(encrypted, ovsk); + + expect(recreated?.toBuffer()).toEqual(taggedNote.toBuffer()); + }); }); }); diff --git a/yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.ts index 4e698e382eb..aa1e45a1aa1 100644 --- a/yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.ts @@ -1,4 +1,4 @@ -import { type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js'; +import { type AztecAddress, type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; @@ -11,7 +11,11 @@ const PLACEHOLDER_TAG = new Fr(33); * Encrypted note payload with a tag used for retrieval by clients. */ export class TaggedNote { - constructor(public notePayload: L1NotePayload, public tag = PLACEHOLDER_TAG) {} + constructor( + public notePayload: L1NotePayload, + public incomingTag = PLACEHOLDER_TAG, + public outgoingTag = PLACEHOLDER_TAG, + ) {} /** * Deserializes the TaggedNote object from a Buffer. @@ -20,9 +24,10 @@ export class TaggedNote { */ static fromBuffer(buffer: Buffer | BufferReader): TaggedNote { const reader = BufferReader.asReader(buffer); - const tag = Fr.fromBuffer(reader); + const incomingTag = Fr.fromBuffer(reader); + const outgoingTag = Fr.fromBuffer(reader); const payload = L1NotePayload.fromBuffer(reader); - return new TaggedNote(payload, tag); + return new TaggedNote(payload, incomingTag, outgoingTag); } /** @@ -30,39 +35,53 @@ export class TaggedNote { * @returns Buffer representation of the TaggedNote object (unencrypted). */ public toBuffer(): Buffer { - return serializeToBuffer(this.tag, this.notePayload); + return serializeToBuffer(this.incomingTag, this.outgoingTag, this.notePayload); } - /** - * Encrypt the L1NotePayload object using the owner's public key and the ephemeral private key, then attach the tag. - * @param ownerPubKey - Public key of the owner of the TaggedNote object. - * @returns The encrypted TaggedNote object. - */ - public toEncryptedBuffer(ownerPubKey: PublicKey): Buffer { - const encryptedL1NotePayload = this.notePayload.toEncryptedBuffer(ownerPubKey); - return serializeToBuffer(this.tag, encryptedL1NotePayload); + static random(): TaggedNote { + return new TaggedNote(L1NotePayload.random()); } - /** - * Decrypts the L1NotePayload object using the owner's private key. - * @param data - Encrypted TaggedNote object. - * @param ownerPrivKey - Private key of the owner of the TaggedNote object. - * @returns Instance of TaggedNote if the decryption was successful, undefined otherwise. - */ - static fromEncryptedBuffer(data: Buffer, ownerPrivKey: GrumpkinPrivateKey): TaggedNote | undefined { - const reader = BufferReader.asReader(data); - const tag = Fr.fromBuffer(reader); - - const encryptedL1NotePayload = reader.readToEnd(); + public encrypt( + ephSk: GrumpkinPrivateKey, + recipient: AztecAddress, + ivpk: PublicKey, + ovsk: GrumpkinPrivateKey, + ): Buffer { + return serializeToBuffer( + this.incomingTag, + this.outgoingTag, + this.notePayload.encrypt(ephSk, recipient, ivpk, ovsk), + ); + } - const payload = L1NotePayload.fromEncryptedBuffer(encryptedL1NotePayload, ownerPrivKey); - if (!payload) { + static decryptAsIncoming(data: Buffer | bigint[], ivsk: GrumpkinPrivateKey) { + // Right now heavily abusing that we will likely fail if bad decryption + // as some field will likely end up not being in the field etc. + try { + const input = Buffer.isBuffer(data) ? data : Buffer.from(data.map((x: bigint) => Number(x))); + const reader = BufferReader.asReader(input); + const incomingTag = Fr.fromBuffer(reader); + const outgoingTag = Fr.fromBuffer(reader); + const payload = L1NotePayload.decryptAsIncoming(reader.readToEnd(), ivsk); + return new TaggedNote(payload, incomingTag, outgoingTag); + } catch (e) { return; } - return new TaggedNote(payload, tag); } - static random(): TaggedNote { - return new TaggedNote(L1NotePayload.random()); + static decryptAsOutgoing(data: Buffer | bigint[], ovsk: GrumpkinPrivateKey) { + // Right now heavily abusing that we will likely fail if bad decryption + // as some field will likely end up not being in the field etc. + try { + const input = Buffer.isBuffer(data) ? data : Buffer.from(data.map((x: bigint) => Number(x))); + const reader = BufferReader.asReader(input); + const incomingTag = Fr.fromBuffer(reader); + const outgoingTag = Fr.fromBuffer(reader); + const payload = L1NotePayload.decryptAsOutgoing(reader.readToEnd(), ovsk); + return new TaggedNote(payload, incomingTag, outgoingTag); + } catch (e) { + return; + } } } diff --git a/yarn-project/end-to-end/src/benchmarks/bench_tx_size_fees.test.ts b/yarn-project/end-to-end/src/benchmarks/bench_tx_size_fees.test.ts index a73f08a32d2..a55331b5f3b 100644 --- a/yarn-project/end-to-end/src/benchmarks/bench_tx_size_fees.test.ts +++ b/yarn-project/end-to-end/src/benchmarks/bench_tx_size_fees.test.ts @@ -67,28 +67,28 @@ describe('benchmarks/tx_size_fees', () => { 'native fee', () => NativeFeePaymentMethod.create(aliceWallet), // DA: - // non-rev: 1 nullifiers, overhead; rev: 2 note hashes, 1 nullifier, 616 B enc logs, 0 B unenc logs, teardown + // non-rev: 1 nullifiers, overhead; rev: 2 note hashes, 1 nullifier, 1168 B enc note logs, 0 B enc logs, 0 B unenc logs, teardown // L2: // non-rev: 0; rev: 0 - 200012416n, + 200021120n, ], [ 'public fee', () => Promise.resolve(new PublicFeePaymentMethod(token.address, fpc.address, aliceWallet)), // DA: - // non-rev: 1 nullifiers, overhead; rev: 2 note hashes, 1 nullifier, 616 B enc logs, 0 B unenc logs, teardown + // non-rev: 1 nullifiers, overhead; rev: 2 note hashes, 1 nullifier, 1168 B enc note logs, 0 B enc logs,0 B unenc logs, teardown // L2: // non-rev: 0; rev: 0 - 200012416n, + 200021120n, ], [ 'private fee', () => Promise.resolve(new PrivateFeePaymentMethod(token.address, fpc.address, aliceWallet)), // DA: - // non-rev: 3 nullifiers, overhead; rev: 2 note hashes, 616 B enc logs, 0 B unenc logs, teardown + // non-rev: 3 nullifiers, overhead; rev: 2 note hashes, 1168 B enc note logs, 0 B enc logs, 0 B unenc logs, teardown // L2: // non-rev: 0; rev: 0 - 200012928n, + 200021632n, ], ] as const)( 'sends a tx with a fee with %s payment method', diff --git a/yarn-project/end-to-end/src/e2e_block_building.test.ts b/yarn-project/end-to-end/src/e2e_block_building.test.ts index cbafb01600a..e923674e705 100644 --- a/yarn-project/end-to-end/src/e2e_block_building.test.ts +++ b/yarn-project/end-to-end/src/e2e_block_building.test.ts @@ -273,7 +273,7 @@ describe('e2e_block_building', () => { expect(rct.status).toEqual('mined'); const decryptedLogs = tx.noteEncryptedLogs .unrollLogs() - .map(l => TaggedNote.fromEncryptedBuffer(l.data, keys.masterIncomingViewingSecretKey)); + .map(l => TaggedNote.decryptAsIncoming(l.data, keys.masterIncomingViewingSecretKey)); const notevalues = decryptedLogs.map(l => l?.notePayload.note.items[0]); expect(notevalues[0]).toEqual(new Fr(10)); expect(notevalues[1]).toEqual(new Fr(11)); diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator.ts b/yarn-project/prover-client/src/orchestrator/orchestrator.ts index 48d144b05fd..eb7198b7607 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator.ts @@ -425,6 +425,7 @@ export class ProvingOrchestrator { .toBuffer() .equals(tx.processedTx.encryptedLogs.hash()) ) { + // @todo This rejection messages is never seen. Never making it out to the logs provingState.reject( `Encrypted logs hash mismatch: ${ tx.baseRollupInputs.kernelData.publicInputs.end.encryptedLogsHash diff --git a/yarn-project/pxe/src/note_processor/note_processor.test.ts b/yarn-project/pxe/src/note_processor/note_processor.test.ts index 8c2ba5d8b01..e9c656c09d3 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.test.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.test.ts @@ -10,6 +10,7 @@ import { TaggedNote, } from '@aztec/circuit-types'; import { + AztecAddress, Fr, type GrumpkinPrivateKey, INITIAL_L2_BLOCK_NUM, @@ -18,7 +19,7 @@ import { deriveKeys, } from '@aztec/circuits.js'; import { pedersenHash } from '@aztec/foundation/crypto'; -import { Point } from '@aztec/foundation/fields'; +import { GrumpkinScalar, Point } from '@aztec/foundation/fields'; import { openTmpStore } from '@aztec/kv-store/utils'; import { type AcirSimulator } from '@aztec/simulator'; @@ -62,7 +63,7 @@ describe('Note Processor', () => { const logs: EncryptedFunctionL2Logs[] = []; for (let noteIndex = 0; noteIndex < MAX_NEW_NOTE_HASHES_PER_TX; ++noteIndex) { const isOwner = ownedDataIndices.includes(noteIndex); - const publicKey = isOwner ? ownerMasterIncomingViewingPublicKey : Point.random(); + const ivsk = isOwner ? ownerMasterIncomingViewingPublicKey : Point.random(); const note = (isOwner && ownedNotes[usedOwnedNote]) || TaggedNote.random(); usedOwnedNote += note === ownedNotes[usedOwnedNote] ? 1 : 0; newNotes.push(note); @@ -70,7 +71,12 @@ describe('Note Processor', () => { ownedL1NotePayloads.push(note.notePayload); } // const encryptedNote = - const log = note.toEncryptedBuffer(publicKey); + //const log = note.toEncryptedBuffer(publicKey); + + const ephSk = GrumpkinScalar.random(); + const ovsk = GrumpkinScalar.random(); + const recipient = AztecAddress.random(); + const log = note.encrypt(ephSk, recipient, ivsk, ovsk); // 1 tx containing 1 function invocation containing 1 log logs.push(new EncryptedFunctionL2Logs([new EncryptedL2Log(log)])); } diff --git a/yarn-project/pxe/src/note_processor/note_processor.ts b/yarn-project/pxe/src/note_processor/note_processor.ts index 3eaa6b3006d..f8b4c13b571 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.ts @@ -130,7 +130,8 @@ export class NoteProcessor { for (const functionLogs of txFunctionLogs) { for (const log of functionLogs.logs) { this.stats.seen++; - const taggedNote = TaggedNote.fromEncryptedBuffer(log.data, secretKey); + // @todo Issue(#6410) We should also try decrypting as outgoing if this fails. + const taggedNote = TaggedNote.decryptAsIncoming(log.data, secretKey); if (taggedNote?.notePayload) { const { notePayload: payload } = taggedNote; // We have successfully decrypted the data. diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index 9def84c9f12..26d8de73567 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -286,33 +286,39 @@ export class Oracle { return newValues.map(toACVMField); } - emitEncryptedLog( + emitEncryptedLog(encryptedLog: ACVMField[], [counter]: ACVMField[]): void { + // Convert each field to a number and then to a buffer (1 byte is stored in 1 field) + const processedInput = Buffer.from(encryptedLog.map(fromACVMField).map(f => f.toNumber())); + this.typedOracle.emitEncryptedLog(processedInput, +counter); + } + + emitEncryptedNoteLog([noteHash]: ACVMField[], encryptedNote: ACVMField[], [counter]: ACVMField[]): void { + // Convert each field to a number and then to a buffer (1 byte is stored in 1 field) + const processedInput = Buffer.from(encryptedNote.map(fromACVMField).map(f => f.toNumber())); + this.typedOracle.emitEncryptedNoteLog(fromACVMField(noteHash), processedInput, +counter); + } + + computeEncryptedLog( [contractAddress]: ACVMField[], [storageSlot]: ACVMField[], [noteTypeId]: ACVMField[], [publicKeyX]: ACVMField[], [publicKeyY]: ACVMField[], - log: ACVMField[], - [counter]: ACVMField[], + preimage: ACVMField[], ): ACVMField[] { const publicKey = new Point(fromACVMField(publicKeyX), fromACVMField(publicKeyY)); - const encLog = this.typedOracle.emitEncryptedLog( + const encLog = this.typedOracle.computeEncryptedLog( AztecAddress.fromString(contractAddress), Fr.fromString(storageSlot), Fr.fromString(noteTypeId), publicKey, - log.map(fromACVMField), - +counter, + preimage.map(fromACVMField), ); - // TODO(1139): We should encrypt in the circuit, but instead we inject here - // encryption output is 112 + 32 * (N + 3) bytes, for log len N - // so split into N + 7 fields (gross but avoids 300+ ACVMFields) - const encLogFields = []; - for (let i = 0; i < Math.ceil(encLog.length / 31); i++) { - encLogFields.push(toACVMField(encLog.subarray(31 * i, Math.min(31 * (i + 1), encLog.length)))); - } - - return encLogFields; + const bytes: ACVMField[] = []; + encLog.forEach(v => { + bytes.push(toACVMField(v)); + }); + return bytes; } emitUnencryptedLog( diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 4a910040384..9c39638adea 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -183,15 +183,22 @@ export abstract class TypedOracle { throw new OracleMethodNotAvailableError('storageWrite'); } - emitEncryptedLog( + emitEncryptedLog(_encryptedNote: Buffer, _counter: number): void { + throw new OracleMethodNotAvailableError('emitEncryptedLog'); + } + + emitEncryptedNoteLog(_noteHash: Fr, _encryptedNote: Buffer, _counter: number): void { + throw new OracleMethodNotAvailableError('emitEncryptedNoteLog'); + } + + computeEncryptedLog( _contractAddress: AztecAddress, _storageSlot: Fr, _noteTypeId: Fr, _publicKey: PublicKey, - _log: Fr[], - _counter: number, + _preimage: Fr[], ): Buffer { - throw new OracleMethodNotAvailableError('emitEncryptedLog'); + throw new OracleMethodNotAvailableError('computeEncryptedLog'); } emitUnencryptedLog(_log: UnencryptedL2Log, _counter: number): void { diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 9fec9c9f9a7..8c06e9b1b76 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -7,7 +7,6 @@ import { type NoteStatus, TaggedNote, type UnencryptedL2Log, - encryptBuffer, } from '@aztec/circuit-types'; import { CallContext, @@ -19,18 +18,11 @@ import { type TxContext, } from '@aztec/circuits.js'; import { Aes128 } from '@aztec/circuits.js/barretenberg'; -import { - computeInnerNoteHash, - computeNoteContentHash, - computePublicDataTreeLeafSlot, - computeUniqueNoteHash, - siloNoteHash, -} from '@aztec/circuits.js/hash'; +import { computePublicDataTreeLeafSlot, computeUniqueNoteHash, siloNoteHash } from '@aztec/circuits.js/hash'; import { type FunctionAbi, type FunctionArtifact, countArgumentsSize } from '@aztec/foundation/abi'; -import { type AztecAddress } from '@aztec/foundation/aztec-address'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, GrumpkinScalar, type Point } from '@aztec/foundation/fields'; import { applyStringFormatting, createDebugLogger } from '@aztec/foundation/log'; -import { serializeToBuffer } from '@aztec/foundation/serialize'; import { type NoteData, toACVMWitness } from '../acvm/index.js'; import { type PackedValuesCache } from '../common/packed_values_cache.js'; @@ -362,44 +354,53 @@ export class ClientExecutionContext extends ViewDataOracle { } /** - * Encrypt a note and emit it as a log. + * Emit encrypted data + * @param encryptedNote - The encrypted data. + * @param counter - The effects counter. + */ + public override emitEncryptedLog(encryptedData: Buffer, counter: number) { + const encryptedLog = new CountedLog(new EncryptedL2Log(encryptedData), counter); + this.encryptedLogs.push(encryptedLog); + } + + /** + * Emit encrypted note data + * @param noteHash - The note hash. + * @param encryptedNote - The encrypted note data. + * @param counter - The effects counter. + */ + public override emitEncryptedNoteLog(noteHash: Fr, encryptedNote: Buffer, counter: number) { + const encryptedLog = new CountedLog(new EncryptedL2Log(encryptedNote), counter); + this.noteEncryptedLogs.push(encryptedLog); + this.noteCache.addNewLog(encryptedLog, noteHash); + } + + /** + * Encrypt a note * @param contractAddress - The contract address of the note. * @param storageSlot - The storage slot the note is at. * @param noteTypeId - The type ID of the note. - * @param publicKey - The public key of the account that can decrypt the log. - * @param log - The log contents. + * @param ivpk - The master incoming viewing public key. + * @param preimage - The note preimage. */ - public override emitEncryptedLog( + public override computeEncryptedLog( contractAddress: AztecAddress, storageSlot: Fr, noteTypeId: Fr, - publicKey: Point, - log: Fr[], - counter: number, + ivpk: Point, + preimage: Fr[], ) { - // TODO(Miranda): This is a temporary solution until we encrypt logs in the circuit - // Then we require a new oracle that deals only with notes - const note = new Note(log); - const innerNoteHash = computeInnerNoteHash(storageSlot, computeNoteContentHash(log)); - const noteExists = this.noteCache.checkNoteExists(contractAddress, innerNoteHash); - if (noteExists) { - // Log linked to note - const l1NotePayload = new L1NotePayload(note, contractAddress, storageSlot, noteTypeId); - const taggedNote = new TaggedNote(l1NotePayload); - const encryptedNote = taggedNote.toEncryptedBuffer(publicKey); - const encryptedLog = new CountedLog(new EncryptedL2Log(encryptedNote), counter); - this.noteEncryptedLogs.push(encryptedLog); - this.noteCache.addNewLog(encryptedLog, innerNoteHash); - return encryptedNote; - } else { - // Generic non-note log - // We assume only the log and address are required - const preimage = Buffer.concat([contractAddress.toBuffer(), serializeToBuffer(log)]); - const encryptedMsg = encryptBuffer(preimage, GrumpkinScalar.random(), publicKey); - const encryptedLog = new EncryptedL2Log(encryptedMsg); - this.encryptedLogs.push(new CountedLog(encryptedLog, counter)); - return encryptedMsg; - } + const note = new Note(preimage); + const l1NotePayload = new L1NotePayload(note, contractAddress, storageSlot, noteTypeId); + const taggedNote = new TaggedNote(l1NotePayload); + + const ephSk = GrumpkinScalar.random(); + + // @todo Issue(#6410) Right now we are completely ignoring the outgoing log. Just drawing random data. + const ovsk = GrumpkinScalar.random(); + const recipient = AztecAddress.random(); + + return taggedNote.encrypt(ephSk, recipient, ivpk, ovsk); } /**