From 689fdbfa3e4454df480ef211b9cb391e6c432646 Mon Sep 17 00:00:00 2001 From: IlyasRidhuan Date: Wed, 25 Sep 2024 14:27:35 +0000 Subject: [PATCH] feat(avm): class id + contract address --- barretenberg/cpp/src/barretenberg/bb/main.cpp | 12 +- .../vm/avm/tests/execution.test.cpp | 114 ++++++++------ .../vm/avm/trace/bytecode_trace.cpp | 4 +- .../vm/avm/trace/bytecode_trace.hpp | 5 +- .../barretenberg/vm/avm/trace/execution.cpp | 58 +++---- .../barretenberg/vm/avm/trace/execution.hpp | 14 +- .../vm/avm/trace/execution_hints.hpp | 67 +++++++- .../src/barretenberg/vm/avm/trace/trace.cpp | 7 +- .../src/barretenberg/vm/avm/trace/trace.hpp | 3 +- .../contracts/avm_test_contract/src/main.nr | 8 +- .../bb-prover/src/avm_proving.test.ts | 8 +- yarn-project/bb-prover/src/bb/execute.ts | 7 - .../circuits.js/src/structs/avm/avm.ts | 143 ++++++++++++++---- .../circuits.js/src/tests/factories.ts | 19 ++- .../src/avm_integration.test.ts | 14 +- .../src/orchestrator/orchestrator.ts | 1 - .../simulator/src/avm/avm_simulator.test.ts | 1 + .../simulator/src/avm/journal/journal.ts | 46 +++++- .../src/public/dual_side_effect_trace.ts | 20 ++- .../enqueued_call_side_effect_trace.test.ts | 2 +- .../public/enqueued_call_side_effect_trace.ts | 39 ++++- .../src/public/side_effect_trace.test.ts | 2 +- .../simulator/src/public/side_effect_trace.ts | 36 ++++- .../src/public/side_effect_trace_interface.ts | 7 +- 24 files changed, 460 insertions(+), 177 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index 4958dc7765e..df9d7bf88ad 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -953,18 +953,17 @@ void vk_as_fields(const std::string& vk_path, const std::string& output_path) * @param hints_path Path to the file containing the serialised avm circuit hints * @param output_path Path (directory) to write the output proof and verification keys */ -void avm_prove(const std::filesystem::path& bytecode_path, - const std::filesystem::path& calldata_path, +void avm_prove(const std::filesystem::path& calldata_path, const std::filesystem::path& public_inputs_path, const std::filesystem::path& hints_path, const std::filesystem::path& output_path) { - std::vector const bytecode = read_file(bytecode_path); std::vector const calldata = many_from_buffer(read_file(calldata_path)); std::vector const public_inputs_vec = many_from_buffer(read_file(public_inputs_path)); auto const avm_hints = bb::avm_trace::ExecutionHints::from(read_file(hints_path)); - vinfo("bytecode size: ", bytecode.size()); + // Using [0] is fine now for the top-level call, but we might need to index by address in future + vinfo("bytecode size: ", avm_hints.all_contract_bytecode[0].bytecode.size()); vinfo("calldata size: ", calldata.size()); vinfo("public_inputs size: ", public_inputs_vec.size()); vinfo("hints.storage_value_hints size: ", avm_hints.storage_value_hints.size()); @@ -979,7 +978,7 @@ void avm_prove(const std::filesystem::path& bytecode_path, // Prove execution and return vk auto const [verification_key, proof] = - AVM_TRACK_TIME_V("prove/all", avm_trace::Execution::prove(bytecode, calldata, public_inputs_vec, avm_hints)); + AVM_TRACK_TIME_V("prove/all", avm_trace::Execution::prove(calldata, public_inputs_vec, avm_hints)); std::vector vk_as_fields = verification_key.to_field_elements(); @@ -1526,7 +1525,6 @@ int main(int argc, char* argv[]) write_recursion_inputs_honk(bytecode_path, witness_path, output_path); #ifndef DISABLE_AZTEC_VM } else if (command == "avm_prove") { - std::filesystem::path avm_bytecode_path = get_option(args, "--avm-bytecode", "./target/avm_bytecode.bin"); std::filesystem::path avm_calldata_path = get_option(args, "--avm-calldata", "./target/avm_calldata.bin"); std::filesystem::path avm_public_inputs_path = get_option(args, "--avm-public-inputs", "./target/avm_public_inputs.bin"); @@ -1535,7 +1533,7 @@ int main(int argc, char* argv[]) std::filesystem::path output_path = get_option(args, "-o", "./proofs"); extern std::filesystem::path avm_dump_trace_path; avm_dump_trace_path = get_option(args, "--avm-dump-trace", ""); - avm_prove(avm_bytecode_path, avm_calldata_path, avm_public_inputs_path, avm_hints_path, output_path); + avm_prove(avm_calldata_path, avm_public_inputs_path, avm_hints_path, output_path); } else if (command == "avm_verify") { return avm_verify(proof_path, vk_path) ? 0 : 1; #endif diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp index 52d87b837a7..4eabce2b29d 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp @@ -36,13 +36,9 @@ class AvmExecutionTests : public ::testing::Test { Execution::set_trace_builder_constructor([](VmPublicInputsNT public_inputs, ExecutionHints execution_hints, uint32_t side_effect_counter, - std::vector calldata, - const std::vector>& all_contracts_bytecode) { - return AvmTraceBuilder(std::move(public_inputs), - std::move(execution_hints), - side_effect_counter, - std::move(calldata), - all_contracts_bytecode) + std::vector calldata) { + return AvmTraceBuilder( + std::move(public_inputs), std::move(execution_hints), side_effect_counter, std::move(calldata)) .set_full_precomputed_tables(false) .set_range_check_required(false); }); @@ -57,7 +53,8 @@ class AvmExecutionTests : public ::testing::Test { srs::init_crs_factory("../srs_db/ignition"); public_inputs_vec.at(DA_START_GAS_LEFT_PCPI_OFFSET) = DEFAULT_INITIAL_DA_GAS; public_inputs_vec.at(L2_START_GAS_LEFT_PCPI_OFFSET) = DEFAULT_INITIAL_L2_GAS; - public_inputs = convert_public_inputs(public_inputs_vec); + public_inputs_vec.at(ADDRESS_KERNEL_INPUTS_COL_OFFSET) = 0xdeadbeef; + public_inputs = avm_trace::convert_public_inputs(public_inputs_vec); }; /** @@ -71,7 +68,21 @@ class AvmExecutionTests : public ::testing::Test { std::vector calldata{}; std::vector returndata{}; - return Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints()); + auto execution_hints = ExecutionHints().with_avm_contract_bytecode({ bytecode }); + execution_hints.all_contract_bytecode[0].contract_instance.address = 0xdeadbeef; + + return AvmExecutionTests::gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); + } + + std::vector gen_trace(std::vector bytecode, + std::vector const& calldata, + std::vector const& public_inputs_vec, + std::vector& returndata, + ExecutionHints& execution_hints) const + { + execution_hints.all_contract_bytecode = { bytecode }; + execution_hints.all_contract_bytecode[0].contract_instance.address = 0xdeadbeef; + return Execution::gen_trace(calldata, public_inputs_vec, returndata, execution_hints); } void feed_output(uint32_t output_offset, FF const& value, FF const& side_effect_counter, FF const& metadata) @@ -474,8 +485,8 @@ TEST_F(AvmExecutionTests, jumpAndCalldatacopy) Field(&Instruction::operands, ElementsAre(VariantWith(5))))); std::vector returndata; - auto trace = - Execution::gen_trace(bytecode, std::vector{ 13, 156 }, public_inputs_vec, returndata, ExecutionHints()); + ExecutionHints execution_hints; + auto trace = gen_trace(bytecode, std::vector{ 13, 156 }, public_inputs_vec, returndata, execution_hints); // Expected sequence of PCs during execution std::vector pc_sequence{ @@ -564,10 +575,9 @@ TEST_F(AvmExecutionTests, jumpiAndCalldatacopy) ElementsAre(VariantWith(0), VariantWith(6), VariantWith(10))))); std::vector returndata; - auto trace_jump = - Execution::gen_trace(bytecode, std::vector{ 9873123 }, public_inputs_vec, returndata, ExecutionHints()); - auto trace_no_jump = - Execution::gen_trace(bytecode, std::vector{ 0 }, public_inputs_vec, returndata, ExecutionHints()); + ExecutionHints execution_hints; + auto trace_jump = gen_trace(bytecode, std::vector{ 9873123 }, public_inputs_vec, returndata, execution_hints); + auto trace_no_jump = gen_trace(bytecode, std::vector{ 0 }, public_inputs_vec, returndata, execution_hints); // Expected sequence of PCs during execution with jump std::vector pc_sequence_jump{ 0, 1, 2, 3, 4, 6, 7 }; @@ -774,8 +784,9 @@ TEST_F(AvmExecutionTests, toRadixLeOpcode) // Assign a vector that we will mutate internally in gen_trace to store the return values; std::vector returndata; - auto trace = Execution::gen_trace( - bytecode, std::vector{ FF::modulus - FF(1) }, public_inputs_vec, returndata, ExecutionHints()); + ExecutionHints execution_hints; + auto trace = + gen_trace(bytecode, std::vector{ FF::modulus - FF(1) }, public_inputs_vec, returndata, execution_hints); // Find the first row enabling the TORADIXLE selector // Expected output is bitwise decomposition of MODULUS - 1..could hardcode the result but it's a bit long @@ -840,8 +851,9 @@ TEST_F(AvmExecutionTests, toRadixLeOpcodeBitsMode) // Assign a vector that we will mutate internally in gen_trace to store the return values; std::vector returndata; - auto trace = Execution::gen_trace( - bytecode, std::vector{ FF::modulus - FF(1) }, public_inputs_vec, returndata, ExecutionHints()); + ExecutionHints execution_hints; + auto trace = + gen_trace(bytecode, std::vector{ FF::modulus - FF(1) }, public_inputs_vec, returndata, execution_hints); // Find the first row enabling the TORADIXLE selector // Expected output is bitwise decomposition of MODULUS - 1..could hardcode the result but it's a bit long @@ -915,7 +927,8 @@ TEST_F(AvmExecutionTests, sha256CompressionOpcode) // 4091010797,3974542186]), std::vector expected_output = { 1862536192, 526086805, 2067405084, 593147560, 726610467, 813867028, 4091010797ULL, 3974542186ULL }; - auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints()); + ExecutionHints execution_hints; + auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); EXPECT_EQ(returndata, expected_output); @@ -976,7 +989,8 @@ TEST_F(AvmExecutionTests, poseidon2PermutationOpCode) FF(std::string("0x018555a8eb50cf07f64b019ebaf3af3c925c93e631f3ecd455db07bbb52bbdd3")), FF(std::string("0x0cbea457c91c22c6c31fd89afd2541efc2edf31736b9f721e823b2165c90fd41")) }; - auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints()); + ExecutionHints execution_hints; + auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); EXPECT_EQ(returndata, expected_output); @@ -1043,7 +1057,8 @@ TEST_F(AvmExecutionTests, keccakf1600OpCode) // Assign a vector that we will mutate internally in gen_trace to store the return values; std::vector calldata = std::vector(); std::vector returndata = std::vector(); - auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints()); + ExecutionHints execution_hints; + auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); EXPECT_EQ(returndata, expected_output); @@ -1110,7 +1125,8 @@ TEST_F(AvmExecutionTests, embeddedCurveAddOpCode) // Assign a vector that we will mutate internally in gen_trace to store the return values; std::vector returndata; std::vector calldata = { a.x, a.y, FF(a_is_inf ? 1 : 0), b.x, b.y, FF(b_is_inf ? 1 : 0) }; - auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints()); + ExecutionHints execution_hints; + auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); EXPECT_EQ(returndata, expected_output); @@ -1197,7 +1213,8 @@ TEST_F(AvmExecutionTests, msmOpCode) // Assign a vector that we will mutate internally in gen_trace to store the return values; std::vector returndata; - auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints()); + ExecutionHints execution_hints; + auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); EXPECT_EQ(returndata, expected_output); @@ -1355,16 +1372,16 @@ TEST_F(AvmExecutionTests, kernelInputOpcodes) std::vector calldata; FF sender = 1; - FF address = 2; - FF function_selector = 3; - FF transaction_fee = 4; - FF chainid = 5; - FF version = 6; - FF blocknumber = 7; - FF timestamp = 8; - FF feeperl2gas = 9; - FF feeperdagas = 10; - FF is_static_call = 11; + FF address = 0xdeadbeef; + FF function_selector = 4; + FF transaction_fee = 5; + FF chainid = 6; + FF version = 7; + FF blocknumber = 8; + FF timestamp = 9; + FF feeperl2gas = 10; + FF feeperdagas = 11; + FF is_static_call = 12; // The return data for this test should be a the opcodes in sequence, as the opcodes dst address lines up with // this array The returndata call above will then return this array @@ -1393,7 +1410,8 @@ TEST_F(AvmExecutionTests, kernelInputOpcodes) public_inputs_vec[FEE_PER_L2_GAS_PCPI_OFFSET] = feeperl2gas; std::vector returndata; - auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints()); + ExecutionHints execution_hints; + auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); // Validate returndata EXPECT_EQ(returndata, expected_returndata); @@ -1559,7 +1577,8 @@ TEST_F(AvmExecutionTests, ExecutorThrowsWithTooMuchGasAllocated) auto bytecode = hex_to_bytes(bytecode_hex); auto instructions = Deserialization::parse(bytecode); - EXPECT_THROW_WITH_MESSAGE(Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints()), + ExecutionHints execution_hints; + EXPECT_THROW_WITH_MESSAGE(gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints), "Cannot allocate more than MAX_L2_GAS_PER_ENQUEUED_CALL to the AVM for " "execution of an enqueued call"); } @@ -1578,7 +1597,8 @@ TEST_F(AvmExecutionTests, ExecutorThrowsWithIncorrectNumberOfPublicInputs) auto bytecode = hex_to_bytes(bytecode_hex); auto instructions = Deserialization::parse(bytecode); - EXPECT_THROW_WITH_MESSAGE(Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints()), + ExecutionHints execution_hints; + EXPECT_THROW_WITH_MESSAGE(gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints), "Public inputs vector is not of PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH"); } @@ -1621,7 +1641,8 @@ TEST_F(AvmExecutionTests, kernelOutputEmitOpcodes) std::vector calldata = {}; std::vector returndata = {}; - auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints()); + ExecutionHints execution_hints; + auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); // CHECK EMIT NOTE HASH // Check output data + side effect counters have been set correctly @@ -1656,7 +1677,7 @@ TEST_F(AvmExecutionTests, kernelOutputEmitOpcodes) auto emit_log_row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.main_sel_op_emit_unencrypted_log == 1; }); // Trust me bro for now, this is the truncated sha output - FF expected_hash = FF(std::string("0x006db65fd59fd356f6729140571b5bcd6bb3b83492a16e1bf0a3884442fc3c8a")); + FF expected_hash = FF(std::string("0x003383cbb254941b33c0aaf8476c4b9b532d70a2fb105ee908dd332f7d942df6")); EXPECT_EQ(emit_log_row->main_ia, expected_hash); EXPECT_EQ(emit_log_row->main_side_effect_counter, 2); // Value is 40 = 32 * log_length + 40 (and log_length is 0 in this case). @@ -1722,7 +1743,7 @@ TEST_F(AvmExecutionTests, kernelOutputStorageLoadOpcodeSimple) // side effect counter 0 = value 42 auto execution_hints = ExecutionHints().with_storage_value_hints({ { 0, 42 } }); - auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); + auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); // CHECK SLOAD // Check output data + side effect counters have been set correctly @@ -1776,7 +1797,8 @@ TEST_F(AvmExecutionTests, kernelOutputStorageStoreOpcodeSimple) std::vector returndata; - auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints()); + ExecutionHints execution_hints; + auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); // CHECK SSTORE auto sstore_row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.main_sel_op_sstore == 1; }); EXPECT_EQ(sstore_row->main_ia, 42); // Read value @@ -1839,7 +1861,7 @@ TEST_F(AvmExecutionTests, kernelOutputStorageOpcodes) // side effect counter 0 = value 42 auto execution_hints = ExecutionHints().with_storage_value_hints({ { 0, 42 } }); - auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); + auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); // CHECK SLOAD // Check output data + side effect counters have been set correctly @@ -1922,7 +1944,7 @@ TEST_F(AvmExecutionTests, kernelOutputHashExistsOpcodes) .with_storage_value_hints({ { 0, 1 }, { 1, 1 }, { 2, 1 } }) .with_note_hash_exists_hints({ { 0, 1 }, { 1, 1 }, { 2, 1 } }); - auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); + auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); // CHECK NOTEHASHEXISTS auto note_hash_row = @@ -2056,10 +2078,10 @@ TEST_F(AvmExecutionTests, opCallOpcodes) .l2_gas_used = 0, .da_gas_used = 0, .end_side_effect_counter = 0, - .bytecode = {}, + .contract_address = 0, } }); - auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); + auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); EXPECT_EQ(returndata, std::vector({ 9, 8, 1 })); // The 1 represents the success validate_trace(std::move(trace), public_inputs, calldata, returndata); @@ -2116,7 +2138,7 @@ TEST_F(AvmExecutionTests, opGetContractInstanceOpcodes) auto execution_hints = ExecutionHints().with_contract_instance_hints({ { address, { address, 1, 2, 3, 4, 5, public_keys_hints } } }); - auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); + auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints); EXPECT_EQ(returndata, std::vector({ 1, 2, 3, 4, 5, returned_point.x })); // The first one represents true validate_trace(std::move(trace), public_inputs, calldata, returndata); diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.cpp index 82041e02871..9fc743010de 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.cpp @@ -4,7 +4,7 @@ namespace bb::avm_trace { using poseidon2 = crypto::Poseidon2; -AvmBytecodeTraceBuilder::AvmBytecodeTraceBuilder(const std::vector>& all_contracts_bytecode) +AvmBytecodeTraceBuilder::AvmBytecodeTraceBuilder(const std::vector& all_contracts_bytecode) : all_contracts_bytecode(all_contracts_bytecode) {} @@ -31,7 +31,7 @@ void AvmBytecodeTraceBuilder::build_bytecode_columns() // This is the main loop that will generate the bytecode trace for (auto& contract_bytecode : all_contracts_bytecode) { FF running_hash = FF::zero(); - auto packed_bytecode = pack_bytecode(contract_bytecode); + auto packed_bytecode = pack_bytecode(contract_bytecode.bytecode); // This size is already based on the number of fields for (size_t i = 0; i < packed_bytecode.size(); ++i) { bytecode_trace.push_back(BytecodeTraceEntry{ diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.hpp index a62784fe59e..aab3d6d6a7f 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.hpp @@ -22,9 +22,8 @@ class AvmBytecodeTraceBuilder { // Derive the contract address FF contract_address{}; }; - AvmBytecodeTraceBuilder() = default; // These interfaces will change when we start feeding in more inputs and hints - AvmBytecodeTraceBuilder(const std::vector>& all_contracts_bytecode); + AvmBytecodeTraceBuilder(const std::vector& all_contracts_bytecode); size_t size() const { return bytecode_trace.size(); } void reset(); @@ -38,7 +37,7 @@ class AvmBytecodeTraceBuilder { std::vector bytecode_trace; // The first element is the main top-level contract, the rest are external calls - std::vector> all_contracts_bytecode; + std::vector all_contracts_bytecode; // TODO: Come back to this // VmPublicInputs public_inputs; // ExecutionHints hints; diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp index b3edc6f5826..6e5659f91dd 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp @@ -146,18 +146,13 @@ void show_trace_info(const auto& trace) } // namespace // Needed for dependency injection in tests. -Execution::TraceBuilderConstructor Execution::trace_builder_constructor = - [](VmPublicInputs public_inputs, - ExecutionHints execution_hints, - uint32_t side_effect_counter, - std::vector calldata, - std::vector> all_contract_bytecode) { - return AvmTraceBuilder(std::move(public_inputs), - std::move(execution_hints), - side_effect_counter, - std::move(calldata), - all_contract_bytecode); - }; +Execution::TraceBuilderConstructor Execution::trace_builder_constructor = [](VmPublicInputs public_inputs, + ExecutionHints execution_hints, + uint32_t side_effect_counter, + std::vector calldata) { + return AvmTraceBuilder( + std::move(public_inputs), std::move(execution_hints), side_effect_counter, std::move(calldata)); +}; /** * @brief Temporary routine to generate default public inputs (gas values) until we get @@ -180,8 +175,7 @@ std::vector Execution::getDefaultPublicInputs() * @throws runtime_error exception when the bytecode is invalid. * @return The verifier key and zk proof of the execution. */ -std::tuple Execution::prove(std::vector const& bytecode, - std::vector const& calldata, +std::tuple Execution::prove(std::vector const& calldata, std::vector const& public_inputs_vec, ExecutionHints const& execution_hints) { @@ -190,8 +184,8 @@ std::tuple Execution::prove(std::vector returndata; - std::vector trace = AVM_TRACK_TIME_V( - "prove/gen_trace", gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints)); + std::vector trace = + AVM_TRACK_TIME_V("prove/gen_trace", gen_trace(calldata, public_inputs_vec, returndata, execution_hints)); if (!avm_dump_trace_path.empty()) { info("Dumping trace as CSV to: " + avm_dump_trace_path.string()); dump_trace_as_csv(trace, avm_dump_trace_path); @@ -268,15 +262,13 @@ bool Execution::verify(AvmFlavor::VerificationKey vk, HonkProof const& proof) * @param public_inputs expressed as a vector of finite field elements. * @return The trace as a vector of Row. */ -std::vector Execution::gen_trace(std::vector const& bytecode, - std::vector const& calldata, +std::vector Execution::gen_trace(std::vector const& calldata, std::vector const& public_inputs_vec, std::vector& returndata, ExecutionHints const& execution_hints) { - std::vector instructions = Deserialization::parse(bytecode); - vinfo("Deserialized " + std::to_string(instructions.size()) + " instructions"); + vinfo("------- GENERATING TRACE -------"); // TODO(https://github.com/AztecProtocol/aztec-packages/issues/6718): construction of the public input columns // should be done in the kernel - this is stubbed and underconstrained @@ -284,15 +276,23 @@ std::vector Execution::gen_trace(std::vector const& bytecode, uint32_t start_side_effect_counter = !public_inputs_vec.empty() ? static_cast(public_inputs_vec[START_SIDE_EFFECT_COUNTER_PCPI_OFFSET]) : 0; - std::vector> all_contract_bytecode; - all_contract_bytecode.reserve(execution_hints.externalcall_hints.size() + 1); - // Start with the main, top-level contract bytecode - all_contract_bytecode.push_back(bytecode); - for (const auto& externalcall_hint : execution_hints.externalcall_hints) { - all_contract_bytecode.emplace_back(externalcall_hint.bytecode); - } - AvmTraceBuilder trace_builder = Execution::trace_builder_constructor( - public_inputs, execution_hints, start_side_effect_counter, calldata, all_contract_bytecode); + + // This address is the top-level contract address + vinfo("Length of all contract bytecode: ", execution_hints.all_contract_bytecode.size()); + + FF contract_address = std::get<0>(public_inputs)[ADDRESS_KERNEL_INPUTS_COL_OFFSET]; + vinfo("Top level contract address: ", contract_address); + // We use it to extract the bytecode we need to execute + std::vector bytecode = + std::find_if(execution_hints.all_contract_bytecode.begin(), + execution_hints.all_contract_bytecode.end(), + [&](auto& contract) { return contract.contract_instance.address == contract_address; }) + ->bytecode; + + std::vector instructions = Deserialization::parse(bytecode); + vinfo("Deserialized " + std::to_string(instructions.size()) + " instructions"); + AvmTraceBuilder trace_builder = + Execution::trace_builder_constructor(public_inputs, execution_hints, start_side_effect_counter, calldata); // Copied version of pc maintained in trace builder. The value of pc is evolving based // on opcode logic and therefore is not maintained here. However, the next opcode in the execution diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.hpp index da222b87f67..6b20392decc 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.hpp @@ -15,12 +15,10 @@ namespace bb::avm_trace { class Execution { public: static constexpr size_t SRS_SIZE = 1 << 22; - using TraceBuilderConstructor = - std::function calldata, - const std::vector>& all_contract_bytecode)>; + using TraceBuilderConstructor = std::function calldata)>; Execution() = default; @@ -30,8 +28,7 @@ class Execution { // Bytecode is currently the bytecode of the top-level function call // Eventually this will be the bytecode of the dispatch function of top-level contract - static std::vector gen_trace(std::vector const& bytecode, - std::vector const& calldata, + static std::vector gen_trace(std::vector const& calldata, std::vector const& public_inputs, std::vector& returndata, ExecutionHints const& execution_hints); @@ -43,7 +40,6 @@ class Execution { } static std::tuple prove( - std::vector const& bytecode, std::vector const& calldata = {}, std::vector const& public_inputs_vec = getDefaultPublicInputs(), ExecutionHints const& execution_hints = {}); diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution_hints.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution_hints.hpp index 0b3065982d1..4e9fe566bb8 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution_hints.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution_hints.hpp @@ -14,7 +14,7 @@ struct ExternalCallHint { uint32_t l2_gas_used; uint32_t da_gas_used; FF end_side_effect_counter; - std::vector bytecode; + FF contract_address; }; // Add support for deserialization of ExternalCallHint. This is implicitly used by serialize::read @@ -27,7 +27,21 @@ inline void read(uint8_t const*& it, ExternalCallHint& hint) read(it, hint.da_gas_used); read(it, hint.l2_gas_used); read(it, hint.end_side_effect_counter); - read(it, hint.bytecode); + read(it, hint.contract_address); +} + +struct ContractClassIdHint { + FF artifact_hash; + FF private_fn_root; + FF public_bytecode_commitment; +}; + +inline void read(uint8_t const*& it, ContractClassIdHint& preimage) +{ + using serialize::read; + read(it, preimage.artifact_hash); + read(it, preimage.private_fn_root); + read(it, preimage.public_bytecode_commitment); } struct PublicKeysHint { @@ -48,7 +62,7 @@ struct PublicKeysHint { struct ContractInstanceHint { FF address; - FF instance_found_in_address; + bool exists; // Useful for membership checks FF salt; FF deployer_addr; FF contract_class_id; @@ -71,7 +85,7 @@ inline void read(uint8_t const*& it, ContractInstanceHint& hint) { using serialize::read; read(it, hint.address); - read(it, hint.instance_found_in_address); + read(it, hint.exists); read(it, hint.salt); read(it, hint.deployer_addr); read(it, hint.contract_class_id); @@ -79,6 +93,32 @@ inline void read(uint8_t const*& it, ContractInstanceHint& hint) read(it, hint.public_keys); } +struct AvmContractBytecode { + std::vector bytecode; + ContractInstanceHint contract_instance{}; + ContractClassIdHint contract_class_id_preimage{}; + + AvmContractBytecode() = default; + AvmContractBytecode(std::vector bytecode, + ContractInstanceHint contract_instance, + ContractClassIdHint contract_class_id_preimage) + : bytecode(std::move(bytecode)) + , contract_instance(contract_instance) + , contract_class_id_preimage(contract_class_id_preimage) + {} + AvmContractBytecode(std::vector bytecode) + : bytecode(std::move(bytecode)) + {} +}; + +inline void read(uint8_t const*& it, AvmContractBytecode& bytecode) +{ + using serialize::read; + read(it, bytecode.bytecode); + read(it, bytecode.contract_instance); + read(it, bytecode.contract_class_id_preimage); +} + struct ExecutionHints { std::vector> storage_value_hints; std::vector> note_hash_exists_hints; @@ -86,6 +126,8 @@ struct ExecutionHints { std::vector> l1_to_l2_message_exists_hints; std::vector externalcall_hints; std::map contract_instance_hints; + // We could make this address-indexed + std::vector all_contract_bytecode; ExecutionHints() = default; @@ -120,6 +162,11 @@ struct ExecutionHints { this->contract_instance_hints = std::move(contract_instance_hints); return *this; } + ExecutionHints& with_avm_contract_bytecode(std::vector all_contract_bytecode) + { + this->all_contract_bytecode = std::move(all_contract_bytecode); + return *this; + } static void push_vec_into_map(std::unordered_map& into_map, const std::vector>& from_pair_vec) @@ -172,14 +219,18 @@ struct ExecutionHints { contract_instance_hints[instance.address] = instance; } + std::vector all_contract_bytecode; + read(it, all_contract_bytecode); + if (it != data.data() + data.size()) { - throw_or_abort("Failed to deserialize ExecutionHints: only read" + std::to_string(it - data.data()) + + throw_or_abort("Failed to deserialize ExecutionHints: only read " + std::to_string(it - data.data()) + " bytes out of " + std::to_string(data.size()) + " bytes"); } return { std::move(storage_value_hints), std::move(note_hash_exists_hints), std::move(nullifier_exists_hints), std::move(l1_to_l2_message_exists_hints), - std::move(externalcall_hints), std::move(contract_instance_hints) }; + std::move(externalcall_hints), std::move(contract_instance_hints), + std::move(all_contract_bytecode) }; } private: @@ -188,13 +239,15 @@ struct ExecutionHints { std::vector> nullifier_exists_hints, std::vector> l1_to_l2_message_exists_hints, std::vector externalcall_hints, - std::map contract_instance_hints) + std::map contract_instance_hints, + std::vector all_contract_bytecode) : storage_value_hints(std::move(storage_value_hints)) , note_hash_exists_hints(std::move(note_hash_exists_hints)) , nullifier_exists_hints(std::move(nullifier_exists_hints)) , l1_to_l2_message_exists_hints(std::move(l1_to_l2_message_exists_hints)) , externalcall_hints(std::move(externalcall_hints)) , contract_instance_hints(std::move(contract_instance_hints)) + , all_contract_bytecode(std::move(all_contract_bytecode)) {} }; diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp index 26b321810aa..acf48874976 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp @@ -261,14 +261,13 @@ void AvmTraceBuilder::finalise_mem_trace_lookup_counts() AvmTraceBuilder::AvmTraceBuilder(VmPublicInputs public_inputs, ExecutionHints execution_hints_, uint32_t side_effect_counter, - std::vector calldata, - const std::vector>& all_contract_bytecode) + std::vector calldata) // NOTE: we initialise the environment builder here as it requires public inputs : calldata(std::move(calldata)) , side_effect_counter(side_effect_counter) , execution_hints(std::move(execution_hints_)) , kernel_trace_builder(side_effect_counter, public_inputs, execution_hints) - , bytecode_trace_builder(all_contract_bytecode) + , bytecode_trace_builder(execution_hints_.all_contract_bytecode) { // TODO: think about cast gas_trace_builder.set_initial_gas( @@ -2392,7 +2391,7 @@ void AvmTraceBuilder::op_get_contract_instance(uint8_t indirect, uint32_t addres ContractInstanceHint contract_instance = execution_hints.contract_instance_hints.at(read_address.val); std::vector public_key_fields = contract_instance.public_keys.to_fields(); // NOTE: we don't write the first entry (the contract instance's address/key) to memory - std::vector contract_instance_vec = { contract_instance.instance_found_in_address, + std::vector contract_instance_vec = { contract_instance.exists ? FF::one() : FF::zero(), contract_instance.salt, contract_instance.deployer_addr, contract_instance.contract_class_id, diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp index 3113dd58b4a..a611634a53b 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp @@ -33,8 +33,7 @@ class AvmTraceBuilder { AvmTraceBuilder(VmPublicInputs public_inputs = {}, ExecutionHints execution_hints = {}, uint32_t side_effect_counter = 0, - std::vector calldata = {}, - const std::vector>& all_contract_bytecode = {}); + std::vector calldata = {}); uint32_t getPc() const { return pc; } diff --git a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr index ee524904896..2a5bfac6d6e 100644 --- a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -596,9 +596,9 @@ contract AvmTest { let _ = l1_to_l2_msg_exists(1, 2); dep::aztec::oracle::debug_log::debug_log("send_l2_to_l1_msg"); let _ = send_l2_to_l1_msg(EthAddress::from_field(0x2020), 1); - dep::aztec::oracle::debug_log::debug_log("nested_call_to_add"); - let _ = nested_call_to_add(1, 2); - dep::aztec::oracle::debug_log::debug_log("nested_static_call_to_add"); - let _ = nested_static_call_to_add(1, 2); + //dep::aztec::oracle::debug_log::debug_log("nested_call_to_add"); + //let _ = nested_call_to_add(1, 2); + //dep::aztec::oracle::debug_log::debug_log("nested_static_call_to_add"); + //let _ = nested_static_call_to_add(1, 2); } } diff --git a/yarn-project/bb-prover/src/avm_proving.test.ts b/yarn-project/bb-prover/src/avm_proving.test.ts index 50af482b364..73f516724e0 100644 --- a/yarn-project/bb-prover/src/avm_proving.test.ts +++ b/yarn-project/bb-prover/src/avm_proving.test.ts @@ -6,6 +6,7 @@ import { SerializableContractInstance, VerificationKeyData, } from '@aztec/circuits.js'; +import { makeContractClassPublic } from '@aztec/circuits.js/testing'; import { Fr, Point } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; import { AvmSimulator, PublicSideEffectTrace, type WorldStateDB } from '@aztec/simulator'; @@ -81,6 +82,9 @@ const proveAndVerifyAvmTestContract = async ( }).withAddress(environment.address); worldStateDB.getContractInstance.mockResolvedValue(Promise.resolve(contractInstance)); + const contractClass = makeContractClassPublic(); + worldStateDB.getContractClass.mockResolvedValue(Promise.resolve(contractClass)); + const storageValue = new Fr(5); worldStateDB.storageRead.mockResolvedValue(Promise.resolve(storageValue)); @@ -97,13 +101,14 @@ const proveAndVerifyAvmTestContract = async ( // Use a simple contract that emits a side effect const bytecode = getAvmTestContractBytecode('public_dispatch'); + jest.spyOn(worldStateDB, 'getBytecode').mockResolvedValue(bytecode); // The paths for the barretenberg binary and the write path are hardcoded for now. const bbPath = path.resolve('../../barretenberg/cpp/build/bin/bb'); const bbWorkingDirectory = await fs.mkdtemp(path.join(tmpdir(), 'bb-')); // First we simulate (though it's not needed in this simple case). const simulator = new AvmSimulator(context); - const avmResult = await simulator.executeBytecode(bytecode); + const avmResult = await simulator.execute(); if (assertionErrString == undefined) { expect(avmResult.reverted).toBe(false); @@ -125,7 +130,6 @@ const proveAndVerifyAvmTestContract = async ( const avmCircuitInputs = new AvmCircuitInputs( functionName, - /*bytecode=*/ simulator.getBytecode()!, // uncompressed bytecode /*calldata=*/ context.environment.calldata, /*publicInputs=*/ getPublicInputs(pxResult), /*avmHints=*/ pxResult.avmCircuitHints, diff --git a/yarn-project/bb-prover/src/bb/execute.ts b/yarn-project/bb-prover/src/bb/execute.ts index d5fbcb06d76..76b9403be95 100644 --- a/yarn-project/bb-prover/src/bb/execute.ts +++ b/yarn-project/bb-prover/src/bb/execute.ts @@ -504,7 +504,6 @@ export async function generateAvmProof( } // Paths for the inputs - const bytecodePath = join(workingDirectory, AVM_BYTECODE_FILENAME); const calldataPath = join(workingDirectory, AVM_CALLDATA_FILENAME); const publicInputsPath = join(workingDirectory, AVM_PUBLIC_INPUTS_FILENAME); const avmHintsPath = join(workingDirectory, AVM_HINTS_FILENAME); @@ -525,10 +524,6 @@ export async function generateAvmProof( try { // Write the inputs to the working directory. - await fs.writeFile(bytecodePath, input.bytecode); - if (!filePresent(bytecodePath)) { - return { status: BB_RESULT.FAILURE, reason: `Could not write bytecode at ${bytecodePath}` }; - } await fs.writeFile( calldataPath, input.calldata.map(fr => fr.toBuffer()), @@ -553,8 +548,6 @@ export async function generateAvmProof( } const args = [ - '--avm-bytecode', - bytecodePath, '--avm-calldata', calldataPath, '--avm-public-inputs', diff --git a/yarn-project/circuits.js/src/structs/avm/avm.ts b/yarn-project/circuits.js/src/structs/avm/avm.ts index a53226c429d..fbc25367be1 100644 --- a/yarn-project/circuits.js/src/structs/avm/avm.ts +++ b/yarn-project/circuits.js/src/structs/avm/avm.ts @@ -1,7 +1,9 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { type FieldsOf } from '@aztec/foundation/types'; +import { type ContractClassIdPreimage } from '../../contract/contract_class_id.js'; import { PublicKeys } from '../../types/public_keys.js'; import { Gas } from '../gas.js'; import { PublicCircuitPublicInputs } from '../public_circuit_public_inputs.js'; @@ -82,14 +84,13 @@ export class AvmExternalCallHint { * @param returnData the data returned by the external call. * @param gasUsed gas used by the external call (not including the cost of the CALL opcode itself). * @param endSideEffectCounter value of side effect counter at the end of the external call. - * @param bytecode currently the bytecode of the nested call function, will be changed to the contract bytecode (via the dispatch function) of the nested call */ constructor( public readonly success: Fr, returnData: Fr[], public readonly gasUsed: Gas, public readonly endSideEffectCounter: Fr, - public readonly bytecode: Buffer, + public readonly contractAddress: AztecAddress, ) { this.returnData = new Vector(returnData); } @@ -120,7 +121,7 @@ export class AvmExternalCallHint { this.returnData.items.length == 0 && this.gasUsed.isEmpty() && this.endSideEffectCounter.isZero() && - this.bytecode.length == 0 + this.contractAddress.isZero() ); } @@ -135,7 +136,7 @@ export class AvmExternalCallHint { fields.returnData.items, fields.gasUsed, fields.endSideEffectCounter, - fields.bytecode, + fields.contractAddress, ); } @@ -145,18 +146,7 @@ export class AvmExternalCallHint { * @returns An array of fields. */ static getFields(fields: FieldsOf) { - // Buffers aren't serialised the same way as they are read (lenth prefixed), so we need to do this manually. - const lengthPrefixedBytecode = Buffer.alloc(fields.bytecode.length + 4); - // Add a 4-byte length prefix to the bytecode. - lengthPrefixedBytecode.writeUInt32BE(fields.bytecode.length); - fields.bytecode.copy(lengthPrefixedBytecode, 4); - return [ - fields.success, - fields.returnData, - fields.gasUsed, - fields.endSideEffectCounter, - lengthPrefixedBytecode, - ] as const; + return [fields.success, fields.returnData, fields.gasUsed, fields.endSideEffectCounter, fields.contractAddress]; } /** @@ -171,7 +161,7 @@ export class AvmExternalCallHint { reader.readVector(Fr), reader.readObject(Gas), Fr.fromBuffer(reader), - reader.readBuffer(), + AztecAddress.fromBuffer(reader), ); } @@ -188,7 +178,7 @@ export class AvmExternalCallHint { export class AvmContractInstanceHint { constructor( public readonly address: Fr, - public readonly exists: Fr, + public readonly exists: boolean, public readonly salt: Fr, public readonly deployer: Fr, public readonly contractClassId: Fr, @@ -218,7 +208,7 @@ export class AvmContractInstanceHint { isEmpty(): boolean { return ( this.address.isZero() && - this.exists.isZero() && + !this.exists && this.salt.isZero() && this.deployer.isZero() && this.contractClassId.isZero() && @@ -262,7 +252,7 @@ export class AvmContractInstanceHint { const reader = BufferReader.asReader(buff); return new AvmContractInstanceHint( Fr.fromBuffer(reader), - Fr.fromBuffer(reader), + reader.readBoolean(), Fr.fromBuffer(reader), Fr.fromBuffer(reader), Fr.fromBuffer(reader), @@ -281,6 +271,99 @@ export class AvmContractInstanceHint { } } +export class AvmContractBytecodeHints { + /* + * @param bytecode currently the bytecode of the nested call function, will be changed to the contract bytecode (via the dispatch function) of the nested call + * @param contractInstance the contract instance of the nested call, used to derive the contract address + * @param contractClassPreimage the contract class preimage of the nested call, used to derive the class id + * */ + constructor( + public readonly bytecode: Buffer, + public contractInstanceHint: AvmContractInstanceHint, + public contractClassHint: ContractClassIdPreimage, + ) {} + /** + * Serializes the inputs to a buffer. + * @returns - The inputs serialized to a buffer. + */ + toBuffer() { + return serializeToBuffer(...AvmContractBytecodeHints.getFields(this)); + } + + /** + * Serializes the inputs to a hex string. + * @returns The instance serialized to a hex string. + */ + toString() { + return this.toBuffer().toString('hex'); + } + + /** + * Is the struct empty? + * @returns whether all members are empty. + */ + isEmpty(): boolean { + return this.bytecode.length == 0; + } + + /** + * Creates a new instance from fields. + * @param fields - Fields to create the instance from. + * @returns A new AvmHint instance. + */ + static from(fields: FieldsOf): AvmContractBytecodeHints { + return new AvmContractBytecodeHints(fields.bytecode, fields.contractInstanceHint, fields.contractClassHint); + } + + /** + * Extracts fields from an instance. + * @param fields - Fields to create the instance from. + * @returns An array of fields. + */ + static getFields(fields: FieldsOf) { + // Buffers aren't serialised the same way as they are read (lenth prefixed), so we need to do this manually. + const lengthPrefixedBytecode = Buffer.alloc(fields.bytecode.length + 4); + // Add a 4-byte length prefix to the bytecode. + lengthPrefixedBytecode.writeUInt32BE(fields.bytecode.length); + fields.bytecode.copy(lengthPrefixedBytecode, 4); + return [ + lengthPrefixedBytecode, + /* Contract Instance - exclude version */ + fields.contractInstanceHint, + /* Contract Class */ + fields.contractClassHint.artifactHash, + fields.contractClassHint.privateFunctionsRoot, + fields.contractClassHint.publicBytecodeCommitment, + ] as const; + } + + /** + * Deserializes from a buffer or reader. + * @param buffer - Buffer or reader to read from. + * @returns The deserialized instance. + */ + static fromBuffer(buff: Buffer | BufferReader): AvmContractBytecodeHints { + const reader = BufferReader.asReader(buff); + const bytecode = reader.readBuffer(); + const contractInstanceHint = AvmContractInstanceHint.fromBuffer(reader); + const contractClassHint = { + artifactHash: Fr.fromBuffer(reader), + privateFunctionsRoot: Fr.fromBuffer(reader), + publicBytecodeCommitment: Fr.fromBuffer(reader), + }; + return new AvmContractBytecodeHints(bytecode, contractInstanceHint, contractClassHint); + } + + /** + * Deserializes from a hex string. + * @param str - Hex string to read from. + * @returns The deserialized instance. + */ + static fromString(str: string): AvmContractBytecodeHints { + return AvmContractBytecodeHints.fromBuffer(Buffer.from(str, 'hex')); + } +} + // TODO(dbanks12): rename AvmCircuitHints export class AvmExecutionHints { public readonly storageValues: Vector; @@ -289,6 +372,7 @@ export class AvmExecutionHints { public readonly l1ToL2MessageExists: Vector; public readonly externalCalls: Vector; public readonly contractInstances: Vector; + public readonly contractBytecodeHints: Vector; constructor( storageValues: AvmKeyValueHint[], @@ -297,6 +381,7 @@ export class AvmExecutionHints { l1ToL2MessageExists: AvmKeyValueHint[], externalCalls: AvmExternalCallHint[], contractInstances: AvmContractInstanceHint[], + contractBytecodeHints: AvmContractBytecodeHints[], ) { this.storageValues = new Vector(storageValues); this.noteHashExists = new Vector(noteHashExists); @@ -304,6 +389,7 @@ export class AvmExecutionHints { this.l1ToL2MessageExists = new Vector(l1ToL2MessageExists); this.externalCalls = new Vector(externalCalls); this.contractInstances = new Vector(contractInstances); + this.contractBytecodeHints = new Vector(contractBytecodeHints); } /** @@ -311,7 +397,7 @@ export class AvmExecutionHints { * @returns an empty instance. */ empty() { - return new AvmExecutionHints([], [], [], [], [], []); + return new AvmExecutionHints([], [], [], [], [], [], []); } /** @@ -341,7 +427,8 @@ export class AvmExecutionHints { this.nullifierExists.items.length == 0 && this.l1ToL2MessageExists.items.length == 0 && this.externalCalls.items.length == 0 && - this.contractInstances.items.length == 0 + this.contractInstances.items.length == 0 && + this.contractBytecodeHints.items.length == 0 ); } @@ -358,6 +445,7 @@ export class AvmExecutionHints { fields.l1ToL2MessageExists.items, fields.externalCalls.items, fields.contractInstances.items, + fields.contractBytecodeHints.items, ); } @@ -374,6 +462,7 @@ export class AvmExecutionHints { fields.l1ToL2MessageExists, fields.externalCalls, fields.contractInstances, + fields.contractBytecodeHints, ] as const; } @@ -391,6 +480,7 @@ export class AvmExecutionHints { reader.readVector(AvmKeyValueHint), reader.readVector(AvmExternalCallHint), reader.readVector(AvmContractInstanceHint), + reader.readVector(AvmContractBytecodeHints), ); } @@ -408,14 +498,13 @@ export class AvmExecutionHints { * @returns The empty instance. */ static empty() { - return new AvmExecutionHints([], [], [], [], [], []); + return new AvmExecutionHints([], [], [], [], [], [], []); } } export class AvmCircuitInputs { constructor( public readonly functionName: string, // only informational - public readonly bytecode: Buffer, public readonly calldata: Fr[], public readonly publicInputs: PublicCircuitPublicInputs, public readonly avmHints: AvmExecutionHints, @@ -430,8 +519,6 @@ export class AvmCircuitInputs { return serializeToBuffer( functionNameBuffer.length, functionNameBuffer, - this.bytecode.length, - this.bytecode, this.calldata.length, this.calldata, this.publicInputs.toBuffer(), @@ -454,7 +541,6 @@ export class AvmCircuitInputs { isEmpty(): boolean { return ( this.functionName.length == 0 && - this.bytecode.length == 0 && this.calldata.length == 0 && this.publicInputs.isEmpty() && this.avmHints.isEmpty() @@ -476,7 +562,7 @@ export class AvmCircuitInputs { * @returns An array of fields. */ static getFields(fields: FieldsOf) { - return [fields.functionName, fields.bytecode, fields.calldata, fields.publicInputs, fields.avmHints] as const; + return [fields.functionName, fields.calldata, fields.publicInputs, fields.avmHints] as const; } /** @@ -488,7 +574,6 @@ export class AvmCircuitInputs { const reader = BufferReader.asReader(buff); return new AvmCircuitInputs( /*functionName=*/ reader.readBuffer().toString(), - /*bytecode=*/ reader.readBuffer(), /*calldata=*/ reader.readVector(Fr), PublicCircuitPublicInputs.fromBuffer(reader), AvmExecutionHints.fromBuffer(reader), diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index c8e16d52986..4a1c7ff4771 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -146,6 +146,7 @@ import { GasSettings } from '../structs/gas_settings.js'; import { GlobalVariables } from '../structs/global_variables.js'; import { Header } from '../structs/header.js'; import { + AvmContractBytecodeHints, EnqueuedCallData, PublicAccumulatedDataArrayLengths, PublicDataLeafHint, @@ -1376,10 +1377,22 @@ export function makeAvmExternalCallHint(seed = 0): AvmExternalCallHint { makeArray((seed % 100) + 10, i => new Fr(i), seed + 0x1000), new Gas(seed + 0x200, seed), new Fr(seed + 0x300), - makeBytes((seed % 100) + 10, seed + 0x400), + new Fr(seed + 0x400), ); } +export function makeAvmBytecodeHints(seed = 0): AvmContractBytecodeHints { + const instance = makeAvmContractInstanceHint(seed); + const { artifactHash, privateFunctionsRoot, packedBytecode } = makeContractClassPublic(seed); + const publicBytecodeCommitment = computePublicBytecodeCommitment(packedBytecode); + + return new AvmContractBytecodeHints(packedBytecode, instance, { + artifactHash, + privateFunctionsRoot, + publicBytecodeCommitment, + }); +} + /** * Makes arbitrary AvmContractInstanceHint. * @param seed - The seed to use for generating the state reference. @@ -1388,7 +1401,7 @@ export function makeAvmExternalCallHint(seed = 0): AvmExternalCallHint { export function makeAvmContractInstanceHint(seed = 0): AvmContractInstanceHint { return new AvmContractInstanceHint( new Fr(seed), - new Fr(seed + 0x1), + true /* exists */, new Fr(seed + 0x2), new Fr(seed + 0x3), new Fr(seed + 0x4), @@ -1422,6 +1435,7 @@ export function makeAvmExecutionHints( l1ToL2MessageExists: makeVector(baseLength + 3, makeAvmKeyValueHint, seed + 0x4500), externalCalls: makeVector(baseLength + 4, makeAvmExternalCallHint, seed + 0x4600), contractInstances: makeVector(baseLength + 5, makeAvmContractInstanceHint, seed + 0x4700), + contractBytecodeHints: makeVector(baseLength + 6, makeAvmBytecodeHints, seed + 0x4800), ...overrides, }); } @@ -1434,7 +1448,6 @@ export function makeAvmExecutionHints( export function makeAvmCircuitInputs(seed = 0, overrides: Partial> = {}): AvmCircuitInputs { return AvmCircuitInputs.from({ functionName: `function${seed}`, - bytecode: makeBytes((seed % 100) + 100, seed), calldata: makeArray((seed % 100) + 10, i => new Fr(i), seed + 0x1000), publicInputs: makePublicCircuitPublicInputs(seed + 0x2000), avmHints: makeAvmExecutionHints(seed + 0x3000), diff --git a/yarn-project/ivc-integration/src/avm_integration.test.ts b/yarn-project/ivc-integration/src/avm_integration.test.ts index 16f7388d036..4e1a8128f9e 100644 --- a/yarn-project/ivc-integration/src/avm_integration.test.ts +++ b/yarn-project/ivc-integration/src/avm_integration.test.ts @@ -14,6 +14,7 @@ import { AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS, PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH, } from '@aztec/circuits.js/constants'; +import { makeContractClassPublic } from '@aztec/circuits.js/testing'; import { Fr, Point } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; import { BufferReader } from '@aztec/foundation/serialize'; @@ -146,13 +147,13 @@ const proveAvmTestContract = async ( calldata: Fr[] = [], assertionErrString?: string, ): Promise => { + const worldStateDB = mock(); const startSideEffectCounter = 0; const functionSelector = getAvmTestContractFunctionSelector(functionName); calldata = [functionSelector.toField(), ...calldata]; const globals = GlobalVariables.empty(); const environment = initExecutionEnvironment({ functionSelector, calldata, globals }); - const worldStateDB = mock(); const contractInstance = new SerializableContractInstance({ version: 1, salt: new Fr(0x123), @@ -166,10 +167,13 @@ const proveAvmTestContract = async ( new Point(new Fr(0x313233), new Fr(0x343536), false), ), }).withAddress(environment.address); - worldStateDB.getContractInstance.mockResolvedValue(await Promise.resolve(contractInstance)); + worldStateDB.getContractInstance.mockResolvedValue(contractInstance); + + const contractClass = makeContractClassPublic(); + worldStateDB.getContractClass.mockResolvedValue(contractClass); const storageValue = new Fr(5); - worldStateDB.storageRead.mockResolvedValue(await Promise.resolve(storageValue)); + worldStateDB.storageRead.mockResolvedValue(storageValue); const trace = new PublicSideEffectTrace(startSideEffectCounter); const persistableState = initPersistableStateManager({ worldStateDB, trace }); @@ -181,12 +185,13 @@ const proveAvmTestContract = async ( // Use a simple contract that emits a side effect const bytecode = getAvmTestContractBytecode('public_dispatch'); + jest.spyOn(worldStateDB, 'getBytecode').mockResolvedValue(bytecode); // The paths for the barretenberg binary and the write path are hardcoded for now. const bbPath = path.resolve('../../barretenberg/cpp/build/bin/bb'); const bbWorkingDirectory = await fs.mkdtemp(path.join(tmpdir(), 'bb-')); // First we simulate (though it's not needed in this simple case). const simulator = new AvmSimulator(context); - const avmResult = await simulator.executeBytecode(bytecode); + const avmResult = await simulator.execute(); if (assertionErrString == undefined) { expect(avmResult.reverted).toBe(false); @@ -208,7 +213,6 @@ const proveAvmTestContract = async ( const avmCircuitInputs = new AvmCircuitInputs( functionName, - /*bytecode=*/ simulator.getBytecode()!, // uncompressed bytecode /*calldata=*/ context.environment.calldata, /*publicInputs=*/ getPublicInputs(pxResult), /*avmHints=*/ pxResult.avmCircuitHints, diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator.ts b/yarn-project/prover-client/src/orchestrator/orchestrator.ts index 27648fcca0b..bf453c98d0b 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator.ts @@ -1224,7 +1224,6 @@ export class ProvingOrchestrator implements EpochProver { async (signal: AbortSignal) => { const inputs: AvmCircuitInputs = new AvmCircuitInputs( publicFunction.vmRequest!.functionName, - publicFunction.vmRequest!.bytecode, publicFunction.vmRequest!.calldata, publicFunction.vmRequest!.kernelRequest.inputs.publicCall.publicInputs, publicFunction.vmRequest!.avmHints, diff --git a/yarn-project/simulator/src/avm/avm_simulator.test.ts b/yarn-project/simulator/src/avm/avm_simulator.test.ts index a4c39654a5a..1ddbc75beae 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.test.ts @@ -949,6 +949,7 @@ describe('AVM simulator: transpiled Noir contracts', () => { const callBytecode = getAvmTestContractBytecode('nested_call_to_assert_same'); const nestedBytecode = getAvmTestContractBytecode('public_dispatch'); mockGetBytecode(worldStateDB, nestedBytecode); + mockTraceFork(trace); const results = await new AvmSimulator(context).executeBytecode(callBytecode); expect(results.reverted).toBe(true); // The outer call should revert. diff --git a/yarn-project/simulator/src/avm/journal/journal.ts b/yarn-project/simulator/src/avm/journal/journal.ts index 3f6f115f5a0..ac4b488a87b 100644 --- a/yarn-project/simulator/src/avm/journal/journal.ts +++ b/yarn-project/simulator/src/avm/journal/journal.ts @@ -1,7 +1,15 @@ -import { AztecAddress, type FunctionSelector, type Gas, SerializableContractInstance } from '@aztec/circuits.js'; +import { + AztecAddress, + type FunctionSelector, + type Gas, + SerializableContractInstance, + computePublicBytecodeCommitment, +} from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; +import assert from 'assert'; + import { getPublicFunctionDebugName } from '../../common/debug_fn_name.js'; import { type WorldStateDB } from '../../public/public_db_sources.js'; import { type TracedContractInstance } from '../../public/side_effect_trace.js'; @@ -237,10 +245,42 @@ export class AvmPersistableStateManager { } /** - * Get a contract's bytecode from the contracts DB + * Get a contract's bytecode from the contracts DB, also trace the contract class and instance */ public async getBytecode(contractAddress: AztecAddress, selector: FunctionSelector): Promise { - return await this.worldStateDB.getBytecode(contractAddress, selector); + let exists = true; + // If the bytecode is not found, we let the executor decide that to do + const bytecode = await this.worldStateDB.getBytecode(contractAddress, selector); + let contractInstance = await this.worldStateDB.getContractInstance(contractAddress); + // If the contract instance is not found, we assume it has not be deployed. We will also be unable to find the + // contract class as we will not have the id. While the class might exist, we hopefully won't need it to generate a proof (tbd). + if (contractInstance === undefined) { + exists = false; + contractInstance = SerializableContractInstance.default().withAddress(contractAddress); + this.trace.traceGetBytecode( + bytecode ?? Buffer.alloc(1), + { exists, ...contractInstance }, + { + artifactHash: Fr.zero(), + privateFunctionsRoot: Fr.zero(), + publicBytecodeCommitment: Fr.zero(), + }, + ); + return bytecode; + } + const contractClass = await this.worldStateDB.getContractClass(contractInstance.contractClassId); + assert( + contractClass, + `Contract class not found in DB, but a contract instance was found with this class ID (${contractInstance.contractClassId}). This should not happen!`, + ); + const contractClassPreimage = { + artifactHash: contractClass.artifactHash, + privateFunctionsRoot: contractClass.privateFunctionsRoot, + publicBytecodeCommitment: computePublicBytecodeCommitment(contractClass.packedBytecode), + }; + this.trace.traceGetBytecode(bytecode!, { exists, ...contractInstance }, contractClassPreimage); + + return bytecode; } /** diff --git a/yarn-project/simulator/src/public/dual_side_effect_trace.ts b/yarn-project/simulator/src/public/dual_side_effect_trace.ts index f2c82196605..7067f2f0899 100644 --- a/yarn-project/simulator/src/public/dual_side_effect_trace.ts +++ b/yarn-project/simulator/src/public/dual_side_effect_trace.ts @@ -1,8 +1,9 @@ -import { - type CombinedConstantData, - type ContractInstanceWithAddress, - type Gas, - type VMCircuitPublicInputs, +import type { + CombinedConstantData, + ContractClassIdPreimage, + ContractInstanceWithAddress, + Gas, + VMCircuitPublicInputs, } from '@aztec/circuits.js'; import { type Fr } from '@aztec/foundation/fields'; @@ -32,6 +33,15 @@ export class DualSideEffectTrace implements PublicSideEffectTraceInterface { return this.innerCallTrace.getCounter(); } + public traceGetBytecode( + bytecode: Buffer, + contractInstance: TracedContractInstance, + contractClass: ContractClassIdPreimage, + ) { + this.innerCallTrace.traceGetBytecode(bytecode, contractInstance, contractClass); + this.enqueuedCallTrace.traceGetBytecode(bytecode, contractInstance, contractClass); + } + public tracePublicStorageRead(contractAddress: Fr, slot: Fr, value: Fr, exists: boolean, cached: boolean) { this.innerCallTrace.tracePublicStorageRead(contractAddress, slot, value, exists, cached); this.enqueuedCallTrace.tracePublicStorageRead(contractAddress, slot, value, exists, cached); diff --git a/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.test.ts b/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.test.ts index 38201e377dc..5bc2fc0f5ff 100644 --- a/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.test.ts +++ b/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.test.ts @@ -245,7 +245,7 @@ describe('Enqueued-call Side Effect Trace', () => { { // hint omits "version" and has "exists" as an Fr ...instanceWithoutVersion, - exists: new Fr(instance.exists), + exists: instance.exists, }, ]); }); diff --git a/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.ts b/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.ts index 516771d30f5..a22aa8e4ad5 100644 --- a/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.ts +++ b/yarn-project/simulator/src/public/enqueued_call_side_effect_trace.ts @@ -1,5 +1,6 @@ import { UnencryptedL2Log } from '@aztec/circuit-types'; import { + AvmContractBytecodeHints, AvmContractInstanceHint, AvmExecutionHints, AvmExternalCallHint, @@ -7,6 +8,7 @@ import { AztecAddress, CallContext, type CombinedConstantData, + type ContractClassIdPreimage, type ContractInstanceWithAddress, ContractStorageRead, ContractStorageUpdateRequest, @@ -157,6 +159,35 @@ export class PublicEnqueuedCallSideEffectTrace implements PublicSideEffectTraceI this.sideEffectCounter++; } + // This tracing function gets called everytime we start simulation/execution. + // This happens both when starting a new top-level trace and the start of every nested trace + // We use this to collect the AvmContractBytecodeHints + public traceGetBytecode( + bytecode: Buffer, + contractInstance: TracedContractInstance, + contractClass: ContractClassIdPreimage, + ) { + // Deduplicate - we might want a map here to make this more efficient + const idx = this.avmCircuitHints.contractBytecodeHints.items.findIndex( + hint => hint.contractInstanceHint.address === contractInstance.address, + ); + // If this is the first time we have seen the contract instance, add it to the hints + if (idx === -1) { + const instance = new AvmContractInstanceHint( + contractInstance.address, + contractInstance.exists, + contractInstance.salt, + contractInstance.deployer, + contractInstance.contractClassId, + contractInstance.initializationHash, + contractInstance.publicKeys, + ); + this.avmCircuitHints.contractBytecodeHints.items.push( + new AvmContractBytecodeHints(bytecode, instance, contractClass), + ); + } + } + public tracePublicStorageRead(contractAddress: Fr, slot: Fr, value: Fr, _exists: boolean, _cached: boolean) { // NOTE: exists and cached are unused for now but may be used for optimizations or kernel hints later if ( @@ -314,7 +345,7 @@ export class PublicEnqueuedCallSideEffectTrace implements PublicSideEffectTraceI this.avmCircuitHints.contractInstances.items.push( new AvmContractInstanceHint( instance.address, - new Fr(instance.exists ? 1 : 0), + instance.exists, instance.salt, instance.deployer, instance.contractClassId, @@ -334,13 +365,13 @@ export class PublicEnqueuedCallSideEffectTrace implements PublicSideEffectTraceI /** The trace of the nested call. */ nestedCallTrace: this, /** The execution environment of the nested call. */ - _nestedEnvironment: AvmExecutionEnvironment, + nestedEnvironment: AvmExecutionEnvironment, /** How much gas was available for this public execution. */ startGasLeft: Gas, /** How much gas was left after this public execution. */ endGasLeft: Gas, /** Bytecode used for this execution. */ - bytecode: Buffer, + _bytecode: Buffer, /** The call's results */ avmCallResults: AvmContractCallResult, /** Function name for logging */ @@ -368,7 +399,7 @@ export class PublicEnqueuedCallSideEffectTrace implements PublicSideEffectTraceI avmCallResults.output, gasUsed, endSideEffectCounter, - bytecode, + nestedEnvironment.address, ), ); } diff --git a/yarn-project/simulator/src/public/side_effect_trace.test.ts b/yarn-project/simulator/src/public/side_effect_trace.test.ts index 096f64d9f22..f24c04a9288 100644 --- a/yarn-project/simulator/src/public/side_effect_trace.test.ts +++ b/yarn-project/simulator/src/public/side_effect_trace.test.ts @@ -243,7 +243,7 @@ describe('Side Effect Trace', () => { { // hint omits "version" and has "exists" as an Fr ...instanceWithoutVersion, - exists: new Fr(instance.exists), + exists: instance.exists, }, ]); }); diff --git a/yarn-project/simulator/src/public/side_effect_trace.ts b/yarn-project/simulator/src/public/side_effect_trace.ts index 7b58742af8a..ccd353a58d8 100644 --- a/yarn-project/simulator/src/public/side_effect_trace.ts +++ b/yarn-project/simulator/src/public/side_effect_trace.ts @@ -1,11 +1,13 @@ import { PublicExecutionRequest, UnencryptedFunctionL2Logs, UnencryptedL2Log } from '@aztec/circuit-types'; import { + AvmContractBytecodeHints, AvmContractInstanceHint, AvmExecutionHints, AvmExternalCallHint, AvmKeyValueHint, AztecAddress, CallContext, + type ContractClassIdPreimage, type ContractInstanceWithAddress, ContractStorageRead, ContractStorageUpdateRequest, @@ -92,6 +94,36 @@ export class PublicSideEffectTrace implements PublicSideEffectTraceInterface { // TODO(dbanks12): checks against tx-wide limit need access to parent trace's length + // This tracing function gets called everytime we start simulation/execution. + // This happens both when starting a new top-level trace and the start of every nested trace + // We use this to collect the AvmContractBytecodeHints + public traceGetBytecode( + bytecode: Buffer, + contractInstance: TracedContractInstance, + contractClass: ContractClassIdPreimage, + ) { + // Deduplicate - we might want a map here to make this more efficient + const idx = this.avmCircuitHints.contractBytecodeHints.items.findIndex( + hint => hint.contractInstanceHint.address === contractInstance.address, + ); + // If this is the first time we have seen the contract instance, add it to the hints + if (idx === -1) { + const instance = new AvmContractInstanceHint( + contractInstance.address, + contractInstance.exists, + contractInstance.salt, + contractInstance.deployer, + contractInstance.contractClassId, + contractInstance.initializationHash, + contractInstance.publicKeys, + ); + this.avmCircuitHints.contractBytecodeHints.items.push( + new AvmContractBytecodeHints(bytecode, instance, contractClass), + ); + this.logger.debug(`Traced New Contract Bytecode deployed at ${contractInstance.address}`); + } + } + public tracePublicStorageRead(contractAddress: Fr, slot: Fr, value: Fr, _exists: boolean, _cached: boolean) { // NOTE: exists and cached are unused for now but may be used for optimizations or kernel hints later if (this.contractStorageReads.length >= MAX_PUBLIC_DATA_READS_PER_TX) { @@ -223,7 +255,7 @@ export class PublicSideEffectTrace implements PublicSideEffectTraceInterface { this.avmCircuitHints.contractInstances.items.push( new AvmContractInstanceHint( instance.address, - new Fr(instance.exists ? 1 : 0), + instance.exists, instance.salt, instance.deployer, instance.contractClassId, @@ -288,7 +320,7 @@ export class PublicSideEffectTrace implements PublicSideEffectTraceInterface { result.returnValues, gasUsed, result.endSideEffectCounter, - bytecode, + nestedEnvironment.address, ), ); } diff --git a/yarn-project/simulator/src/public/side_effect_trace_interface.ts b/yarn-project/simulator/src/public/side_effect_trace_interface.ts index e3f7b7c2ae2..7a5c44bd5fe 100644 --- a/yarn-project/simulator/src/public/side_effect_trace_interface.ts +++ b/yarn-project/simulator/src/public/side_effect_trace_interface.ts @@ -1,4 +1,4 @@ -import { type Gas } from '@aztec/circuits.js'; +import { type ContractClassIdPreimage, type Gas } from '@aztec/circuits.js'; import { type Fr } from '@aztec/foundation/fields'; import { type AvmContractCallResult } from '../avm/avm_contract_call_result.js'; @@ -9,6 +9,11 @@ export interface PublicSideEffectTraceInterface { fork(): PublicSideEffectTraceInterface; getCounter(): number; // all "trace*" functions can throw SideEffectLimitReachedError + traceGetBytecode( + bytecode: Buffer, + contractInstance: TracedContractInstance, + contractClass: ContractClassIdPreimage, + ): void; tracePublicStorageRead(contractAddress: Fr, slot: Fr, value: Fr, exists: boolean, cached: boolean): void; tracePublicStorageWrite(contractAddress: Fr, slot: Fr, value: Fr): void; traceNoteHashCheck(contractAddress: Fr, noteHash: Fr, leafIndex: Fr, exists: boolean): void;