Skip to content

Commit 84205d8

Browse files
authored
feat(avm): trace contract class and contract instance (#8840)
This PR is centred around tracing and passing contract class & instance during simulator execution and passing it to circuit. We store each contract class & instance whenever the `simulator` calls `getBytecode`. This changes the input interface to the bb binary - we no longer take in a specific bytecode to execute. Instead we get a vector of `{contract_class, contract_instance, bytecode}` which define all the (deduplicated) contract bytecode that will be executed during this "one-enqueued call" (actual implementation of 1-enqueued call tbd). This doesnt do any derivation of id or address yet
1 parent 2bb09e5 commit 84205d8

File tree

24 files changed

+460
-177
lines changed

24 files changed

+460
-177
lines changed

barretenberg/cpp/src/barretenberg/bb/main.cpp

+5-7
Original file line numberDiff line numberDiff line change
@@ -953,18 +953,17 @@ void vk_as_fields(const std::string& vk_path, const std::string& output_path)
953953
* @param hints_path Path to the file containing the serialised avm circuit hints
954954
* @param output_path Path (directory) to write the output proof and verification keys
955955
*/
956-
void avm_prove(const std::filesystem::path& bytecode_path,
957-
const std::filesystem::path& calldata_path,
956+
void avm_prove(const std::filesystem::path& calldata_path,
958957
const std::filesystem::path& public_inputs_path,
959958
const std::filesystem::path& hints_path,
960959
const std::filesystem::path& output_path)
961960
{
962-
std::vector<uint8_t> const bytecode = read_file(bytecode_path);
963961
std::vector<fr> const calldata = many_from_buffer<fr>(read_file(calldata_path));
964962
std::vector<fr> const public_inputs_vec = many_from_buffer<fr>(read_file(public_inputs_path));
965963
auto const avm_hints = bb::avm_trace::ExecutionHints::from(read_file(hints_path));
966964

967-
vinfo("bytecode size: ", bytecode.size());
965+
// Using [0] is fine now for the top-level call, but we might need to index by address in future
966+
vinfo("bytecode size: ", avm_hints.all_contract_bytecode[0].bytecode.size());
968967
vinfo("calldata size: ", calldata.size());
969968
vinfo("public_inputs size: ", public_inputs_vec.size());
970969
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,
979978

980979
// Prove execution and return vk
981980
auto const [verification_key, proof] =
982-
AVM_TRACK_TIME_V("prove/all", avm_trace::Execution::prove(bytecode, calldata, public_inputs_vec, avm_hints));
981+
AVM_TRACK_TIME_V("prove/all", avm_trace::Execution::prove(calldata, public_inputs_vec, avm_hints));
983982

984983
std::vector<fr> vk_as_fields = verification_key.to_field_elements();
985984

@@ -1526,7 +1525,6 @@ int main(int argc, char* argv[])
15261525
write_recursion_inputs_honk<UltraFlavor>(bytecode_path, witness_path, output_path);
15271526
#ifndef DISABLE_AZTEC_VM
15281527
} else if (command == "avm_prove") {
1529-
std::filesystem::path avm_bytecode_path = get_option(args, "--avm-bytecode", "./target/avm_bytecode.bin");
15301528
std::filesystem::path avm_calldata_path = get_option(args, "--avm-calldata", "./target/avm_calldata.bin");
15311529
std::filesystem::path avm_public_inputs_path =
15321530
get_option(args, "--avm-public-inputs", "./target/avm_public_inputs.bin");
@@ -1535,7 +1533,7 @@ int main(int argc, char* argv[])
15351533
std::filesystem::path output_path = get_option(args, "-o", "./proofs");
15361534
extern std::filesystem::path avm_dump_trace_path;
15371535
avm_dump_trace_path = get_option(args, "--avm-dump-trace", "");
1538-
avm_prove(avm_bytecode_path, avm_calldata_path, avm_public_inputs_path, avm_hints_path, output_path);
1536+
avm_prove(avm_calldata_path, avm_public_inputs_path, avm_hints_path, output_path);
15391537
} else if (command == "avm_verify") {
15401538
return avm_verify(proof_path, vk_path) ? 0 : 1;
15411539
#endif

barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp

+68-46
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,9 @@ class AvmExecutionTests : public ::testing::Test {
3636
Execution::set_trace_builder_constructor([](VmPublicInputsNT public_inputs,
3737
ExecutionHints execution_hints,
3838
uint32_t side_effect_counter,
39-
std::vector<FF> calldata,
40-
const std::vector<std::vector<uint8_t>>& all_contracts_bytecode) {
41-
return AvmTraceBuilder(std::move(public_inputs),
42-
std::move(execution_hints),
43-
side_effect_counter,
44-
std::move(calldata),
45-
all_contracts_bytecode)
39+
std::vector<FF> calldata) {
40+
return AvmTraceBuilder(
41+
std::move(public_inputs), std::move(execution_hints), side_effect_counter, std::move(calldata))
4642
.set_full_precomputed_tables(false)
4743
.set_range_check_required(false);
4844
});
@@ -57,7 +53,8 @@ class AvmExecutionTests : public ::testing::Test {
5753
srs::init_crs_factory("../srs_db/ignition");
5854
public_inputs_vec.at(DA_START_GAS_LEFT_PCPI_OFFSET) = DEFAULT_INITIAL_DA_GAS;
5955
public_inputs_vec.at(L2_START_GAS_LEFT_PCPI_OFFSET) = DEFAULT_INITIAL_L2_GAS;
60-
public_inputs = convert_public_inputs(public_inputs_vec);
56+
public_inputs_vec.at(ADDRESS_KERNEL_INPUTS_COL_OFFSET) = 0xdeadbeef;
57+
public_inputs = avm_trace::convert_public_inputs(public_inputs_vec);
6158
};
6259

6360
/**
@@ -71,7 +68,21 @@ class AvmExecutionTests : public ::testing::Test {
7168
std::vector<FF> calldata{};
7269
std::vector<FF> returndata{};
7370

74-
return Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints());
71+
auto execution_hints = ExecutionHints().with_avm_contract_bytecode({ bytecode });
72+
execution_hints.all_contract_bytecode[0].contract_instance.address = 0xdeadbeef;
73+
74+
return AvmExecutionTests::gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
75+
}
76+
77+
std::vector<Row> gen_trace(std::vector<uint8_t> bytecode,
78+
std::vector<FF> const& calldata,
79+
std::vector<FF> const& public_inputs_vec,
80+
std::vector<FF>& returndata,
81+
ExecutionHints& execution_hints) const
82+
{
83+
execution_hints.all_contract_bytecode = { bytecode };
84+
execution_hints.all_contract_bytecode[0].contract_instance.address = 0xdeadbeef;
85+
return Execution::gen_trace(calldata, public_inputs_vec, returndata, execution_hints);
7586
}
7687

7788
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)
474485
Field(&Instruction::operands, ElementsAre(VariantWith<uint16_t>(5)))));
475486

476487
std::vector<FF> returndata;
477-
auto trace =
478-
Execution::gen_trace(bytecode, std::vector<FF>{ 13, 156 }, public_inputs_vec, returndata, ExecutionHints());
488+
ExecutionHints execution_hints;
489+
auto trace = gen_trace(bytecode, std::vector<FF>{ 13, 156 }, public_inputs_vec, returndata, execution_hints);
479490

480491
// Expected sequence of PCs during execution
481492
std::vector<FF> pc_sequence{
@@ -564,10 +575,9 @@ TEST_F(AvmExecutionTests, jumpiAndCalldatacopy)
564575
ElementsAre(VariantWith<uint8_t>(0), VariantWith<uint16_t>(6), VariantWith<uint16_t>(10)))));
565576

566577
std::vector<FF> returndata;
567-
auto trace_jump =
568-
Execution::gen_trace(bytecode, std::vector<FF>{ 9873123 }, public_inputs_vec, returndata, ExecutionHints());
569-
auto trace_no_jump =
570-
Execution::gen_trace(bytecode, std::vector<FF>{ 0 }, public_inputs_vec, returndata, ExecutionHints());
578+
ExecutionHints execution_hints;
579+
auto trace_jump = gen_trace(bytecode, std::vector<FF>{ 9873123 }, public_inputs_vec, returndata, execution_hints);
580+
auto trace_no_jump = gen_trace(bytecode, std::vector<FF>{ 0 }, public_inputs_vec, returndata, execution_hints);
571581

572582
// Expected sequence of PCs during execution with jump
573583
std::vector<FF> pc_sequence_jump{ 0, 1, 2, 3, 4, 6, 7 };
@@ -774,8 +784,9 @@ TEST_F(AvmExecutionTests, toRadixLeOpcode)
774784

775785
// Assign a vector that we will mutate internally in gen_trace to store the return values;
776786
std::vector<FF> returndata;
777-
auto trace = Execution::gen_trace(
778-
bytecode, std::vector<FF>{ FF::modulus - FF(1) }, public_inputs_vec, returndata, ExecutionHints());
787+
ExecutionHints execution_hints;
788+
auto trace =
789+
gen_trace(bytecode, std::vector<FF>{ FF::modulus - FF(1) }, public_inputs_vec, returndata, execution_hints);
779790

780791
// Find the first row enabling the TORADIXLE selector
781792
// 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)
840851

841852
// Assign a vector that we will mutate internally in gen_trace to store the return values;
842853
std::vector<FF> returndata;
843-
auto trace = Execution::gen_trace(
844-
bytecode, std::vector<FF>{ FF::modulus - FF(1) }, public_inputs_vec, returndata, ExecutionHints());
854+
ExecutionHints execution_hints;
855+
auto trace =
856+
gen_trace(bytecode, std::vector<FF>{ FF::modulus - FF(1) }, public_inputs_vec, returndata, execution_hints);
845857

846858
// Find the first row enabling the TORADIXLE selector
847859
// 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)
915927
// 4091010797,3974542186]),
916928
std::vector<FF> expected_output = { 1862536192, 526086805, 2067405084, 593147560,
917929
726610467, 813867028, 4091010797ULL, 3974542186ULL };
918-
auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints());
930+
ExecutionHints execution_hints;
931+
auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
919932

920933
EXPECT_EQ(returndata, expected_output);
921934

@@ -976,7 +989,8 @@ TEST_F(AvmExecutionTests, poseidon2PermutationOpCode)
976989
FF(std::string("0x018555a8eb50cf07f64b019ebaf3af3c925c93e631f3ecd455db07bbb52bbdd3")),
977990
FF(std::string("0x0cbea457c91c22c6c31fd89afd2541efc2edf31736b9f721e823b2165c90fd41"))
978991
};
979-
auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints());
992+
ExecutionHints execution_hints;
993+
auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
980994

981995
EXPECT_EQ(returndata, expected_output);
982996

@@ -1043,7 +1057,8 @@ TEST_F(AvmExecutionTests, keccakf1600OpCode)
10431057
// Assign a vector that we will mutate internally in gen_trace to store the return values;
10441058
std::vector<FF> calldata = std::vector<FF>();
10451059
std::vector<FF> returndata = std::vector<FF>();
1046-
auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints());
1060+
ExecutionHints execution_hints;
1061+
auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
10471062

10481063
EXPECT_EQ(returndata, expected_output);
10491064

@@ -1110,7 +1125,8 @@ TEST_F(AvmExecutionTests, embeddedCurveAddOpCode)
11101125
// Assign a vector that we will mutate internally in gen_trace to store the return values;
11111126
std::vector<FF> returndata;
11121127
std::vector<FF> calldata = { a.x, a.y, FF(a_is_inf ? 1 : 0), b.x, b.y, FF(b_is_inf ? 1 : 0) };
1113-
auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints());
1128+
ExecutionHints execution_hints;
1129+
auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
11141130

11151131
EXPECT_EQ(returndata, expected_output);
11161132

@@ -1197,7 +1213,8 @@ TEST_F(AvmExecutionTests, msmOpCode)
11971213

11981214
// Assign a vector that we will mutate internally in gen_trace to store the return values;
11991215
std::vector<FF> returndata;
1200-
auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints());
1216+
ExecutionHints execution_hints;
1217+
auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
12011218

12021219
EXPECT_EQ(returndata, expected_output);
12031220

@@ -1355,16 +1372,16 @@ TEST_F(AvmExecutionTests, kernelInputOpcodes)
13551372
std::vector<FF> calldata;
13561373

13571374
FF sender = 1;
1358-
FF address = 2;
1359-
FF function_selector = 3;
1360-
FF transaction_fee = 4;
1361-
FF chainid = 5;
1362-
FF version = 6;
1363-
FF blocknumber = 7;
1364-
FF timestamp = 8;
1365-
FF feeperl2gas = 9;
1366-
FF feeperdagas = 10;
1367-
FF is_static_call = 11;
1375+
FF address = 0xdeadbeef;
1376+
FF function_selector = 4;
1377+
FF transaction_fee = 5;
1378+
FF chainid = 6;
1379+
FF version = 7;
1380+
FF blocknumber = 8;
1381+
FF timestamp = 9;
1382+
FF feeperl2gas = 10;
1383+
FF feeperdagas = 11;
1384+
FF is_static_call = 12;
13681385

13691386
// The return data for this test should be a the opcodes in sequence, as the opcodes dst address lines up with
13701387
// this array The returndata call above will then return this array
@@ -1393,7 +1410,8 @@ TEST_F(AvmExecutionTests, kernelInputOpcodes)
13931410
public_inputs_vec[FEE_PER_L2_GAS_PCPI_OFFSET] = feeperl2gas;
13941411

13951412
std::vector<FF> returndata;
1396-
auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints());
1413+
ExecutionHints execution_hints;
1414+
auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
13971415

13981416
// Validate returndata
13991417
EXPECT_EQ(returndata, expected_returndata);
@@ -1559,7 +1577,8 @@ TEST_F(AvmExecutionTests, ExecutorThrowsWithTooMuchGasAllocated)
15591577
auto bytecode = hex_to_bytes(bytecode_hex);
15601578
auto instructions = Deserialization::parse(bytecode);
15611579

1562-
EXPECT_THROW_WITH_MESSAGE(Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints()),
1580+
ExecutionHints execution_hints;
1581+
EXPECT_THROW_WITH_MESSAGE(gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints),
15631582
"Cannot allocate more than MAX_L2_GAS_PER_ENQUEUED_CALL to the AVM for "
15641583
"execution of an enqueued call");
15651584
}
@@ -1578,7 +1597,8 @@ TEST_F(AvmExecutionTests, ExecutorThrowsWithIncorrectNumberOfPublicInputs)
15781597
auto bytecode = hex_to_bytes(bytecode_hex);
15791598
auto instructions = Deserialization::parse(bytecode);
15801599

1581-
EXPECT_THROW_WITH_MESSAGE(Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints()),
1600+
ExecutionHints execution_hints;
1601+
EXPECT_THROW_WITH_MESSAGE(gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints),
15821602
"Public inputs vector is not of PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH");
15831603
}
15841604

@@ -1621,7 +1641,8 @@ TEST_F(AvmExecutionTests, kernelOutputEmitOpcodes)
16211641

16221642
std::vector<FF> calldata = {};
16231643
std::vector<FF> returndata = {};
1624-
auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints());
1644+
ExecutionHints execution_hints;
1645+
auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
16251646

16261647
// CHECK EMIT NOTE HASH
16271648
// Check output data + side effect counters have been set correctly
@@ -1656,7 +1677,7 @@ TEST_F(AvmExecutionTests, kernelOutputEmitOpcodes)
16561677
auto emit_log_row =
16571678
std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.main_sel_op_emit_unencrypted_log == 1; });
16581679
// Trust me bro for now, this is the truncated sha output
1659-
FF expected_hash = FF(std::string("0x006db65fd59fd356f6729140571b5bcd6bb3b83492a16e1bf0a3884442fc3c8a"));
1680+
FF expected_hash = FF(std::string("0x003383cbb254941b33c0aaf8476c4b9b532d70a2fb105ee908dd332f7d942df6"));
16601681
EXPECT_EQ(emit_log_row->main_ia, expected_hash);
16611682
EXPECT_EQ(emit_log_row->main_side_effect_counter, 2);
16621683
// Value is 40 = 32 * log_length + 40 (and log_length is 0 in this case).
@@ -1722,7 +1743,7 @@ TEST_F(AvmExecutionTests, kernelOutputStorageLoadOpcodeSimple)
17221743
// side effect counter 0 = value 42
17231744
auto execution_hints = ExecutionHints().with_storage_value_hints({ { 0, 42 } });
17241745

1725-
auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
1746+
auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
17261747

17271748
// CHECK SLOAD
17281749
// Check output data + side effect counters have been set correctly
@@ -1776,7 +1797,8 @@ TEST_F(AvmExecutionTests, kernelOutputStorageStoreOpcodeSimple)
17761797

17771798
std::vector<FF> returndata;
17781799

1779-
auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, ExecutionHints());
1800+
ExecutionHints execution_hints;
1801+
auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
17801802
// CHECK SSTORE
17811803
auto sstore_row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.main_sel_op_sstore == 1; });
17821804
EXPECT_EQ(sstore_row->main_ia, 42); // Read value
@@ -1839,7 +1861,7 @@ TEST_F(AvmExecutionTests, kernelOutputStorageOpcodes)
18391861
// side effect counter 0 = value 42
18401862
auto execution_hints = ExecutionHints().with_storage_value_hints({ { 0, 42 } });
18411863

1842-
auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
1864+
auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
18431865

18441866
// CHECK SLOAD
18451867
// Check output data + side effect counters have been set correctly
@@ -1922,7 +1944,7 @@ TEST_F(AvmExecutionTests, kernelOutputHashExistsOpcodes)
19221944
.with_storage_value_hints({ { 0, 1 }, { 1, 1 }, { 2, 1 } })
19231945
.with_note_hash_exists_hints({ { 0, 1 }, { 1, 1 }, { 2, 1 } });
19241946

1925-
auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
1947+
auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
19261948

19271949
// CHECK NOTEHASHEXISTS
19281950
auto note_hash_row =
@@ -2056,10 +2078,10 @@ TEST_F(AvmExecutionTests, opCallOpcodes)
20562078
.l2_gas_used = 0,
20572079
.da_gas_used = 0,
20582080
.end_side_effect_counter = 0,
2059-
.bytecode = {},
2081+
.contract_address = 0,
20602082
} });
20612083

2062-
auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
2084+
auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
20632085
EXPECT_EQ(returndata, std::vector<FF>({ 9, 8, 1 })); // The 1 represents the success
20642086

20652087
validate_trace(std::move(trace), public_inputs, calldata, returndata);
@@ -2116,7 +2138,7 @@ TEST_F(AvmExecutionTests, opGetContractInstanceOpcodes)
21162138
auto execution_hints =
21172139
ExecutionHints().with_contract_instance_hints({ { address, { address, 1, 2, 3, 4, 5, public_keys_hints } } });
21182140

2119-
auto trace = Execution::gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
2141+
auto trace = gen_trace(bytecode, calldata, public_inputs_vec, returndata, execution_hints);
21202142
EXPECT_EQ(returndata, std::vector<FF>({ 1, 2, 3, 4, 5, returned_point.x })); // The first one represents true
21212143

21222144
validate_trace(std::move(trace), public_inputs, calldata, returndata);

barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace bb::avm_trace {
66
using poseidon2 = crypto::Poseidon2<crypto::Poseidon2Bn254ScalarFieldParams>;
7-
AvmBytecodeTraceBuilder::AvmBytecodeTraceBuilder(const std::vector<std::vector<uint8_t>>& all_contracts_bytecode)
7+
AvmBytecodeTraceBuilder::AvmBytecodeTraceBuilder(const std::vector<AvmContractBytecode>& all_contracts_bytecode)
88
: all_contracts_bytecode(all_contracts_bytecode)
99
{}
1010

@@ -31,7 +31,7 @@ void AvmBytecodeTraceBuilder::build_bytecode_columns()
3131
// This is the main loop that will generate the bytecode trace
3232
for (auto& contract_bytecode : all_contracts_bytecode) {
3333
FF running_hash = FF::zero();
34-
auto packed_bytecode = pack_bytecode(contract_bytecode);
34+
auto packed_bytecode = pack_bytecode(contract_bytecode.bytecode);
3535
// This size is already based on the number of fields
3636
for (size_t i = 0; i < packed_bytecode.size(); ++i) {
3737
bytecode_trace.push_back(BytecodeTraceEntry{

0 commit comments

Comments
 (0)