Skip to content

Commit 2258dbc

Browse files
authored
feat(avm_fuzzing): external calls (#19055)
1) avm.fuzzer.cpp -> avm_differential.fuzzer.cpp 2) Added singletone-proxy class to ContractDB to register contracts 3) Implemented CALL/STATICALL RETURNDATACOPY RETURNDATASIZE More predefined functions and tests will be in followups
1 parent 778825d commit 2258dbc

File tree

15 files changed

+514
-124
lines changed

15 files changed

+514
-124
lines changed

barretenberg/cpp/src/barretenberg/avm_fuzzer/avm.fuzzer.cpp renamed to barretenberg/cpp/src/barretenberg/avm_fuzzer/avm_differential.fuzzer.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include <vector>
66

77
#include "barretenberg/avm_fuzzer/common/interfaces/dbs.hpp"
8+
#include "barretenberg/avm_fuzzer/fuzz_lib/constants.hpp"
9+
#include "barretenberg/avm_fuzzer/fuzz_lib/contract_db_proxy.hpp"
810
#include "barretenberg/avm_fuzzer/fuzz_lib/control_flow.hpp"
911
#include "barretenberg/avm_fuzzer/fuzz_lib/fuzz.hpp"
1012
#include "barretenberg/avm_fuzzer/fuzz_lib/fuzzer_data.hpp"
@@ -40,7 +42,7 @@ SimulatorResult fuzz(const uint8_t* buffer, size_t size)
4042

4143
FuzzerWorldStateManager* ws_mgr = FuzzerWorldStateManager::getInstance();
4244
ws_mgr->fork();
43-
auto res = fuzz(deserialized_data);
45+
auto res = fuzz_against_ts_simulator(deserialized_data);
4446
ws_mgr->reset_world_state();
4547

4648
return res;

barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,11 +237,13 @@ void FuzzerContractDB::add_contracts(const ContractDeploymentData& contract_depl
237237
void FuzzerContractDB::add_contract_class(const ContractClassId& class_id, const ContractClass& contract_class)
238238
{
239239
contract_classes[class_id] = contract_class;
240+
contract_classes_vector.push_back(contract_class);
240241
}
241242

242243
void FuzzerContractDB::add_contract_instance(const AztecAddress& address, const ContractInstance& contract_instance)
243244
{
244245
contract_instances[address] = contract_instance;
246+
contract_instances_vector.push_back({ address, contract_instance });
245247
}
246248

247249
// Based on fromLogs in yarn-project/protocol-contracts/src/class-registry/contract_class_published_event.ts

barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.hpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,10 @@ class FuzzerContractDB : public simulation::ContractDBInterface {
8383
void revert_checkpoint() override;
8484

8585
// Getters for serialization
86-
const std::unordered_map<ContractClassId, ContractClass>& get_contract_classes() const { return contract_classes; }
87-
const std::unordered_map<AztecAddress, ContractInstance>& get_contract_instances() const
86+
const std::vector<ContractClass>& get_contract_classes() const { return contract_classes_vector; }
87+
const std::vector<std::pair<AztecAddress, ContractInstance>>& get_contract_instances() const
8888
{
89-
return contract_instances;
89+
return contract_instances_vector;
9090
}
9191

9292
private:
@@ -96,6 +96,10 @@ class FuzzerContractDB : public simulation::ContractDBInterface {
9696
std::unordered_map<ContractClassId, ContractClass> contract_classes;
9797
std::unordered_map<AztecAddress, ContractInstance> contract_instances;
9898

99+
// Used for serialization keeping track of the order of the contracts and instances
100+
std::vector<ContractClass> contract_classes_vector;
101+
std::vector<std::pair<AztecAddress, ContractInstance>> contract_instances_vector;
102+
99103
struct Checkpoint {
100104
std::unordered_map<ContractClassId, ContractClass> contract_classes;
101105
std::unordered_map<AztecAddress, ContractInstance> contract_instances;

barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/constants.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,10 @@ inline void fuzz_info_(std::function<std::string()> func)
4949
}
5050

5151
#define fuzz_info(...) fuzz_info_([&]() { return format(__VA_ARGS__); })
52+
53+
// =========== Pre defined functions ===========
54+
// Taken from ADD_8 test in fuzz.test.cpp
55+
const std::vector<uint8_t> ADD_8_BYTECODE = { 39, 0, 0, 2, 5, 39, 0, 1, 2, 2, 0, 0, 0, 1,
56+
2, 40, 0, 0, 5, 4, 0, 1, 59, 0, 0, 5, 0, 2 };
57+
58+
const std::vector<std::vector<uint8_t>> PREDEFINED_FUNCTIONS = { ADD_8_BYTECODE };
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#include "barretenberg/avm_fuzzer/fuzz_lib/contract_db_proxy.hpp"
2+
3+
#include "barretenberg/avm_fuzzer/common/interfaces/dbs.hpp"
4+
#include "barretenberg/avm_fuzzer/fuzz_lib/constants.hpp"
5+
#include "barretenberg/avm_fuzzer/fuzz_lib/control_flow.hpp"
6+
#include "barretenberg/avm_fuzzer/fuzz_lib/fuzzer_data.hpp"
7+
#include "barretenberg/avm_fuzzer/fuzz_lib/simulator.hpp"
8+
#include "barretenberg/common/log.hpp"
9+
#include "barretenberg/vm2/simulation/lib/contract_crypto.hpp"
10+
11+
using namespace bb::avm2::fuzzer;
12+
13+
// Temp Helper function to create a default contract class from bytecode
14+
ContractClass create_default_class(const std::vector<uint8_t>& bytecode)
15+
{
16+
// This isn't strictly needed for pure simulation, but if we want to re-use inputs in proving we need valid
17+
// commitment
18+
auto bytecode_commitment = simulation::compute_public_bytecode_commitment(bytecode);
19+
auto class_id =
20+
simulation::compute_contract_class_id(/*artifact_hash=*/0, /*private_fn_root=*/0, bytecode_commitment);
21+
return ContractClass{
22+
.id = class_id,
23+
.artifact_hash = 0,
24+
.private_functions_root = 0,
25+
.packed_bytecode = bytecode,
26+
};
27+
}
28+
29+
// Temp Helper function to create a default contract instance from a class ID
30+
ContractInstance create_default_instance(const ContractClassId& class_id)
31+
{
32+
return ContractInstance{
33+
.salt = 0,
34+
.deployer = MSG_SENDER,
35+
.current_contract_class_id = class_id,
36+
.original_contract_class_id = class_id,
37+
.initialization_hash = 0,
38+
.public_keys = PublicKeys{},
39+
};
40+
}
41+
42+
// Temp Helper function to compute contract address from instance
43+
AztecAddress compute_contract_address(const ContractInstance& instance)
44+
{
45+
// This isn't strictly needed for pure simulation, but if we want to re-use inputs in proving we need valid
46+
// addresses
47+
return simulation::compute_contract_address(instance);
48+
}
49+
50+
// Creates a default transaction that the single app logic enqueued call can be inserted into
51+
Tx create_default_tx(const AztecAddress& contract_address,
52+
const AztecAddress& sender_address,
53+
const std::vector<FF>& calldata,
54+
[[maybe_unused]] const FF& transaction_fee,
55+
bool is_static_call,
56+
const Gas& gas_limit)
57+
{
58+
return Tx{
59+
.hash = TRANSACTION_HASH,
60+
.gas_settings = GasSettings{
61+
.gas_limits = gas_limit,
62+
.max_fees_per_gas = GasFees{ .fee_per_da_gas = FEE_PER_DA_GAS, .fee_per_l2_gas = FEE_PER_L2_GAS },
63+
},
64+
.effective_gas_fees = EFFECTIVE_GAS_FEES,
65+
.non_revertible_accumulated_data = AccumulatedData{
66+
.note_hashes = NON_REVERTIBLE_ACCUMULATED_DATA_NOTE_HASHES,
67+
// This nullifier is needed to make the nonces for note hashes and expected by simulation_helper
68+
.nullifiers = NON_REVERTIBLE_ACCUMULATED_DATA_NULLIFIERS,
69+
.l2_to_l1_messages = NON_REVERTIBLE_ACCUMULATED_DATA_L2_TO_L1_MESSAGES,
70+
},
71+
.revertible_accumulated_data = AccumulatedData{
72+
.note_hashes = REVERTIBLE_ACCUMULATED_DATA_NOTE_HASHES,
73+
.nullifiers = REVERTIBLE_ACCUMULATED_DATA_NULLIFIERS,
74+
.l2_to_l1_messages = REVERTIBLE_ACCUMULATED_DATA_L2_TO_L1_MESSAGES,
75+
},
76+
.setup_enqueued_calls = SETUP_ENQUEUED_CALLS,
77+
.app_logic_enqueued_calls = {
78+
PublicCallRequestWithCalldata{
79+
.request = PublicCallRequest{
80+
.msg_sender = MSG_SENDER,
81+
.contract_address = contract_address,
82+
.is_static_call = is_static_call,
83+
.calldata_hash = 0,
84+
},
85+
.calldata = calldata,
86+
},
87+
},
88+
.teardown_enqueued_call = TEARDOWN_ENQUEUED_CALLS,
89+
.gas_used_by_private = GAS_USED_BY_PRIVATE,
90+
.fee_payer = sender_address,
91+
};
92+
}
93+
94+
namespace bb::avm2::fuzzer {
95+
96+
ContractDBProxy* ContractDBProxy::instance = nullptr;
97+
98+
ContractDBProxy::ContractDBProxy()
99+
{
100+
contract_db = new FuzzerContractDB();
101+
}
102+
103+
ContractDBProxy* ContractDBProxy::get_instance()
104+
{
105+
if (instance == nullptr) {
106+
instance = new ContractDBProxy();
107+
}
108+
return instance;
109+
}
110+
111+
FF ContractDBProxy::register_contract_from_bytecode(const std::vector<uint8_t>& bytecode)
112+
{
113+
if (instance == nullptr) {
114+
instance = new ContractDBProxy();
115+
}
116+
auto default_class = create_default_class(bytecode);
117+
auto default_instance = create_default_instance(default_class.id);
118+
auto contract_address = simulation::compute_contract_address(default_instance);
119+
instance->contract_db->add_contract_class(default_class.id, default_class);
120+
instance->contract_db->add_contract_instance(contract_address, default_instance);
121+
instance->registered_contract_addresses.push_back(contract_address);
122+
123+
FuzzerWorldStateManager::getInstance()->register_contract_address(contract_address);
124+
return contract_address;
125+
}
126+
127+
FF ContractDBProxy::get_function_address(size_t index)
128+
{
129+
if (instance == nullptr) {
130+
instance = new ContractDBProxy();
131+
}
132+
if (instance->registered_contract_addresses.size() < 1) {
133+
return FF::zero();
134+
}
135+
return instance->registered_contract_addresses[index % (instance->registered_contract_addresses.size())];
136+
}
137+
138+
void ContractDBProxy::reset_instance()
139+
{
140+
if (instance != nullptr) {
141+
delete instance;
142+
instance = new ContractDBProxy();
143+
}
144+
}
145+
} // namespace bb::avm2::fuzzer
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/// Singleton proxy class for FuzzerContractDB
2+
#pragma once
3+
4+
#include "barretenberg/avm_fuzzer/common/interfaces/dbs.hpp"
5+
#include "barretenberg/vm2/common/field.hpp"
6+
7+
namespace bb::avm2::fuzzer {
8+
9+
class ContractDBProxy {
10+
private:
11+
static ContractDBProxy* instance;
12+
ContractDBProxy();
13+
14+
FuzzerContractDB* contract_db;
15+
std::vector<FF> registered_contract_addresses;
16+
17+
public:
18+
static ContractDBProxy* get_instance();
19+
20+
/// @brief Register a contract from its bytecode
21+
/// @param bytecode The bytecode of the contract
22+
/// @return The address of the registered contract
23+
/// @note This function will also register the contract address in the world state
24+
/// Adds the contract address to the registered_contract_addresses vector
25+
static FF register_contract_from_bytecode(const std::vector<uint8_t>& bytecode);
26+
27+
static void reset_instance();
28+
29+
FuzzerContractDB* get_contract_db() const { return contract_db; }
30+
31+
/// @brief Get the address of a function by index
32+
/// @return registered_contract_addresses[index % (registered_contract_addresses.size())]
33+
FF get_function_address(size_t index);
34+
};
35+
} // namespace bb::avm2::fuzzer

barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzz.cpp

Lines changed: 10 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "barretenberg/avm_fuzzer/common/interfaces/dbs.hpp"
44
#include "barretenberg/avm_fuzzer/fuzz_lib/constants.hpp"
5+
#include "barretenberg/avm_fuzzer/fuzz_lib/contract_db_proxy.hpp"
56
#include "barretenberg/avm_fuzzer/fuzz_lib/control_flow.hpp"
67
#include "barretenberg/avm_fuzzer/fuzz_lib/fuzzer_data.hpp"
78
#include "barretenberg/avm_fuzzer/fuzz_lib/simulator.hpp"
@@ -10,88 +11,7 @@
1011

1112
using namespace bb::avm2::fuzzer;
1213

13-
// Temp Helper function to create a default contract class from bytecode
14-
ContractClass create_default_class(const std::vector<uint8_t>& bytecode)
15-
{
16-
// This isn't strictly needed for pure simulation, but if we want to re-use inputs in proving we need valid
17-
// commitment
18-
auto bytecode_commitment = simulation::compute_public_bytecode_commitment(bytecode);
19-
auto class_id =
20-
simulation::compute_contract_class_id(/*artifact_hash=*/0, /*private_fn_root=*/0, bytecode_commitment);
21-
return ContractClass{
22-
.id = class_id,
23-
.artifact_hash = 0,
24-
.private_functions_root = 0,
25-
.packed_bytecode = bytecode,
26-
};
27-
}
28-
29-
// Temp Helper function to create a default contract instance from a class ID
30-
ContractInstance create_default_instance(const ContractClassId& class_id)
31-
{
32-
return ContractInstance{
33-
.salt = 0,
34-
.deployer = MSG_SENDER,
35-
.current_contract_class_id = class_id,
36-
.original_contract_class_id = class_id,
37-
.initialization_hash = 0,
38-
.public_keys = PublicKeys{},
39-
};
40-
}
41-
42-
// Temp Helper function to compute contract address from instance
43-
AztecAddress compute_contract_address(const ContractInstance& instance)
44-
{
45-
// This isn't strictly needed for pure simulation, but if we want to re-use inputs in proving we need valid
46-
// addresses
47-
return simulation::compute_contract_address(instance);
48-
}
49-
50-
// Creates a default transaction that the single app logic enqueued call can be inserted into
51-
Tx create_default_tx(const AztecAddress& contract_address,
52-
const AztecAddress& sender_address,
53-
const std::vector<FF>& calldata,
54-
[[maybe_unused]] const FF& transaction_fee,
55-
bool is_static_call,
56-
const Gas& gas_limit)
57-
{
58-
return Tx{
59-
.hash = TRANSACTION_HASH,
60-
.gas_settings = GasSettings{
61-
.gas_limits = gas_limit,
62-
.max_fees_per_gas = GasFees{ .fee_per_da_gas = 10000, .fee_per_l2_gas = 10000 },
63-
},
64-
.effective_gas_fees = EFFECTIVE_GAS_FEES,
65-
.non_revertible_accumulated_data = AccumulatedData{
66-
.note_hashes = NON_REVERTIBLE_ACCUMULATED_DATA_NOTE_HASHES,
67-
// This nullifier is needed to make the nonces for note hashes and expected by simulation_helper
68-
.nullifiers = NON_REVERTIBLE_ACCUMULATED_DATA_NULLIFIERS,
69-
.l2_to_l1_messages = NON_REVERTIBLE_ACCUMULATED_DATA_L2_TO_L1_MESSAGES,
70-
},
71-
.revertible_accumulated_data = AccumulatedData{
72-
.note_hashes = REVERTIBLE_ACCUMULATED_DATA_NOTE_HASHES,
73-
.nullifiers = REVERTIBLE_ACCUMULATED_DATA_NULLIFIERS,
74-
.l2_to_l1_messages = REVERTIBLE_ACCUMULATED_DATA_L2_TO_L1_MESSAGES,
75-
},
76-
.setup_enqueued_calls = SETUP_ENQUEUED_CALLS,
77-
.app_logic_enqueued_calls = {
78-
PublicCallRequestWithCalldata{
79-
.request = PublicCallRequest{
80-
.msg_sender = MSG_SENDER,
81-
.contract_address = contract_address,
82-
.is_static_call = is_static_call,
83-
.calldata_hash = 0,
84-
},
85-
.calldata = calldata,
86-
},
87-
},
88-
.teardown_enqueued_call = TEARDOWN_ENQUEUED_CALLS,
89-
.gas_used_by_private = GAS_USED_BY_PRIVATE,
90-
.fee_payer = sender_address,
91-
};
92-
}
93-
94-
SimulatorResult fuzz(FuzzerData& fuzzer_data)
14+
SimulatorResult fuzz_against_ts_simulator(FuzzerData& fuzzer_data)
9515
{
9616
auto control_flow = ControlFlow(fuzzer_data.instruction_blocks);
9717
for (const auto& cfg_instruction : fuzzer_data.cfg_instructions) {
@@ -107,17 +27,12 @@ SimulatorResult fuzz(FuzzerData& fuzzer_data)
10727
SimulatorResult cpp_result;
10828

10929
FuzzerWorldStateManager* ws_mgr = FuzzerWorldStateManager::getInstance();
110-
111-
// Create contract DB and populate with default class and instance
112-
// todo(ilyas): extend to support multiple contracts via FuzzerData
113-
FuzzerContractDB contract_db;
114-
auto default_class = create_default_class(bytecode);
115-
auto default_instance = create_default_instance(default_class.id);
116-
auto contract_address = simulation::compute_contract_address(default_instance);
117-
contract_db.add_contract_class(default_class.id, default_class);
118-
contract_db.add_contract_instance(contract_address, default_instance);
119-
120-
ws_mgr->register_contract_address(contract_address);
30+
ContractDBProxy* contract_db_proxy = ContractDBProxy::get_instance();
31+
for (const auto& function : PREDEFINED_FUNCTIONS) {
32+
ContractDBProxy::register_contract_from_bytecode(function);
33+
}
34+
auto contract_address = ContractDBProxy::register_contract_from_bytecode(bytecode);
35+
FuzzerContractDB contract_db = *contract_db_proxy->get_contract_db();
12136

12237
// Create the transaction
12338
auto tx = create_default_tx(
@@ -134,6 +49,8 @@ SimulatorResult fuzz(FuzzerData& fuzzer_data)
13449
ws_mgr->checkpoint();
13550
auto js_result = js_simulator->simulate(*ws_mgr, contract_db, tx);
13651

52+
ContractDBProxy::reset_instance();
53+
13754
// If the results does not match
13855
if (!compare_simulator_results(cpp_result, js_result)) {
13956
fuzz_info("CppSimulator ", cpp_result);

barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzz.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
/// @param fuzzer_data the fuzzer data to use for fuzzing
88
/// @returns the simulator result if the results are the same
99
/// @throws an exception if the simulator results are different
10-
SimulatorResult fuzz(FuzzerData& fuzzer_data);
10+
SimulatorResult fuzz_against_ts_simulator(FuzzerData& fuzzer_data);
1111

1212
// Helper functions to create default contract class, instance, and tx
1313
ContractClass create_default_class(const std::vector<uint8_t>& bytecode);

0 commit comments

Comments
 (0)