From 5ae96a0f8dc01618c0100903023c40904089985a Mon Sep 17 00:00:00 2001 From: David Banks <47112877+dbanks12@users.noreply.github.com> Date: Thu, 13 Apr 2023 15:32:52 -0400 Subject: [PATCH] feat: Kernel fix & test improvements/refactoring (#239) * fix bug in native kernel - contract logic was being skipped * zero-initialize all abi and kernel structs. helper function to zero-init arrays of frs * better struct initialization in app circuits files * dont need to do as much in base utils since structs are sanely zero-initialized * use fill to initialize array of fields to zero * fix native private kernel, refactor private kernel tests, add real logic in test setup for computing function/contract tree roots/siblings * new hash functions --- cpp/cmake/module.cmake | 8 +- .../basic_contract_deployment.cpp | 10 +- .../basic_contract_deployment.hpp | 4 +- .../circuits/apps/test_apps/escrow/.test.cpp | 2 +- .../apps/test_apps/escrow/deposit.cpp | 10 +- .../apps/test_apps/escrow/deposit.hpp | 4 +- cpp/src/aztec3/circuits/hash.hpp | 107 +- .../aztec3/circuits/kernel/private/.test.cpp | 1242 +++++------------ .../private/native_private_kernel_circuit.cpp | 64 +- 9 files changed, 509 insertions(+), 942 deletions(-) diff --git a/cpp/cmake/module.cmake b/cpp/cmake/module.cmake index 788b1127..f654774b 100644 --- a/cpp/cmake/module.cmake +++ b/cpp/cmake/module.cmake @@ -113,7 +113,13 @@ function(barretenberg_module MODULE_NAME) WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) else() # Currently haven't found a way to easily wrap the calls in wasmtime when run from ctest. - gtest_discover_tests(${MODULE_NAME}_tests WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + # Needed to add `TEST_DISCOVERY_TIMEOUT` to work around: + # ``` + # Error running test executable. + # ... + # Result: Process terminated due to timeout + # ``` + gtest_discover_tests(${MODULE_NAME}_tests WORKING_DIRECTORY ${CMAKE_BINARY_DIR} PROPERTIES TEST_DISCOVERY_TIMEOUT 600) endif() endif() diff --git a/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.cpp b/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.cpp index 76844774..05bfaf1f 100644 --- a/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.cpp +++ b/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.cpp @@ -9,9 +9,7 @@ namespace aztec3::circuits::apps::test_apps::basic_contract_deployment { using aztec3::circuits::abis::OptionalPrivateCircuitPublicInputs; OptionalPrivateCircuitPublicInputs constructor(FunctionExecutionContext& exec_ctx, - NT::fr const& _arg0, - NT::fr const& _arg1, - NT::fr const& _arg2) + std::array const& args) { /**************************************************************** * PREAMBLE @@ -24,9 +22,9 @@ OptionalPrivateCircuitPublicInputs constructor(FunctionExecutionContext& exe // Convert params into circuit types: auto& composer = exec_ctx.composer; - CT::fr arg0 = to_ct(composer, _arg0); - CT::fr arg1 = to_ct(composer, _arg1); - CT::fr arg2 = to_ct(composer, _arg2); + CT::fr arg0 = to_ct(composer, args[0]); + CT::fr arg1 = to_ct(composer, args[1]); + CT::fr arg2 = to_ct(composer, args[2]); auto& oracle = exec_ctx.oracle; const CT::address msg_sender = oracle.get_msg_sender(); diff --git a/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.hpp b/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.hpp index d606dac3..71851b82 100644 --- a/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.hpp +++ b/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.hpp @@ -10,8 +10,6 @@ namespace aztec3::circuits::apps::test_apps::basic_contract_deployment { using aztec3::circuits::abis::OptionalPrivateCircuitPublicInputs; OptionalPrivateCircuitPublicInputs constructor(FunctionExecutionContext& exec_ctx, - NT::fr const& _arg0, - NT::fr const& _arg1, - NT::fr const& _arg2); + std::array const& args); } // namespace aztec3::circuits::apps::test_apps::basic_contract_deployment \ No newline at end of file diff --git a/cpp/src/aztec3/circuits/apps/test_apps/escrow/.test.cpp b/cpp/src/aztec3/circuits/apps/test_apps/escrow/.test.cpp index 08d6d440..04dd483d 100644 --- a/cpp/src/aztec3/circuits/apps/test_apps/escrow/.test.cpp +++ b/cpp/src/aztec3/circuits/apps/test_apps/escrow/.test.cpp @@ -58,7 +58,7 @@ TEST_F(escrow_tests, test_deposit) auto asset_id = NT::fr(1); auto memo = NT::fr(999); - auto result = deposit(exec_ctx, amount, asset_id, memo); + auto result = deposit(exec_ctx, { amount, asset_id, memo }); info("result: ", result); info("computed witness: ", composer.computed_witness); diff --git a/cpp/src/aztec3/circuits/apps/test_apps/escrow/deposit.cpp b/cpp/src/aztec3/circuits/apps/test_apps/escrow/deposit.cpp index c2ed7322..4c7b0245 100644 --- a/cpp/src/aztec3/circuits/apps/test_apps/escrow/deposit.cpp +++ b/cpp/src/aztec3/circuits/apps/test_apps/escrow/deposit.cpp @@ -10,9 +10,7 @@ namespace aztec3::circuits::apps::test_apps::escrow { using aztec3::circuits::abis::OptionalPrivateCircuitPublicInputs; OptionalPrivateCircuitPublicInputs deposit(FunctionExecutionContext& exec_ctx, - NT::fr const& _amount, - NT::fr const& _asset_id, - NT::fr const& _memo) + std::array const& args) { /**************************************************************** * PREAMBLE @@ -25,9 +23,9 @@ OptionalPrivateCircuitPublicInputs deposit(FunctionExecutionContext& exec_ct // Convert params into circuit types: auto& composer = exec_ctx.composer; - CT::fr amount = to_ct(composer, _amount); - CT::fr asset_id = to_ct(composer, _asset_id); - CT::fr memo = to_ct(composer, _memo); + CT::fr amount = to_ct(composer, args[0]); + CT::fr asset_id = to_ct(composer, args[1]); + CT::fr memo = to_ct(composer, args[2]); auto& oracle = exec_ctx.oracle; const CT::address msg_sender = oracle.get_msg_sender(); diff --git a/cpp/src/aztec3/circuits/apps/test_apps/escrow/deposit.hpp b/cpp/src/aztec3/circuits/apps/test_apps/escrow/deposit.hpp index afb9391f..6151d792 100644 --- a/cpp/src/aztec3/circuits/apps/test_apps/escrow/deposit.hpp +++ b/cpp/src/aztec3/circuits/apps/test_apps/escrow/deposit.hpp @@ -10,8 +10,6 @@ namespace aztec3::circuits::apps::test_apps::escrow { using aztec3::circuits::abis::OptionalPrivateCircuitPublicInputs; OptionalPrivateCircuitPublicInputs deposit(FunctionExecutionContext& exec_ctx, - NT::fr const& _amount, - NT::fr const& _asset_id, - NT::fr const& _memo); + std::array const& args); } // namespace aztec3::circuits::apps::test_apps::escrow \ No newline at end of file diff --git a/cpp/src/aztec3/circuits/hash.hpp b/cpp/src/aztec3/circuits/hash.hpp index 4a71fb1e..c5a722c6 100644 --- a/cpp/src/aztec3/circuits/hash.hpp +++ b/cpp/src/aztec3/circuits/hash.hpp @@ -1,9 +1,14 @@ +#include #include +#include "aztec3/circuits/abis/function_leaf_preimage.hpp" +#include #include namespace aztec3::circuits { using abis::FunctionData; +using aztec3::circuits::abis::FunctionLeafPreimage; +using aztec3::circuits::abis::private_kernel::ContractLeafPreimage; template typename NCT::fr compute_args_hash(std::array args) { @@ -87,20 +92,108 @@ typename NCT::fr add_contract_address_to_nullifier(typename NCT::address contrac * @param siblingPath The nodes representing the merkle siblings of the leaf, its parent, * the next parent, etc up to the sibling below the root * @return The computed Merkle tree root. + * + * TODO need to use conditional assigns instead of `ifs` for circuit version. + * see membership.hpp:check_subtree_membership (left/right/conditional_assign, etc) */ template -typename NCT::fr root_from_sibling_path(typename NCT::fr leaf, - typename NCT::uint32 leafIndex, - std::array siblingPath) +typename NCT::fr root_from_sibling_path(typename NCT::fr const& leaf, + typename NCT::uint32 const& leafIndex, + std::array const& siblingPath) { - for (size_t i = 0; i < siblingPath.size(); i++) { + auto node = leaf; + for (size_t i = 0; i < N; i++) { if (leafIndex & (1 << i)) { - leaf = NCT::merkle_hash(siblingPath[i], leaf); + node = NCT::merkle_hash(siblingPath[i], node); } else { - leaf = NCT::merkle_hash(leaf, siblingPath[i]); + node = NCT::merkle_hash(node, siblingPath[i]); } } - return leaf; // root + return node; // root +} + +/** + * @brief Calculate the function tree root from the sibling path and leaf preimage. + * + * @tparam NCT (native or circuit) + * @param function_selector in leaf preimage + * @param is_private in leaf preimage + * @param vk_hash in leaf preimage + * @param acir_hash in leaf preimage + * @param function_leaf_index leaf index in the function tree + * @param function_leaf_sibling_path + * @return NCT::fr + */ +template +typename NCT::fr function_tree_root_from_siblings( + typename NCT::fr const& function_selector, + typename NCT::boolean const& is_private, + typename NCT::fr const& vk_hash, + typename NCT::fr const& acir_hash, + typename NCT::uint32 const& function_leaf_index, + std::array const& function_leaf_sibling_path) +{ + const auto function_leaf_preimage = FunctionLeafPreimage{ + .function_selector = function_selector, + .is_private = is_private, + .vk_hash = vk_hash, + .acir_hash = acir_hash, + }; + + const auto function_leaf = function_leaf_preimage.hash(); + + auto function_tree_root = + root_from_sibling_path(function_leaf, function_leaf_index, function_leaf_sibling_path); + return function_tree_root; +} + +/** + * @brief Calculate the contract tree root from the sibling path and leaf preimage. + * + * @tparam NCT (native or circuit) + * @param function_tree_root in leaf preimage + * @param storage_contract_address in leaf preimage + * @param portal_contract_address in leaf preimage + * @param contract_leaf_index leaf index in the function tree + * @param contract_leaf_sibling_path + * @return NCT::fr + */ +template +typename NCT::fr contract_tree_root_from_siblings( + typename NCT::fr const& function_tree_root, + typename NCT::address const& storage_contract_address, + typename NCT::address const& portal_contract_address, + typename NCT::uint32 const& contract_leaf_index, + std::array const& contract_leaf_sibling_path) +{ + const ContractLeafPreimage contract_leaf_preimage{ storage_contract_address, + portal_contract_address, + function_tree_root }; + + const auto contract_leaf = contract_leaf_preimage.hash(); + + const auto computed_contract_tree_root = + root_from_sibling_path(contract_leaf, contract_leaf_index, contract_leaf_sibling_path); + return computed_contract_tree_root; +} + +/** + * @brief Compute sibling path for an empty tree. + * + * @tparam NCT (native or circuit) + * @tparam TREE_HEIGHT + * @param zero_leaf the leaf value that corresponds to a zero preimage + * @return std::array + */ +template +std::array compute_empty_sibling_path(typename NCT::fr const& zero_leaf) +{ + std::array sibling_path = { zero_leaf }; + for (size_t i = 1; i < TREE_HEIGHT; i++) { + // hash previous sibling with itself to get node above + sibling_path[i] = NCT::merkle_hash(sibling_path[i - 1], sibling_path[i - 1]); + } + return sibling_path; } } // namespace aztec3::circuits \ No newline at end of file diff --git a/cpp/src/aztec3/circuits/kernel/private/.test.cpp b/cpp/src/aztec3/circuits/kernel/private/.test.cpp index cdcedf5b..1d092f77 100644 --- a/cpp/src/aztec3/circuits/kernel/private/.test.cpp +++ b/cpp/src/aztec3/circuits/kernel/private/.test.cpp @@ -1,14 +1,11 @@ -// #include -// #include -// #include -// #include -// #include #include "index.hpp" #include "init.hpp" #include "c_bind.h" +#include "aztec3/constants.hpp" #include +#include #include #include @@ -26,36 +23,30 @@ #include #include #include -// #include -// #include -#include "aztec3/circuits/kernel/private/utils.hpp" - -#include -// #include +#include "aztec3/circuits/kernel/private/utils.hpp" #include #include #include +#include #include -// #include -// #include -// #include - namespace { -using aztec3::circuits::compute_constructor_hash; +using aztec3::circuits::compute_empty_sibling_path; using aztec3::circuits::abis::CallContext; using aztec3::circuits::abis::CallStackItem; using aztec3::circuits::abis::CallType; using aztec3::circuits::abis::ContractDeploymentData; using aztec3::circuits::abis::FunctionData; +using aztec3::circuits::abis::FunctionLeafPreimage; using aztec3::circuits::abis::OptionalPrivateCircuitPublicInputs; using aztec3::circuits::abis::PrivateCircuitPublicInputs; using aztec3::circuits::abis::SignedTxRequest; using aztec3::circuits::abis::TxContext; using aztec3::circuits::abis::TxRequest; +using aztec3::circuits::abis::private_kernel::NewContractData; using aztec3::circuits::abis::private_kernel::AccumulatedData; using aztec3::circuits::abis::private_kernel::ConstantData; @@ -70,288 +61,253 @@ using aztec3::circuits::apps::test_apps::escrow::deposit; using DummyComposer = aztec3::utils::DummyComposer; -// using aztec3::circuits::mock::mock_circuit; using aztec3::circuits::mock::mock_kernel_circuit; +// A type representing any private circuit function +// (for now it works for deposit and constructor) +using private_function = std::function( + FunctionExecutionContext&, + std::array const&)>; + +// Some helper constants for trees +constexpr size_t MAX_FUNCTION_LEAVES = 2 << (aztec3::FUNCTION_TREE_HEIGHT - 1); +const NT::fr EMPTY_FUNCTION_LEAF = FunctionLeafPreimage{}.hash(); // hash of empty/0 preimage +const NT::fr EMPTY_CONTRACT_LEAF = NewContractData{}.hash(); // hash of empty/0 preimage +const auto& EMPTY_FUNCTION_SIBLINGS = compute_empty_sibling_path(EMPTY_FUNCTION_LEAF); +const auto& EMPTY_CONTRACT_SIBLINGS = compute_empty_sibling_path(EMPTY_CONTRACT_LEAF); + } // namespace namespace aztec3::circuits::kernel::private_kernel { -class private_kernel_tests : public ::testing::Test {}; - -TEST(private_kernel_tests, test_deposit) +/** + * @brief Print some debug info about a composer if in DEBUG_PRINTS mode + * + * @param composer + */ +void debugComposer(Composer const& composer) { - //*************************************************************************** - // Some private circuit proof (`deposit`, in this case) - //*************************************************************************** - - const NT::address escrow_contract_address = 12345; - // const NT::fr escrow_contract_leaf_index = 1; - const NT::fr escrow_portal_contract_address = 23456; - - const NT::fr msg_sender_private_key = 123456789; - const NT::address msg_sender = - NT::fr(uint256_t(0x01071e9a23e0f7edULL, 0x5d77b35d1830fa3eULL, 0xc6ba3660bb1f0c0bULL, 0x2ef9f7f09867fd6eULL)); - const NT::address tx_origin = msg_sender; - - Composer deposit_composer = Composer("../barretenberg/cpp/srs_db/ignition"); - DB db; +#ifdef DEBUG_PRINTS + info("computed witness: ", composer.computed_witness); + // info("witness: ", private_kernel_composer.witness); + // info("constant variables: ", private_kernel_composer.constant_variables); + // info("variables: ", composer.variables); + info("failed?: ", composer.failed()); + info("err: ", composer.err()); + info("n: ", composer.get_num_gates()); +#else + (void)composer; // only used in debug mode +#endif +} - FunctionData function_data{ - .function_selector = 1, // TODO: deduce this from the contract, somehow. +/** + * @brief Generate a verification key for a private circuit. + * + * @details Use some dummy inputs just to get the VK for a private circuit + * + * @param is_constructor Whether this private call is a constructor call + * @param func The private circuit call to generate a VK for + * @param num_args Number of args to that private circuit call + * @return std::shared_ptr - the generated VK + */ +std::shared_ptr gen_func_vk(bool is_constructor, private_function const& func, size_t const num_args) +{ + // Some dummy inputs to get the circuit to compile and get a VK + FunctionData dummy_function_data{ .is_private = true, - .is_constructor = false, - }; - - CallContext call_context{ - .msg_sender = msg_sender, - .storage_contract_address = escrow_contract_address, - .portal_contract_address = 0, - .is_delegate_call = false, - .is_static_call = false, - .is_contract_deployment = false, - }; - - NativeOracle deposit_oracle = - NativeOracle(db, escrow_contract_address, function_data, call_context, msg_sender_private_key); - OracleWrapper deposit_oracle_wrapper = OracleWrapper(deposit_composer, deposit_oracle); - - FunctionExecutionContext deposit_ctx(deposit_composer, deposit_oracle_wrapper); - - auto amount = NT::fr(5); - auto asset_id = NT::fr(1); - auto memo = NT::fr(999); - - OptionalPrivateCircuitPublicInputs opt_deposit_public_inputs = deposit(deposit_ctx, amount, asset_id, memo); - PrivateCircuitPublicInputs deposit_public_inputs = opt_deposit_public_inputs.remove_optionality(); - - Prover deposit_prover = deposit_composer.create_prover(); - NT::Proof deposit_proof = deposit_prover.construct_proof(); - // info("\ndeposit_proof: ", deposit_proof.proof_data); - - std::shared_ptr deposit_vk = deposit_composer.compute_verification_key(); - - //*************************************************************************** - // We can create a TxRequest from some of the above data. Users must sign a TxRequest in order to give permission - // for a tx to take place - creating a SignedTxRequest. - //*************************************************************************** - - TxRequest deposit_tx_request = TxRequest{ - .from = tx_origin, - .to = escrow_contract_address, - .function_data = function_data, - .args = deposit_public_inputs.args, - .nonce = 0, - .tx_context = - TxContext{ - .is_fee_payment_tx = false, - .is_rebate_payment_tx = false, - .is_contract_deployment_tx = false, - .contract_deployment_data = ContractDeploymentData(), - }, - .chain_id = 1, - }; - - SignedTxRequest signed_deposit_tx_request = SignedTxRequest{ - .tx_request = deposit_tx_request, - - // .signature = TODO: need a method for signing a TxRequest. + .is_constructor = is_constructor, }; - //*************************************************************************** - // We mock a kernel circuit proof for the base case of kernel recursion (because even the first iteration of the - // kernel circuit expects to verify some previous kernel circuit). - //*************************************************************************** - - Composer mock_kernel_composer = Composer("../barretenberg/cpp/srs_db/ignition"); - - // TODO: we have a choice to make: - // Either the `end` state of the mock kernel's public inputs can be set equal to the public call we _want_ to - // verify in the first round of recursion, OR, we have some fiddly conditional logic in the circuit to ignore - // certain checks if we're handling the 'base case' of the recursion. - // I've chosen the former, for now. - const CallStackItem deposit_call_stack_item{ - .contract_address = deposit_tx_request.to, - - .function_data = deposit_tx_request.function_data, - - .public_inputs = deposit_public_inputs, + CallContext dummy_call_context{ + .is_contract_deployment = is_constructor, }; - std::array initial_kernel_private_call_stack{}; - initial_kernel_private_call_stack[0] = deposit_call_stack_item.hash(); - - // Some test data: - auto mock_kernel_public_inputs = PublicInputs{ - .end = - AccumulatedData{ - .private_call_stack = initial_kernel_private_call_stack, - }, - - // These will be constant throughout all recursions, so can be set to those of the first function call - the - // deposit tx. - .constants = - ConstantData{ - .old_tree_roots = - OldTreeRoots{ - .private_data_tree_root = deposit_public_inputs.historic_private_data_tree_root, - // .nullifier_tree_root = - // .contract_tree_root = - // .private_kernel_vk_tree_root = - }, - .tx_context = deposit_tx_request.tx_context, - }, - - .is_private = true, - // .is_public = false, - // .is_contract_deployment = false, - }; - - mock_kernel_circuit(mock_kernel_composer, mock_kernel_public_inputs); - - Prover mock_kernel_prover = mock_kernel_composer.create_prover(); - NT::Proof mock_kernel_proof = mock_kernel_prover.construct_proof(); - // info("\nmock_kernel_proof: ", mock_kernel_proof.proof_data); - - std::shared_ptr mock_kernel_vk = mock_kernel_composer.compute_verification_key(); - - //*************************************************************************** - // Now we can execute and prove the first kernel iteration, with all the data generated above: - // - app proof, public inputs, etc. - // - mock kernel proof, public inputs, etc. - //*************************************************************************** - - Composer private_kernel_composer = Composer("../barretenberg/cpp/srs_db/ignition"); - - PrivateInputs private_inputs = PrivateInputs{ - .signed_tx_request = signed_deposit_tx_request, - - .previous_kernel = - PreviousKernelData{ - .public_inputs = mock_kernel_public_inputs, - .proof = mock_kernel_proof, - .vk = mock_kernel_vk, - }, - - .private_call = - PrivateCallData{ - .call_stack_item = deposit_call_stack_item, - .private_call_stack_preimages = deposit_ctx.get_private_call_stack_items(), - - .proof = deposit_proof, - .vk = deposit_vk, - - // .function_leaf_membership_witness TODO - // .contract_leaf_membership_witness TODO - - .portal_contract_address = escrow_portal_contract_address, - - // TODO: MembershipWitness function_leaf_membership_witness; - // TODO: MembershipWitness contract_leaf_membership_witness; - }, - }; - - private_kernel_circuit(private_kernel_composer, private_inputs); + // Dummmy invokation of private call circuit, in order to derive its vk + Composer dummy_composer = Composer("../barretenberg/cpp/srs_db/ignition"); + { + DB dummy_db; + NativeOracle dummy_oracle = is_constructor + ? NativeOracle(dummy_db, 0, dummy_function_data, dummy_call_context, {}, 0) + : NativeOracle(dummy_db, 0, dummy_function_data, dummy_call_context, 0); - Prover final_kernel_prover = private_kernel_composer.create_prover(); - NT::Proof final_kernel_proof = final_kernel_prover.construct_proof(); + OracleWrapper dummy_oracle_wrapper = OracleWrapper(dummy_composer, dummy_oracle); - stdlib::types::Verifier final_kernel_verifier = private_kernel_composer.create_verifier(); - auto final_result = final_kernel_verifier.verify_proof(final_kernel_proof); - EXPECT_EQ(final_result, true); + FunctionExecutionContext dummy_ctx(dummy_composer, dummy_oracle_wrapper); - info("computed witness: ", private_kernel_composer.computed_witness); - // info("witness: ", private_kernel_composer.witness); - // info("constant variables: ", private_kernel_composer.constant_variables); - // info("variables: ", private_kernel_composer.variables); + std::array dummy_args; + // if args are value 0, deposit circuit errors when inserting utxo notes + dummy_args.fill(1); + // Make call to private call circuit itself to lay down constraints + func(dummy_ctx, dummy_args); + // FIXME remove arg + (void)num_args; + } - // TODO: this fails intermittently, with: - // bigfield multiply range check failed - info("failed?: ", private_kernel_composer.failed()); - info("err: ", private_kernel_composer.err()); - info("n: ", private_kernel_composer.get_num_gates()); + // Now we can derive the vk: + return dummy_composer.compute_verification_key(); } -TEST(private_kernel_tests, test_native_deposit) +/** + * @brief Perform a private circuit call and generate the inputs to private kernel + * + * @param is_constructor whether this private circuit call is a constructor + * @param func the private circuit call being validated by this kernel iteration + * @param args_vec the private call's args + * @return PrivateInputs - the inputs to the private call circuit + */ +PrivateInputs do_private_call_get_kernel_inputs(bool const is_constructor, + private_function const& func, + std::vector const& args_vec) { //*************************************************************************** - // Some private circuit proof (`deposit`, in this case) + // Initialize some inputs to private call and kernel circuits //*************************************************************************** - - const NT::address escrow_contract_address = 12345; - // const NT::fr escrow_contract_leaf_index = 1; - const NT::fr escrow_portal_contract_address = 23456; + // TODO randomize inputs + NT::address contract_address = is_constructor ? 0 : 12345; // updated later if in constructor + const NT::uint32 contract_leaf_index = 1; + const NT::uint32 function_leaf_index = 1; + const NT::fr portal_contract_address = 23456; + const NT::fr contract_address_salt = 34567; + const NT::fr acir_hash = 12341234; const NT::fr msg_sender_private_key = 123456789; const NT::address msg_sender = NT::fr(uint256_t(0x01071e9a23e0f7edULL, 0x5d77b35d1830fa3eULL, 0xc6ba3660bb1f0c0bULL, 0x2ef9f7f09867fd6eULL)); const NT::address tx_origin = msg_sender; - /** - * NOTE: this is a bit cheeky. We want to test the _native_ kernel circuit implementation. But I don't want to write - * a corresponding _native_ version of every 'app'. So let's just compute the circuit version of the app, and then - * convert it to native types, so that it can be fed into the kernel circuit. - * - */ - Composer deposit_composer = Composer("../barretenberg/cpp/srs_db/ignition"); - DB db; - FunctionData function_data{ .function_selector = 1, // TODO: deduce this from the contract, somehow. .is_private = true, - .is_constructor = false, + .is_constructor = is_constructor, }; CallContext call_context{ .msg_sender = msg_sender, - .storage_contract_address = escrow_contract_address, - .portal_contract_address = 0, + .storage_contract_address = contract_address, + .portal_contract_address = portal_contract_address, .is_delegate_call = false, .is_static_call = false, - .is_contract_deployment = false, + .is_contract_deployment = is_constructor, }; - NativeOracle deposit_oracle = - NativeOracle(db, escrow_contract_address, function_data, call_context, msg_sender_private_key); - OracleWrapper deposit_oracle_wrapper = OracleWrapper(deposit_composer, deposit_oracle); + // sometimes need private call args as array + std::array args{}; + for (size_t i = 0; i < args_vec.size(); ++i) { + args[i] = args_vec[i]; + } + + //*************************************************************************** + // Initialize contract related information like private call VK (and its hash), + // function tree, contract tree, contract address for newly deployed contract, + // etc... + //*************************************************************************** + + // generate private circuit VK and its hash using circuit with dummy inputs + // it is needed below: + // for constructors - to generate the contract address, function leaf, etc + // for private calls - to generate the function leaf, etc + const std::shared_ptr private_circuit_vk = gen_func_vk(is_constructor, func, args_vec.size()); + const NT::fr private_circuit_vk_hash = + stdlib::recursion::verification_key::compress_native(private_circuit_vk, GeneratorIndex::VK); + + ContractDeploymentData contract_deployment_data{}; + NT::fr contract_tree_root = 0; // TODO set properly for constructor? + if (is_constructor) { + // TODO compute function tree root from leaves + // create leaf preimage for each function and hash all into tree + // push to array/vector + // use variation of `compute_root_partial_left_tree` to compute the root from leaves + // const auto& function_leaf_preimage = FunctionLeafPreimage{ + // .function_selector = function_data.function_selector, + // .is_private = function_data.is_private, + // .vk_hash = private_circuit_vk_hash, + // .acir_hash = acir_hash, + //}; + std::vector function_leaves(MAX_FUNCTION_LEAVES, EMPTY_FUNCTION_LEAF); + // const NT::fr& function_tree_root = plonk::stdlib::merkle_tree::compute_tree_root_native(function_leaves); + + // TODO use actual function tree root computed from leaves + // update cdd with actual info + contract_deployment_data = { + .constructor_vk_hash = private_circuit_vk_hash, + .function_tree_root = plonk::stdlib::merkle_tree::compute_tree_root_native(function_leaves), + .contract_address_salt = contract_address_salt, + .portal_contract_address = portal_contract_address, + }; - FunctionExecutionContext deposit_ctx(deposit_composer, deposit_oracle_wrapper); + // Get constructor hash for use when deriving contract address + auto constructor_hash = compute_constructor_hash(function_data, args, private_circuit_vk_hash); + + // Derive contract address so that it can be used inside the constructor itself + contract_address = compute_contract_address( + msg_sender, contract_address_salt, contract_deployment_data.function_tree_root, constructor_hash); + // update the contract address in the call context now that it is known + call_context.storage_contract_address = contract_address; + } else { + const NT::fr& function_tree_root = function_tree_root_from_siblings(function_data.function_selector, + function_data.is_private, + private_circuit_vk_hash, + acir_hash, + function_leaf_index, + EMPTY_FUNCTION_SIBLINGS); + + // update contract_tree_root with real value + contract_tree_root = contract_tree_root_from_siblings(function_tree_root, + contract_address, + portal_contract_address, + contract_leaf_index, + EMPTY_CONTRACT_SIBLINGS); + } - auto amount = NT::fr(5); - auto asset_id = NT::fr(1); - auto memo = NT::fr(999); + //*************************************************************************** + // Create a private circuit/call using composer, oracles, execution context + // Generate its proof and public inputs for submission with a TX request + //*************************************************************************** + Composer private_circuit_composer = Composer("../barretenberg/cpp/srs_db/ignition"); - OptionalPrivateCircuitPublicInputs opt_deposit_public_inputs = deposit(deposit_ctx, amount, asset_id, memo); - PrivateCircuitPublicInputs deposit_public_inputs = opt_deposit_public_inputs.remove_optionality(); + DB db; + NativeOracle oracle = + is_constructor + ? NativeOracle( + db, contract_address, function_data, call_context, contract_deployment_data, msg_sender_private_key) + : NativeOracle(db, contract_address, function_data, call_context, msg_sender_private_key); - Prover deposit_prover = deposit_composer.create_prover(); - NT::Proof deposit_proof = deposit_prover.construct_proof(); + OracleWrapper oracle_wrapper = OracleWrapper(private_circuit_composer, oracle); - std::shared_ptr deposit_vk = deposit_composer.compute_verification_key(); + FunctionExecutionContext ctx(private_circuit_composer, oracle_wrapper); + + OptionalPrivateCircuitPublicInputs opt_private_circuit_public_inputs = func(ctx, args); + PrivateCircuitPublicInputs private_circuit_public_inputs = + opt_private_circuit_public_inputs.remove_optionality(); + // TODO this should likely be handled as part of the DB/Oracle/Context infrastructure + private_circuit_public_inputs.historic_contract_tree_root = contract_tree_root; + + Prover private_circuit_prover = private_circuit_composer.create_prover(); + NT::Proof private_circuit_proof = private_circuit_prover.construct_proof(); + // info("\nproof: ", private_circuit_proof.proof_data); //*************************************************************************** // We can create a TxRequest from some of the above data. Users must sign a TxRequest in order to give permission // for a tx to take place - creating a SignedTxRequest. //*************************************************************************** - - TxRequest deposit_tx_request = TxRequest{ + TxRequest tx_request = TxRequest{ .from = tx_origin, - .to = escrow_contract_address, + .to = contract_address, .function_data = function_data, - .args = deposit_public_inputs.args, + .args = private_circuit_public_inputs.args, .nonce = 0, .tx_context = TxContext{ .is_fee_payment_tx = false, .is_rebate_payment_tx = false, - .is_contract_deployment_tx = false, - .contract_deployment_data = ContractDeploymentData(), + .is_contract_deployment_tx = is_constructor, + .contract_deployment_data = contract_deployment_data, }, .chain_id = 1, }; - SignedTxRequest signed_deposit_tx_request = SignedTxRequest{ - .tx_request = deposit_tx_request, + SignedTxRequest signed_tx_request = SignedTxRequest{ + .tx_request = tx_request, // .signature = TODO: need a method for signing a TxRequest. }; @@ -360,7 +316,6 @@ TEST(private_kernel_tests, test_native_deposit) // We mock a kernel circuit proof for the base case of kernel recursion (because even the first iteration of the // kernel circuit expects to verify some previous kernel circuit). //*************************************************************************** - Composer mock_kernel_composer = Composer("../barretenberg/cpp/srs_db/ignition"); // TODO: we have a choice to make: @@ -368,36 +323,33 @@ TEST(private_kernel_tests, test_native_deposit) // verify in the first round of recursion, OR, we have some fiddly conditional logic in the circuit to ignore // certain checks if we're handling the 'base case' of the recursion. // I've chosen the former, for now. - const CallStackItem deposit_call_stack_item{ - .contract_address = deposit_tx_request.to, - - .function_data = deposit_tx_request.function_data, - - .public_inputs = deposit_public_inputs, + const CallStackItem call_stack_item{ + .contract_address = tx_request.to, + .function_data = tx_request.function_data, + .public_inputs = private_circuit_public_inputs, }; std::array initial_kernel_private_call_stack{}; - initial_kernel_private_call_stack[0] = deposit_call_stack_item.hash(); + initial_kernel_private_call_stack[0] = call_stack_item.hash(); // Some test data: - PublicInputs mock_kernel_public_inputs = PublicInputs{ + auto mock_kernel_public_inputs = PublicInputs{ .end = AccumulatedData{ .private_call_stack = initial_kernel_private_call_stack, }, - // These will be constant throughout all recursions, so can be set to those of the first function call - the - // deposit tx. + // These will be constant throughout all recursions, so can be set to those of the first function call - the tx. .constants = ConstantData{ .old_tree_roots = OldTreeRoots{ - .private_data_tree_root = deposit_public_inputs.historic_private_data_tree_root, + .private_data_tree_root = private_circuit_public_inputs.historic_private_data_tree_root, // .nullifier_tree_root = - // .contract_tree_root = + .contract_tree_root = private_circuit_public_inputs.historic_contract_tree_root, // .private_kernel_vk_tree_root = }, - .tx_context = deposit_tx_request.tx_context, + .tx_context = tx_request.tx_context, }, .is_private = true, @@ -409,20 +361,15 @@ TEST(private_kernel_tests, test_native_deposit) Prover mock_kernel_prover = mock_kernel_composer.create_prover(); NT::Proof mock_kernel_proof = mock_kernel_prover.construct_proof(); + // info("\nmock_kernel_proof: ", mock_kernel_proof.proof_data); std::shared_ptr mock_kernel_vk = mock_kernel_composer.compute_verification_key(); //*************************************************************************** - // Now we can execute and prove the first kernel iteration, with all the data generated above: - // - app proof, public inputs, etc. - // - mock kernel proof, public inputs, etc. + // Now we can construct the full private inputs to the kernel circuit //*************************************************************************** - - // NOTE: WE DON'T USE A COMPOSER HERE, SINCE WE WANT TO TEST THE `native_private_kernel_circuit` - // Composer private_kernel_composer = Composer("../barretenberg/cpp/srs_db/ignition"); - - PrivateInputs private_inputs = PrivateInputs{ - .signed_tx_request = signed_deposit_tx_request, + PrivateInputs kernel_private_inputs = PrivateInputs{ + .signed_tx_request = signed_tx_request, .previous_kernel = PreviousKernelData{ @@ -433,690 +380,227 @@ TEST(private_kernel_tests, test_native_deposit) .private_call = PrivateCallData{ - .call_stack_item = deposit_call_stack_item, - .private_call_stack_preimages = deposit_ctx.get_private_call_stack_items(), + .call_stack_item = call_stack_item, + .private_call_stack_preimages = ctx.get_private_call_stack_items(), - .proof = deposit_proof, - .vk = deposit_vk, - // .vk_path TODO + .proof = private_circuit_proof, + .vk = private_circuit_vk, - // TODO: MembershipWitness function_leaf_membership_witness; - // TODO: MembershipWitness contract_leaf_membership_witness; + .function_leaf_membership_witness = { + .leaf_index = function_leaf_index, + .sibling_path = EMPTY_FUNCTION_SIBLINGS, + }, + .contract_leaf_membership_witness = { + .leaf_index = contract_leaf_index, + .sibling_path = EMPTY_CONTRACT_SIBLINGS, + }, - .portal_contract_address = escrow_portal_contract_address, + .portal_contract_address = portal_contract_address, + + .acir_hash = acir_hash, }, }; - DummyComposer composer = DummyComposer(); - PublicInputs public_inputs = native_private_kernel_circuit(composer, private_inputs); - - // Prover final_kernel_prover = private_kernel_composer.create_prover(); - // NT::Proof final_kernel_proof = final_kernel_prover.construct_proof(); - - // stdlib::types::Verifier final_kernel_verifier = private_kernel_composer.create_verifier(); - // auto final_result = final_kernel_verifier.verify_proof(final_kernel_proof); - // EXPECT_EQ(final_result, true); - - // info("computed witness: ", private_kernel_composer.computed_witness); - // info("witness: ", private_kernel_composer.witness); - // info("constant variables: ", private_kernel_composer.constant_variables); - // info("variables: ", private_kernel_composer.variables); - - // TODO: this fails intermittently, with: - // bigfield multiply range check failed - // info("failed?: ", private_kernel_composer.failed()); - // info("err: ", private_kernel_composer.err()); - // info("n: ", private_kernel_composer.get_num_gates()); + return kernel_private_inputs; } -TEST(private_kernel_tests, test_basic_contract_deployment) +/** + * @brief Validate that the deployed contract address is correct. + * + * @details Compare the public inputs new contract address + * with one manually computed from private inputs. + * @param private_inputs to be used in manual computation + * @param public_inputs that contain the expected new contract address + */ +void validate_deployed_contract_address(PrivateInputs const& private_inputs, PublicInputs const& public_inputs) { - //*************************************************************************** - // Some private circuit proof (`constructor`, in this case) - //*************************************************************************** - - // contract address for newly deployed contract will be derived - // below after constructor VK is computed - // const NT::fr new_contract_leaf_index = 1; - const NT::fr new_portal_contract_address = 23456; - const NT::fr contract_address_salt = 34567; - - const NT::fr msg_sender_private_key = 123456789; - const NT::address msg_sender = - NT::fr(uint256_t(0x01071e9a23e0f7edULL, 0x5d77b35d1830fa3eULL, 0xc6ba3660bb1f0c0bULL, 0x2ef9f7f09867fd6eULL)); - const NT::address tx_origin = msg_sender; - - FunctionData function_data{ - .function_selector = 1, // TODO: deduce this from the contract, somehow. - .is_private = true, - .is_constructor = true, - }; - - CallContext call_context{ - .msg_sender = msg_sender, - .storage_contract_address = 0, // will be replaced with contract address once it is calculated - .portal_contract_address = 0, - .is_delegate_call = false, - .is_static_call = false, - .is_contract_deployment = true, - }; - - NT::fr arg0 = 5; - NT::fr arg1 = 1; - NT::fr arg2 = 999; - std::array args = { 0 }; - args[0] = arg0; - args[1] = arg1; - args[2] = arg2; - - Composer dummy_constructor_composer = Composer("../barretenberg/cpp/srs_db/ignition"); - { - // Dummmy invokation, in order to derive the vk of this circuit - - // We need to use _dummy_ contract_deployment_data first, because the _proper_ version of the - // contract_deployment_data will need to contain the constructor_vk_hash... but the constructor's vk can only be - // computed after the composer has composed the circuit! - ContractDeploymentData dummy_contract_deployment_data{ - .constructor_vk_hash = 0, // zeros are okay for dummy call - .function_tree_root = 0, - .contract_address_salt = 0, - .portal_contract_address = 0, - }; - - DB dummy_db; - NativeOracle dummy_constructor_oracle = NativeOracle(dummy_db, - 0, // contract address not known during VK computation - function_data, - call_context, - dummy_contract_deployment_data, - msg_sender_private_key); - OracleWrapper dummy_constructor_oracle_wrapper = - OracleWrapper(dummy_constructor_composer, dummy_constructor_oracle); - - FunctionExecutionContext dummy_constructor_ctx(dummy_constructor_composer, dummy_constructor_oracle_wrapper); - - constructor(dummy_constructor_ctx, arg0, arg1, arg2); - } - - // Now we can derive the vk: - std::shared_ptr constructor_vk = dummy_constructor_composer.compute_verification_key(); - auto constructor_vk_hash = - stdlib::recursion::verification_key::compress_native(constructor_vk, GeneratorIndex::VK); - - // Now, we can proceed with the proper (non-dummy) invokation of our constructor circuit: - - Composer constructor_composer = Composer("../barretenberg/cpp/srs_db/ignition"); - DB db; - - ContractDeploymentData contract_deployment_data{ - .constructor_vk_hash = constructor_vk_hash, - .function_tree_root = 0, // TODO actually get this? - .contract_address_salt = contract_address_salt, - .portal_contract_address = new_portal_contract_address, - }; - - // Get constructor hash for use when deriving contract address - auto constructor_hash = compute_constructor_hash(function_data, args, constructor_vk_hash); - - // Derive contract address so that it can be used inside the constructor itself - const NT::address new_contract_address = compute_contract_address( - msg_sender, contract_address_salt, contract_deployment_data.function_tree_root, constructor_hash); - // update the contract address in the call context now that it is known - call_context.storage_contract_address = new_contract_address; - - NativeOracle constructor_oracle = NativeOracle( - db, new_contract_address, function_data, call_context, contract_deployment_data, msg_sender_private_key); - OracleWrapper constructor_oracle_wrapper = OracleWrapper(constructor_composer, constructor_oracle); - - FunctionExecutionContext constructor_ctx(constructor_composer, constructor_oracle_wrapper); - - OptionalPrivateCircuitPublicInputs opt_constructor_public_inputs = - constructor(constructor_ctx, arg0, arg1, arg2); - - PrivateCircuitPublicInputs constructor_public_inputs = opt_constructor_public_inputs.remove_optionality(); - - Prover constructor_prover = constructor_composer.create_prover(); - NT::Proof constructor_proof = constructor_prover.construct_proof(); - // info("\nconstructor_proof: ", constructor_proof.proof_data); - - auto expected_constructor_hash = - NT::compress({ function_data.hash(), - NT::compress(constructor_public_inputs.args, CONSTRUCTOR_ARGS), - constructor_vk_hash }, - CONSTRUCTOR); - NT::fr expected_contract_address = NT::compress({ msg_sender, - contract_deployment_data.contract_address_salt, - contract_deployment_data.function_tree_root, - expected_constructor_hash }, - CONTRACT_ADDRESS); - - //*************************************************************************** - // We can create a TxRequest from some of the above data. Users must sign a TxRequest in order to give permission - // for a tx to take place - creating a SignedTxRequest. - //*************************************************************************** - - TxRequest constructor_tx_request = TxRequest{ - .from = tx_origin, - .to = new_contract_address, - .function_data = function_data, - .args = constructor_public_inputs.args, - .nonce = 0, - .tx_context = - TxContext{ - .is_fee_payment_tx = false, - .is_rebate_payment_tx = false, - .is_contract_deployment_tx = true, - .contract_deployment_data = contract_deployment_data, - }, - .chain_id = 1, - }; - - SignedTxRequest signed_constructor_tx_request = SignedTxRequest{ - .tx_request = constructor_tx_request, - - // .signature = TODO: need a method for signing a TxRequest. - }; - - //*************************************************************************** - // We mock a kernel circuit proof for the base case of kernel recursion (because even the first iteration of the - // kernel circuit expects to verify some previous kernel circuit). - //*************************************************************************** - - Composer mock_kernel_composer = Composer("../barretenberg/cpp/srs_db/ignition"); - // TODO: we have a choice to make: - // Either the `end` state of the mock kernel's public inputs can be set equal to the public call we _want_ to - // verify in the first round of recursion, OR, we have some fiddly conditional logic in the circuit to ignore - // certain checks if we're handling the 'base case' of the recursion. - // I've chosen the former, for now. - const CallStackItem constructor_call_stack_item{ - .contract_address = constructor_tx_request.to, - - .function_data = constructor_tx_request.function_data, - - .public_inputs = constructor_public_inputs, - }; - - std::array initial_kernel_private_call_stack{}; - initial_kernel_private_call_stack[0] = constructor_call_stack_item.hash(); + auto tx_request = private_inputs.signed_tx_request.tx_request; + auto cdd = private_inputs.signed_tx_request.tx_request.tx_context.contract_deployment_data; + + auto private_circuit_vk_hash = stdlib::recursion::verification_key::compress_native( + private_inputs.private_call.vk, GeneratorIndex::VK); + auto expected_constructor_hash = NT::compress({ private_inputs.private_call.call_stack_item.function_data.hash(), + NT::compress(tx_request.args, CONSTRUCTOR_ARGS), + private_circuit_vk_hash }, + CONSTRUCTOR); + NT::fr expected_contract_address = + NT::compress({ tx_request.from, cdd.contract_address_salt, cdd.function_tree_root, expected_constructor_hash }, + CONTRACT_ADDRESS); + EXPECT_EQ(public_inputs.end.new_contracts[0].contract_address.to_field(), expected_contract_address); +} - // Some test data: - auto mock_kernel_public_inputs = PublicInputs(); - mock_kernel_public_inputs.end.private_call_stack = initial_kernel_private_call_stack, - mock_kernel_public_inputs.constants.old_tree_roots.private_data_tree_root = - constructor_public_inputs.historic_private_data_tree_root; - mock_kernel_public_inputs.constants.tx_context = constructor_tx_request.tx_context; - mock_kernel_public_inputs.is_private = true; +/** + * @brief Some private circuit proof (`deposit`, in this case) + */ +TEST(private_kernel_tests, test_circuit_deposit) +{ + NT::fr const& amount = 5; + NT::fr const& asset_id = 1; + NT::fr const& memo = 999; - mock_kernel_circuit(mock_kernel_composer, mock_kernel_public_inputs); + auto const& private_inputs = do_private_call_get_kernel_inputs(false, deposit, { amount, asset_id, memo }); - Prover mock_kernel_prover = mock_kernel_composer.create_prover(); - NT::Proof mock_kernel_proof = mock_kernel_prover.construct_proof(); - // info("\nmock_kernel_proof: ", mock_kernel_proof.proof_data); + // Execute and prove the first kernel iteration + Composer private_kernel_composer("../barretenberg/cpp/srs_db/ignition"); + auto const& public_inputs = private_kernel_circuit(private_kernel_composer, private_inputs); - std::shared_ptr mock_kernel_vk = mock_kernel_composer.compute_verification_key(); + // Check contract address was correctly computed by the circuit + validate_deployed_contract_address(private_inputs, public_inputs); - //*************************************************************************** - // Now we can execute and prove the first kernel iteration, with all the data generated above: - // - app proof, public inputs, etc. - // - mock kernel proof, public inputs, etc. - //*************************************************************************** + // Create the final kernel proof and verify it natively. + auto final_kernel_prover = private_kernel_composer.create_prover(); + auto const& final_kernel_proof = final_kernel_prover.construct_proof(); - Composer private_kernel_composer = Composer("../barretenberg/cpp/srs_db/ignition"); + auto final_kernel_verifier = private_kernel_composer.create_verifier(); + auto const& final_result = final_kernel_verifier.verify_proof(final_kernel_proof); + EXPECT_EQ(final_result, true); - PrivateInputs private_inputs = PrivateInputs{ - .signed_tx_request = signed_constructor_tx_request, + debugComposer(private_kernel_composer); +} - .previous_kernel = - PreviousKernelData{ - .public_inputs = mock_kernel_public_inputs, - .proof = mock_kernel_proof, - .vk = mock_kernel_vk, - }, +/** + * @brief Some private circuit simulation (`deposit`, in this case) + */ +TEST(private_kernel_tests, test_native_deposit) +{ + NT::fr const& amount = 5; + NT::fr const& asset_id = 1; + NT::fr const& memo = 999; - .private_call = - PrivateCallData{ - .call_stack_item = constructor_call_stack_item, - .private_call_stack_preimages = constructor_ctx.get_private_call_stack_items(), + auto const& private_inputs = do_private_call_get_kernel_inputs(false, deposit, { amount, asset_id, memo }); + DummyComposer composer; + auto const& public_inputs = native_private_kernel_circuit(composer, private_inputs); - .proof = constructor_proof, - .vk = constructor_vk, + validate_deployed_contract_address(private_inputs, public_inputs); +} - // .function_leaf_membership_witness TODO - // .contract_leaf_membership_witness TODO +/** + * @brief Some private circuit proof (`constructor`, in this case) + */ +TEST(private_kernel_tests, test_basic_contract_deployment) +{ + NT::fr const& arg0 = 5; + NT::fr const& arg1 = 1; + NT::fr const& arg2 = 999; + std::vector args_vec = { arg0, arg1, arg2 }; - .portal_contract_address = new_portal_contract_address, - }, - }; + auto const& private_inputs = do_private_call_get_kernel_inputs(true, constructor, args_vec); - auto private_kernel_circuit_public_inputs = private_kernel_circuit(private_kernel_composer, private_inputs); + // Execute and prove the first kernel iteration + Composer private_kernel_composer("../barretenberg/cpp/srs_db/ignition"); + auto const& public_inputs = private_kernel_circuit(private_kernel_composer, private_inputs); // Check contract address was correctly computed by the circuit - EXPECT_EQ(private_kernel_circuit_public_inputs.end.new_contracts[0].contract_address.to_field(), - expected_contract_address); + validate_deployed_contract_address(private_inputs, public_inputs); // Create the final kernel proof and verify it natively. - stdlib::types::Prover final_kernel_prover = private_kernel_composer.create_prover(); - NT::Proof final_kernel_proof = final_kernel_prover.construct_proof(); + auto final_kernel_prover = private_kernel_composer.create_prover(); + auto const& final_kernel_proof = final_kernel_prover.construct_proof(); - stdlib::types::Verifier final_kernel_verifier = private_kernel_composer.create_verifier(); - auto final_result = final_kernel_verifier.verify_proof(final_kernel_proof); + auto final_kernel_verifier = private_kernel_composer.create_verifier(); + auto const& final_result = final_kernel_verifier.verify_proof(final_kernel_proof); EXPECT_EQ(final_result, true); - info("computed witness: ", private_kernel_composer.computed_witness); - // info("witness: ", private_kernel_composer.witness); - // info("constant variables: ", private_kernel_composer.constant_variables); - // info("variables: ", private_kernel_composer.variables); - - // TODO: this fails intermittently, with: - // bigfield multiply range check failed - info("failed?: ", private_kernel_composer.failed()); - info("err: ", private_kernel_composer.err()); - info("n: ", private_kernel_composer.num_gates); + debugComposer(private_kernel_composer); } +/** + * @brief Some private circuit simulation (`constructor`, in this case) + */ TEST(private_kernel_tests, test_native_basic_contract_deployment) { - //*************************************************************************** - // Some private circuit proof (`constructor`, in this case) - //*************************************************************************** - - // contract address for newly deployed contract will be derived - // below after constructor VK is computed - // const NT::fr new_contract_leaf_index = 1; - const NT::fr new_portal_contract_address = 23456; - const NT::fr contract_address_salt = 34567; - - const NT::fr msg_sender_private_key = 123456789; - const NT::address msg_sender = - NT::fr(uint256_t(0x01071e9a23e0f7edULL, 0x5d77b35d1830fa3eULL, 0xc6ba3660bb1f0c0bULL, 0x2ef9f7f09867fd6eULL)); - const NT::address tx_origin = msg_sender; - - /** - * NOTE: this is a bit cheeky. We want to test the _native_ kernel circuit implementation. But I don't want to write - * a corresponding _native_ version of every 'app'. So let's just compute the circuit version of the app, and then - * convert it to native types, so that it can be fed into the kernel circuit. - * - */ - FunctionData function_data{ - .function_selector = 1, // TODO: deduce this from the contract, somehow. - .is_private = true, - .is_constructor = true, - }; - - CallContext call_context{ - .msg_sender = msg_sender, - .storage_contract_address = 0, // will be replaced with contract address once it is calculated - .portal_contract_address = 0, - .is_delegate_call = false, - .is_static_call = false, - .is_contract_deployment = true, - }; - - NT::fr arg0 = 5; - NT::fr arg1 = 1; - NT::fr arg2 = 999; - std::array args = { 0 }; - args[0] = arg0; - args[1] = arg1; - args[2] = arg2; - - Composer dummy_constructor_composer = Composer("../barretenberg/cpp/srs_db/ignition"); - { - // Dummmy invokation, in order to derive the vk of this circuit - - // We need to use _dummy_ contract_deployment_data first, because the _proper_ version of the - // contract_deployment_data will need to contain the constructor_vk_hash... but the constructor's vk can only be - // computed after the composer has composed the circuit! - ContractDeploymentData dummy_contract_deployment_data{ - .constructor_vk_hash = 0, // zeros are okay for dummy call - .function_tree_root = 0, - .contract_address_salt = 0, - .portal_contract_address = 0, - }; - - DB dummy_db; - NativeOracle dummy_constructor_oracle = NativeOracle(dummy_db, - 0, // contract address not known during VK computation - function_data, - call_context, - dummy_contract_deployment_data, - msg_sender_private_key); - OracleWrapper dummy_constructor_oracle_wrapper = - OracleWrapper(dummy_constructor_composer, dummy_constructor_oracle); - - FunctionExecutionContext dummy_constructor_ctx(dummy_constructor_composer, dummy_constructor_oracle_wrapper); - - constructor(dummy_constructor_ctx, arg0, arg1, arg2); - } - - // Now we can derive the vk: - std::shared_ptr constructor_vk = dummy_constructor_composer.compute_verification_key(); - auto constructor_vk_hash = - stdlib::recursion::verification_key::compress_native(constructor_vk, GeneratorIndex::VK); - - // Now, we can proceed with the proper (non-dummy) invokation of our constructor circuit: - - Composer constructor_composer = Composer("../barretenberg/cpp/srs_db/ignition"); - DB db; - - ContractDeploymentData contract_deployment_data{ - .constructor_vk_hash = constructor_vk_hash, - .function_tree_root = 0, // TODO actually get this? - .contract_address_salt = contract_address_salt, - .portal_contract_address = new_portal_contract_address, - }; - - // Get constructor hash for use when deriving contract address - auto constructor_hash = compute_constructor_hash(function_data, args, constructor_vk_hash); - - // Derive contract address so that it can be used inside the constructor itself - const NT::address new_contract_address = compute_contract_address( - msg_sender, contract_address_salt, contract_deployment_data.function_tree_root, constructor_hash); - - // update the contract address in the call context now that it is known - call_context.storage_contract_address = new_contract_address; - - NativeOracle constructor_oracle = NativeOracle( - db, new_contract_address, function_data, call_context, contract_deployment_data, msg_sender_private_key); - OracleWrapper constructor_oracle_wrapper = OracleWrapper(constructor_composer, constructor_oracle); - - FunctionExecutionContext constructor_ctx(constructor_composer, constructor_oracle_wrapper); + NT::fr const& arg0 = 5; + NT::fr const& arg1 = 1; + NT::fr const& arg2 = 999; + std::vector args_vec = { arg0, arg1, arg2 }; - OptionalPrivateCircuitPublicInputs opt_constructor_public_inputs = - constructor(constructor_ctx, arg0, arg1, arg2); + auto const& private_inputs = do_private_call_get_kernel_inputs(true, constructor, args_vec); + DummyComposer composer; + auto const& public_inputs = native_private_kernel_circuit(composer, private_inputs); - PrivateCircuitPublicInputs constructor_public_inputs = opt_constructor_public_inputs.remove_optionality(); - - Prover constructor_prover = constructor_composer.create_prover(); - NT::Proof constructor_proof = constructor_prover.construct_proof(); - - // auto constructor_hash_real = - // NT::compress({ function_data.hash(), - // NT::compress(constructor_public_inputs.args, CONSTRUCTOR_ARGS), - // constructor_vk_hash }, - // CONSTRUCTOR); - // auto contract_address_real = NT::compress({ msg_sender, - // contract_deployment_data.contract_address_salt, - // contract_deployment_data.function_tree_root, - // constructor_hash_real }, - // CONTRACT_ADDRESS); - - //*************************************************************************** - // We can create a TxRequest from some of the above data. Users must sign a TxRequest in order to give permission - // for a tx to take place - creating a SignedTxRequest. - //*************************************************************************** - - TxRequest constructor_tx_request = TxRequest{ - .from = tx_origin, - .to = new_contract_address, - .function_data = function_data, - .args = constructor_public_inputs.args, - .nonce = 0, - .tx_context = - TxContext{ - .is_fee_payment_tx = false, - .is_rebate_payment_tx = false, - .is_contract_deployment_tx = true, - .contract_deployment_data = contract_deployment_data, - }, - .chain_id = 1, - }; - - SignedTxRequest signed_constructor_tx_request = SignedTxRequest{ - .tx_request = constructor_tx_request, - - // .signature = TODO: need a method for signing a TxRequest. - }; - - //*************************************************************************** - // We mock a kernel circuit proof for the base case of kernel recursion (because even the first iteration of the - // kernel circuit expects to verify some previous kernel circuit). - //*************************************************************************** - - Composer mock_kernel_composer = Composer("../barretenberg/cpp/srs_db/ignition"); - - // TODO: we have a choice to make: - // Either the `end` state of the mock kernel's public inputs can be set equal to the public call we _want_ to - // verify in the first round of recursion, OR, we have some fiddly conditional logic in the circuit to ignore - // certain checks if we're handling the 'base case' of the recursion. - // I've chosen the former, for now. - const CallStackItem constructor_call_stack_item{ - .contract_address = constructor_tx_request.to, - - .function_data = constructor_tx_request.function_data, - - .public_inputs = constructor_public_inputs, - }; - - std::array initial_kernel_private_call_stack{}; - initial_kernel_private_call_stack[0] = constructor_call_stack_item.hash(); - - // Some test data: - auto mock_kernel_public_inputs = PublicInputs(); - mock_kernel_public_inputs.end.private_call_stack = initial_kernel_private_call_stack, - mock_kernel_public_inputs.constants.old_tree_roots.private_data_tree_root = - constructor_public_inputs.historic_private_data_tree_root; - mock_kernel_public_inputs.constants.tx_context = constructor_tx_request.tx_context; - mock_kernel_public_inputs.is_private = true; - - mock_kernel_circuit(mock_kernel_composer, mock_kernel_public_inputs); - - Prover mock_kernel_prover = mock_kernel_composer.create_prover(); - NT::Proof mock_kernel_proof = mock_kernel_prover.construct_proof(); - // info("\nmock_kernel_proof: ", mock_kernel_proof.proof_data); - - std::shared_ptr mock_kernel_vk = mock_kernel_composer.compute_verification_key(); - - //*************************************************************************** - // Now we can execute and prove the first kernel iteration, with all the data generated above: - // - app proof, public inputs, etc. - // - mock kernel proof, public inputs, etc. - //*************************************************************************** - - // NOTE: WE DON'T USE A COMPOSER HERE, SINCE WE WANT TO TEST THE `native_private_kernel_circuit` - // Composer private_kernel_composer = Composer("../barretenberg/cpp/srs_db/ignition"); - - PrivateInputs private_inputs = PrivateInputs{ - .signed_tx_request = signed_constructor_tx_request, - - .previous_kernel = - PreviousKernelData{ - .public_inputs = mock_kernel_public_inputs, - .proof = mock_kernel_proof, - .vk = mock_kernel_vk, - }, - - .private_call = - PrivateCallData{ - .call_stack_item = constructor_call_stack_item, - .private_call_stack_preimages = constructor_ctx.get_private_call_stack_items(), - - .proof = constructor_proof, - .vk = constructor_vk, - - // .function_leaf_membership_witness TODO - // .contract_leaf_membership_witness TODO - - .portal_contract_address = new_portal_contract_address, - }, - }; - DummyComposer composer = DummyComposer(); - PublicInputs private_kernel_circuit_public_inputs = native_private_kernel_circuit(composer, private_inputs); + validate_deployed_contract_address(private_inputs, public_inputs); } +/** + * @brief Some private circuit simulation checked against its results via cbinds + */ TEST(private_kernel_tests, test_create_proof_cbinds) { - //*************************************************************************** - // Some private NATIVE mocked proof (`constructor`, in this case) - // and the cbind to generate valid outputs - //*************************************************************************** - - const NT::address new_contract_address = 12345; - // const NT::fr new_contract_leaf_index = 1; - const NT::fr new_portal_contract_address = 23456; - - const NT::fr msg_sender_private_key = 123456789; - const NT::address msg_sender = - NT::fr(uint256_t(0x01071e9a23e0f7edULL, 0x5d77b35d1830fa3eULL, 0xc6ba3660bb1f0c0bULL, 0x2ef9f7f09867fd6eULL)); - const NT::address tx_origin = msg_sender; + NT::fr const& arg0 = 5; + NT::fr const& arg1 = 1; + NT::fr const& arg2 = 999; + std::vector args_vec = { arg0, arg1, arg2 }; - Composer constructor_composer = Composer("../barretenberg/cpp/srs_db/ignition"); - DB db; + // first run actual simulation to get public inputs + auto const& private_inputs = do_private_call_get_kernel_inputs(true, constructor, args_vec); + DummyComposer composer; + auto const& public_inputs = native_private_kernel_circuit(composer, private_inputs); - FunctionData function_data{ - .function_selector = 1, // TODO: deduce this from the contract, somehow. - .is_private = true, - .is_constructor = true, - }; - - CallContext call_context{ - .msg_sender = msg_sender, - .storage_contract_address = new_contract_address, - .portal_contract_address = 0, - .is_delegate_call = false, - .is_static_call = false, - .is_contract_deployment = true, - }; - - NativeOracle constructor_oracle = - NativeOracle(db, new_contract_address, function_data, call_context, msg_sender_private_key); - OracleWrapper constructor_oracle_wrapper = OracleWrapper(constructor_composer, constructor_oracle); - - FunctionExecutionContext constructor_ctx(constructor_composer, constructor_oracle_wrapper); - - auto arg0 = NT::fr(5); - auto arg1 = NT::fr(1); - auto arg2 = NT::fr(999); - - OptionalPrivateCircuitPublicInputs opt_constructor_public_inputs = - constructor(constructor_ctx, arg0, arg1, arg2); - - ContractDeploymentData contract_deployment_data{ - .constructor_vk_hash = 0, // TODO actually get this? - .function_tree_root = 0, // TODO actually get this? - .contract_address_salt = 42, - .portal_contract_address = new_portal_contract_address, - }; - opt_constructor_public_inputs.contract_deployment_data = contract_deployment_data; - - PrivateCircuitPublicInputs constructor_public_inputs = opt_constructor_public_inputs.remove_optionality(); - - Prover constructor_prover = constructor_composer.create_prover(); - NT::Proof constructor_proof = constructor_prover.construct_proof(); - // info("\nconstructor_proof: ", constructor_proof.proof_data); - - std::shared_ptr constructor_vk = constructor_composer.compute_verification_key(); - - //*************************************************************************** - // We can create a TxRequest from some of the above data. Users must sign a TxRequest in order to give permission - // for a tx to take place - creating a SignedTxRequest. - //*************************************************************************** - - TxRequest constructor_tx_request = TxRequest{ - .from = tx_origin, - .to = new_contract_address, - .function_data = function_data, - .args = constructor_public_inputs.args, - .nonce = 0, - .tx_context = - TxContext{ - .is_fee_payment_tx = false, - .is_rebate_payment_tx = false, - .is_contract_deployment_tx = false, - .contract_deployment_data = contract_deployment_data, - }, - .chain_id = 1, - }; - - SignedTxRequest signed_constructor_tx_request = SignedTxRequest{ - .tx_request = constructor_tx_request, - - // .signature = TODO: need a method for signing a TxRequest. - }; + // serialize expected public inputs for later comparison + std::vector expected_public_inputs_vec; + write(expected_public_inputs_vec, public_inputs); //*************************************************************************** - // We mock a kernel circuit proof for the base case of kernel recursion (because even the first iteration of the - // kernel circuit expects to verify some previous kernel circuit). + // Now run the simulate/prove cbinds to make sure their outputs match //*************************************************************************** - - Composer mock_kernel_composer = Composer("../barretenberg/cpp/srs_db/ignition"); - - // TODO: we have a choice to make: - // Either the `end` state of the mock kernel's public inputs can be set equal to the public call we _want_ to - // verify in the first round of recursion, OR, we have some fiddly conditional logic in the circuit to ignore - // certain checks if we're handling the 'base case' of the recursion. - // I've chosen the former, for now. - const CallStackItem constructor_call_stack_item{ - .contract_address = constructor_tx_request.to, - - .function_data = constructor_tx_request.function_data, - - .public_inputs = constructor_public_inputs, - }; - - std::array initial_kernel_private_call_stack{}; - initial_kernel_private_call_stack[0] = constructor_call_stack_item.hash(); - // TODO might be able to get rid of proving key buffer uint8_t const* pk_buf; - size_t pk_size = private_kernel__init_proving_key(&pk_buf); - info("Proving key size: ", pk_size); + private_kernel__init_proving_key(&pk_buf); + // info("Proving key size: ", pk_size); // TODO might be able to get rid of verification key buffer - uint8_t const* vk_buf; - size_t vk_size = private_kernel__init_verification_key(pk_buf, &vk_buf); - info("Verification key size: ", vk_size); + // uint8_t const* vk_buf; + // size_t vk_size = private_kernel__init_verification_key(pk_buf, &vk_buf); + // info("Verification key size: ", vk_size); std::vector signed_constructor_tx_request_vec; - write(signed_constructor_tx_request_vec, signed_constructor_tx_request); - - PrivateCallData private_constructor_call = PrivateCallData{ - .call_stack_item = constructor_call_stack_item, - .private_call_stack_preimages = constructor_ctx.get_private_call_stack_items(), - - .proof = constructor_proof, - .vk = constructor_vk, + write(signed_constructor_tx_request_vec, private_inputs.signed_tx_request); - // .function_leaf_membership_witness TODO - // .contract_leaf_membership_witness TODO - - .portal_contract_address = new_portal_contract_address, - }; std::vector private_constructor_call_vec; - write(private_constructor_call_vec, private_constructor_call); + write(private_constructor_call_vec, private_inputs.private_call); - uint8_t const* proof_data; - uint8_t const* public_inputs; - info("Simulating to generate public inputs..."); + uint8_t const* proof_data_buf; + uint8_t const* public_inputs_buf; + // info("Simulating to generate public inputs..."); size_t public_inputs_size = private_kernel__sim(signed_constructor_tx_request_vec.data(), nullptr, // no previous kernel on first iteration private_constructor_call_vec.data(), true, // first iteration - &public_inputs); + &public_inputs_buf); - info("Proving"); + // TODO better equality check + // for (size_t i = 0; i < public_inputs_size; i++) + for (size_t i = 0; i < 10; i++) { + ASSERT_EQ(public_inputs_buf[i], expected_public_inputs_vec[i]); + } + (void)public_inputs_size; + // info("Proving"); size_t proof_data_size = private_kernel__prove(signed_constructor_tx_request_vec.data(), - nullptr, + nullptr, // no previous kernel on first iteration private_constructor_call_vec.data(), pk_buf, true, // first iteration - &proof_data); - info("Proof size: ", proof_data_size); - info("PublicInputs size: ", public_inputs_size); + &proof_data_buf); + (void)proof_data_size; + // info("Proof size: ", proof_data_size); + // info("PublicInputs size: ", public_inputs_size); free((void*)pk_buf); - free((void*)vk_buf); - free((void*)proof_data); - free((void*)public_inputs); + // free((void*)vk_buf); + free((void*)proof_data_buf); + free((void*)public_inputs_buf); } +/** + * @brief Test this dummy cbind + */ TEST(private_kernel_tests, test_dummy_previous_kernel_cbind) { uint8_t const* cbind_previous_kernel_buf; - size_t cbind_buf_size = private_kernel__dummy_previous_kernel(&cbind_previous_kernel_buf); + size_t const cbind_buf_size = private_kernel__dummy_previous_kernel(&cbind_previous_kernel_buf); - PreviousKernelData previous_kernel = utils::dummy_previous_kernel_with_vk_proof(); + auto const& previous_kernel = utils::dummy_previous_kernel_with_vk_proof(); std::vector expected_vec; write(expected_vec, previous_kernel); @@ -1125,12 +609,12 @@ TEST(private_kernel_tests, test_dummy_previous_kernel_cbind) // would be best if we could just check struct equality or check // equality of an entire memory region (same as other similar TODOs // in other test files) - if (cbind_buf_size > 10) { - // for (size_t 0; i < public_inputs_size; i++) { - for (size_t i = 0; i < 10; i++) { - ASSERT_EQ(cbind_previous_kernel_buf[i], expected_vec[i]); - } + // TODO better equality check + // for (size_t i = 0; i < cbind_buf_size; i++) { + for (size_t i = 0; i < 10; i++) { + ASSERT_EQ(cbind_previous_kernel_buf[i], expected_vec[i]); } + (void)cbind_buf_size; } } // namespace aztec3::circuits::kernel::private_kernel diff --git a/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit.cpp b/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit.cpp index d9265ffe..781856bc 100644 --- a/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit.cpp +++ b/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit.cpp @@ -14,7 +14,6 @@ namespace aztec3::circuits::kernel::private_kernel { -using aztec3::circuits::abis::FunctionLeafPreimage; using aztec3::circuits::abis::private_kernel::ContractLeafPreimage; using aztec3::circuits::abis::private_kernel::NewContractData; using aztec3::circuits::abis::private_kernel::PrivateInputs; @@ -29,7 +28,8 @@ using DummyComposer = aztec3::utils::DummyComposer; using aztec3::circuits::compute_constructor_hash; using aztec3::circuits::compute_contract_address; -using aztec3::circuits::root_from_sibling_path; +using aztec3::circuits::contract_tree_root_from_siblings; +using aztec3::circuits::function_tree_root_from_siblings; // using plonk::stdlib::merkle_tree:: @@ -138,44 +138,34 @@ void contract_logic(DummyComposer& composer, PrivateInputs const& private_in * - Hash the contract_leaf with the contract_leaf's sibling_path to get the contract_tree_root */ - const auto function_leaf_preimage = FunctionLeafPreimage{ - .function_selector = private_inputs.private_call.call_stack_item.function_data.function_selector, - .is_private = true, - .vk_hash = private_call_vk_hash, - .acir_hash = private_inputs.private_call.acir_hash, - }; - - const auto function_leaf = function_leaf_preimage.hash(); - - const auto& function_leaf_index = private_inputs.private_call.function_leaf_membership_witness.leaf_index; - const auto& function_leaf_sibling_path = private_inputs.private_call.function_leaf_membership_witness.sibling_path; - - const auto& function_tree_root = - root_from_sibling_path(function_leaf, function_leaf_index, function_leaf_sibling_path); - - const ContractLeafPreimage contract_leaf_preimage{ - storage_contract_address, - portal_contract_address, - function_tree_root, - }; - - const auto contract_leaf = contract_leaf_preimage.hash(); - - auto& contract_leaf_index = private_inputs.private_call.contract_leaf_membership_witness.leaf_index; - auto& contract_leaf_sibling_path = private_inputs.private_call.contract_leaf_membership_witness.sibling_path; - - const auto& computed_contract_tree_root = - root_from_sibling_path(contract_leaf, contract_leaf_index, contract_leaf_sibling_path); - - auto& purported_contract_tree_root = + // ensure that historic/purported contract tree root matches the one in previous kernel + auto const& purported_contract_tree_root = private_inputs.private_call.call_stack_item.public_inputs.historic_contract_tree_root; - composer.do_assert(computed_contract_tree_root == purported_contract_tree_root, - "computed_contract_tree_root doesn't match purported_contract_tree_root"); - - auto& previous_kernel_contract_tree_root = + auto const& previous_kernel_contract_tree_root = private_inputs.previous_kernel.public_inputs.constants.old_tree_roots.contract_tree_root; composer.do_assert(purported_contract_tree_root == previous_kernel_contract_tree_root, "purported_contract_tree_root doesn't match previous_kernel_contract_tree_root"); + + // The logic below ensures that the contract exists in the contracts tree + if (!is_contract_deployment) { + auto const& computed_function_tree_root = function_tree_root_from_siblings( + private_inputs.private_call.call_stack_item.function_data.function_selector, + true, // is_private + private_call_vk_hash, + private_inputs.private_call.acir_hash, + private_inputs.private_call.function_leaf_membership_witness.leaf_index, + private_inputs.private_call.function_leaf_membership_witness.sibling_path); + + auto const& computed_contract_tree_root = contract_tree_root_from_siblings( + computed_function_tree_root, + storage_contract_address, + portal_contract_address, + private_inputs.private_call.contract_leaf_membership_witness.leaf_index, + private_inputs.private_call.contract_leaf_membership_witness.sibling_path); + + composer.do_assert(computed_contract_tree_root == purported_contract_tree_root, + "computed_contract_tree_root doesn't match purported_contract_tree_root"); + } } void update_end_values(DummyComposer& composer, @@ -351,6 +341,8 @@ PublicInputs native_private_kernel_circuit(DummyComposer& composer, PrivateI update_end_values(composer, private_inputs, public_inputs); + contract_logic(composer, private_inputs, public_inputs); + // We'll skip any verification in this native implementation, because for a Local Developer Testnet, there won't // _be_ a valid proof to verify!!! auto aggregation_object = verify_proofs(composer, // private_inputs,