Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
fuzzer_lib.cpp
Go to the documentation of this file.
2
3#include <cstdint>
4#include <string>
5#include <unordered_set>
6#include <vector>
7
22
23using namespace bb::avm2::fuzzer;
24using namespace bb::avm2::simulation;
25using namespace bb::world_state;
26
28{
29 // Add all contract classes and instances to the contract DB
30 for (size_t i = 0; i < tx_data.contract_classes.size(); ++i) {
31 const auto& contract_class = tx_data.contract_classes[i];
32 const auto& contract_instance = tx_data.contract_instances[i];
33 auto contract_address = tx_data.contract_addresses[i];
34 contract_db.add_contract_class(contract_class.id, contract_class);
35 contract_db.add_contract_instance(contract_address, contract_instance);
36 }
37
38 // Register the de-duplicated set of contract addresses to the world state (in insertion order)
40 for (const auto& addr : tx_data.contract_addresses) {
41 if (seen_addresses.insert(addr).second) {
42 fuzz_info("Registering contract address in world state: ", addr);
44 }
45 }
46}
47
49{
50 // Compute fee from gas limits and max fees per gas (upper bound on fee)
51 FF fee_required_da = FF(tx.gas_settings.gas_limits.da_gas) * FF(tx.gas_settings.max_fees_per_gas.fee_per_da_gas);
52 FF fee_required_l2 = FF(tx.gas_settings.gas_limits.l2_gas) * FF(tx.gas_settings.max_fees_per_gas.fee_per_l2_gas);
53
54 // Fund fee payer with required fee
55 ws_mgr.write_fee_payer_balance(tx.fee_payer, fee_required_da + fee_required_l2);
56}
57
63{
64 // Run simulators
65 auto cpp_simulator = CppSimulator();
66 JsSimulator* js_simulator = JsSimulator::getInstance();
67 SimulatorResult cpp_result;
68
69 try {
71 cpp_result = cpp_simulator.simulate(ws_mgr, contract_db, tx_data.tx);
72 ws_mgr.revert();
73 } catch (const std::exception& e) {
74 fuzz_info("CppSimulator threw an exception: ", e.what());
75 cpp_result = SimulatorResult{
76 .reverted = true,
77 .output = {},
78 .end_tree_snapshots = TreeSnapshots(),
79 .revert_reason = e.what(),
80 };
81 ws_mgr.revert();
82 }
83
85 auto js_result = js_simulator->simulate(ws_mgr, contract_db, tx_data.tx);
86
87 // If the results do not match
88 if (!compare_simulator_results(cpp_result, js_result)) {
89 fuzz_info("CppSimulator ", cpp_result);
90 fuzz_info("JsSimulator ", js_result);
91 throw std::runtime_error("Simulator results are different");
92 }
93 fuzz_info("Simulator results match successfully");
94 fuzz_info("CppSimulator ", cpp_result);
95 fuzz_info("JsSimulator ", js_result);
96
97 return cpp_result;
98}
99
107{
108 ProtocolContracts protocol_contracts{};
111 AvmSimulationHelper helper;
112
113 // Reset stats for this iteration
115
116 const PublicSimulatorConfig sim_fast_config{
117 .skip_fee_enforcement = false,
118 .collect_call_metadata = true,
119 .collect_public_inputs = true,
120 };
121
122 const PublicSimulatorConfig sim_hint_config{
123 .skip_fee_enforcement = false,
124 .collect_call_metadata = true,
125 .collect_hints = true,
126 .collect_public_inputs = true,
127 };
128
129 // 1. Run simulate_fast_with_existing_ws
130 // It is the only one that may throw, so we wrap it in try-catch. If it fails, we do not proceed
131 TxSimulationResult fast_result;
132 try {
134 fast_result = AVM_TRACK_TIME_V(
135 "fuzzer/simulate_fast",
137 contract_db, ws_rev, ws, sim_fast_config, tx_data.tx, tx_data.global_variables, protocol_contracts));
138 ws_mgr.revert();
139 } catch (const std::exception& e) {
140 ws_mgr.revert();
141 fuzz_info("simulate_fast_with_existing_ws threw an exception: ", e.what());
142 return 0;
143 }
144
145 // 2. Run simulate_for_hint_collection
148 "fuzzer/simulate_hints",
150 contract_db, ws_rev, ws, sim_hint_config, tx_data.tx, tx_data.global_variables, protocol_contracts));
151 ws_mgr.revert();
152
153 // 2a. Construct proving inputs from hint result
154 bb::avm2::AvmProvingInputs proving_inputs{
155 .public_inputs = hint_result.public_inputs.value(),
156 .hints = hint_result.hints.value(),
157 };
158
159 // 3. (optional) Simulate Fast with hinted db
160 TxSimulationResult sim_fast_hinted_result =
161 helper.simulate_fast_with_hinted_dbs(proving_inputs.hints, sim_fast_config);
162
163 // 4. Compare results from all 3 simulations
164 bool result = compare_cpp_simulator_results({ fast_result, hint_result, sim_fast_hinted_result });
165 BB_ASSERT(result,
166 "Simulation results do not match between simulate_fast, simulate_for_hint_collection, "
167 "and simulate_fast_with_hinted_dbs");
168
169 // 5. Run check_circuit (skip in coverage builds since it's slow and not needed for coverage measurement)
170
171#ifndef COVERAGE_AVM
172 avm2::AvmAPI avm_api;
173 bool check_circuit_result = avm_api.check_circuit(proving_inputs);
174 BB_ASSERT(check_circuit_result,
175 "check_circuit returned false in fuzzer with no exception, this indicates a failure");
176#else
177 // In coverage builds, run simulate_for_witgen instead of check_circuit
178 // This gives us coverage the the event and tracegen code paths without the overhead of check_circuit
179 vinfo("Running simulate_for_witgen in coverage build (skipping check_circuit)");
180 avm2::AvmSimulationHelper simulation_helper;
181 simulation_helper.simulate_for_witgen(proving_inputs.hints);
182#endif
183
184 return 0;
185}
186
187// Initialize FuzzerTxData with sensible defaults
189{
190 FuzzerData fuzzer_data = generate_fuzzer_data(rng, context);
191 FuzzerTxData tx_data = {
192 .input_programs = { fuzzer_data },
194 .global_variables = { .chain_id = CHAIN_ID,
195 .version = VERSION,
196 .block_number = BLOCK_NUMBER,
197 .slot_number = SLOT_NUMBER,
198 .timestamp = TIMESTAMP,
199 .coinbase = COINBASE,
200 .fee_recipient = FEE_RECIPIENT,
201 .gas_fees =
202 GasFees{ .fee_per_da_gas = FEE_PER_DA_GAS, .fee_per_l2_gas = FEE_PER_L2_GAS } },
203 .protocol_contracts = {},
204 };
205 return tx_data;
206}
207
213
215{
216 fuzz_info("Building bytecode from fuzzer data: ", fuzzer_data.instruction_blocks);
217 auto control_flow = ControlFlow(fuzzer_data.instruction_blocks);
218 for (const auto& cfg_instruction : fuzzer_data.cfg_instructions) {
219 control_flow.process_cfg_instruction(cfg_instruction);
220 }
221 auto bytecode = control_flow.build_bytecode(fuzzer_data.return_options);
222
223 auto bytecode_commitment = compute_public_bytecode_commitment(bytecode);
224 auto class_id = compute_contract_class_id(/*artifact_hash=*/0, /*private_fn_root=*/0, bytecode_commitment);
225 ContractClass contract_class{
226 .id = class_id,
227 .artifact_hash = 0,
228 .private_functions_root = 0,
229 .packed_bytecode = bytecode,
230 };
231 ContractInstance contract_instance{
232 .salt = 0,
233 .deployer = MSG_SENDER,
234 .current_contract_class_id = class_id, // Initial and current are the same
235 .original_contract_class_id = class_id,
236 .public_keys = {
237 .nullifier_key = { 0, 0 },
238 .incoming_viewing_key = grumpkin::g1::element::one(),
239 .outgoing_viewing_key = { 0, 0 },
240 .tagging_key = { 0, 0 },
241 },
242 };
243 return { bytecode, contract_class, contract_instance };
244}
245
247 uint8_t* serialized_fuzzer_data,
248 size_t serialized_fuzzer_data_size,
249 size_t max_size,
250 unsigned int seed)
251{
252 auto rng = std::mt19937_64(seed);
253 FuzzerTxData tx_data;
254 try {
255 msgpack::unpack((reinterpret_cast<const char*>(serialized_fuzzer_data)), serialized_fuzzer_data_size)
256 .get()
257 .convert(tx_data);
258 } catch (const std::exception&) {
259 fuzz_info("Failed to deserialize input in CustomMutator, creating default FuzzerTxData");
260 tx_data = create_default_tx_data(rng, context);
261 }
262
263 // Mutate the fuzzer data multiple times for better bytecode variety
264 auto num_mutations = std::uniform_int_distribution<uint8_t>(1, 5)(rng);
265 for (uint8_t i = 0; i < num_mutations; i++) {
267 }
268
269 // Build up bytecodes, contract classes and instances from the fuzzer data
270 tx_data.contract_classes.clear();
271 tx_data.contract_instances.clear();
272 tx_data.contract_addresses.clear();
273 std::vector<AztecAddress> contract_addresses;
274
275 for (auto& fuzzer_data : tx_data.input_programs) {
276 const auto [bytecode, contract_class, contract_instance] = build_bytecode_and_artifacts(fuzzer_data);
277
279 contract_addresses.push_back(contract_address);
280
281 tx_data.contract_classes.push_back(contract_class);
282 tx_data.contract_instances.push_back(contract_instance);
283 }
284
285 tx_data.contract_addresses = contract_addresses;
286
287 // Ensure all enqueued calls have valid contract addresses (not placeholders)
288 // We may add more advanced mutation to change contract addresses later, right now we just ensure they are valid
289 auto idx_dist = std::uniform_int_distribution<size_t>(0, contract_addresses.size() - 1);
290 if (!contract_addresses.empty()) {
291 for (auto& call : tx_data.tx.setup_enqueued_calls) {
292 call.request.contract_address = contract_addresses[idx_dist(rng)];
293 }
294 for (auto& call : tx_data.tx.app_logic_enqueued_calls) {
295 call.request.contract_address = contract_addresses[idx_dist(rng)];
296 }
297 }
298
299 // Select mutation type (weighted against bytecode mutations) -- todo
300 auto mutation_type = std::uniform_int_distribution<uint8_t>(0, 0);
301 TxDataMutationType mutation_choice = static_cast<TxDataMutationType>(mutation_type(rng));
302
303 switch (mutation_choice) {
305 mutate_tx(tx_data.tx, contract_addresses, rng);
306 break;
307 // case TxDataMutationType::BytecodeMutation:
308 // // todo: Maybe here we can do some direct mutations on the bytecode
309 // // Mutations here are likely to cause immediate failure
310 // break;
311 // case TxDataMutationType::ContractClassMutation:
312 // // Mutations here are likely to cause immediate failure
313 // break;
314 // case TxDataMutationType::ContractInstanceMutation:
315 // // Mutations here are likely to cause immediate failure
316 // break;
317 // case TxDataMutationType::GlobalVariablesMutation:
318 // break;
319 // case TxDataMutationType::ProtocolContractsMutation:
320 // break;
321 }
322
323 // todo: do we need to ensure this or are should we able to process 0 enqueued calls?
324 // Ensure at least 1 app_logic enqueued call exists (mutations may have deleted all)
325 if (tx_data.tx.app_logic_enqueued_calls.empty() && !contract_addresses.empty()) {
326 auto idx = std::uniform_int_distribution<size_t>(0, contract_addresses.size() - 1)(rng);
327 std::vector<FF> calldata = {};
328 auto calldata_hash = compute_calldata_hash(calldata);
329 tx_data.tx.app_logic_enqueued_calls.push_back(
331 .contract_address = contract_addresses[idx],
332 .is_static_call = false,
333 .calldata_hash = calldata_hash },
334 .calldata = calldata });
335 }
336 auto [mutated_serialized_fuzzer_data, mutated_serialized_fuzzer_data_size] = msgpack_encode_buffer(tx_data);
337 if (mutated_serialized_fuzzer_data_size > max_size) {
338 delete[] mutated_serialized_fuzzer_data;
339 return 0; // Can't fit mutated data in buffer, skip this mutation
340 }
341 memcpy(serialized_fuzzer_data, mutated_serialized_fuzzer_data, mutated_serialized_fuzzer_data_size);
342 delete[] mutated_serialized_fuzzer_data;
343
344 return mutated_serialized_fuzzer_data_size;
345}
#define BB_ASSERT(expression,...)
Definition assert.hpp:80
const uint32_t BLOCK_NUMBER
Definition constants.hpp:16
#define fuzz_info(...)
Definition constants.hpp:51
const AztecAddress FEE_RECIPIENT
Definition constants.hpp:20
const Gas GAS_LIMIT
Definition constants.hpp:40
const FF TRANSACTION_FEE
Definition constants.hpp:38
const EthAddress COINBASE
Definition constants.hpp:19
const FF MSG_SENDER
Definition constants.hpp:33
const FF SLOT_NUMBER
Definition constants.hpp:17
const FF CHAIN_ID
Definition constants.hpp:14
constexpr uint128_t FEE_PER_DA_GAS
Definition constants.hpp:21
const bool IS_STATIC_CALL
Definition constants.hpp:39
constexpr uint128_t FEE_PER_L2_GAS
Definition constants.hpp:22
std::shared_ptr< Napi::ThreadSafeFunction > bytecode
StrictMock< MockContractDB > contract_db
uses barretenberg/vm2 to simulate the bytecode
Definition simulator.hpp:54
uses the yarn-project/simulator to simulate the bytecode Singleton, because initializing the simulato...
Definition simulator.hpp:63
static JsSimulator * getInstance()
SimulatorResult simulate(fuzzer::FuzzerWorldStateManager &ws_mgr, fuzzer::FuzzerContractDB &contract_db, const Tx &tx) override
bool check_circuit(const ProvingInputs &inputs)
Definition avm_api.cpp:36
TxSimulationResult simulate_for_hint_collection(simulation::ContractDBInterface &raw_contract_db, const world_state::WorldStateRevision &world_state_revision, world_state::WorldState &ws, const PublicSimulatorConfig &config, const Tx &tx, const GlobalVariables &global_variables, const ProtocolContracts &protocol_contracts, simulation::CancellationTokenPtr cancellation_token=nullptr)
TxSimulationResult simulate_fast_with_hinted_dbs(const ExecutionHints &hints, const PublicSimulatorConfig &config)
TxSimulationResult simulate_fast_with_existing_ws(simulation::ContractDBInterface &raw_contract_db, const world_state::WorldStateRevision &world_state_revision, world_state::WorldState &ws, const PublicSimulatorConfig &config, const Tx &tx, const GlobalVariables &global_variables, const ProtocolContracts &protocol_contracts, simulation::CancellationTokenPtr cancellation_token=nullptr)
simulation::EventsContainer simulate_for_witgen(const ExecutionHints &hints)
static Stats & get()
Definition stats.cpp:10
void reset()
Definition stats.cpp:16
world_state::WorldState & get_world_state()
Definition dbs.hpp:94
void register_contract_address(const AztecAddress &contract_address)
Definition dbs.cpp:218
void write_fee_payer_balance(const AztecAddress &fee_payer, const FF &balance)
Definition dbs.cpp:227
world_state::WorldStateRevision get_current_revision() const
Definition dbs.cpp:199
static constexpr element one
Definition group.hpp:46
Holds the Merkle trees responsible for storing the state of the Aztec protocol.
#define vinfo(...)
Definition log.hpp:94
FuzzerWorldStateManager * ws_mgr
Definition fuzz.test.cpp:16
bool compare_cpp_simulator_results(const std::vector< TxSimulationResult > &results)
SimulatorResult fuzz_tx(FuzzerWorldStateManager &ws_mgr, FuzzerContractDB &contract_db, FuzzerTxData &tx_data)
Fuzz CPP vs JS simulator with a full transaction containing multiple enqueued calls.
FuzzerTxData create_default_tx_data(std::mt19937_64 &rng, const FuzzerContext &context)
int fuzz_prover(FuzzerWorldStateManager &ws_mgr, FuzzerContractDB &contract_db, FuzzerTxData &tx_data)
Run the prover fuzzer: fast simulation, hint collection, comparison, and check_circuit.
ContractArtifacts build_bytecode_and_artifacts(FuzzerData &fuzzer_data)
void setup_fuzzer_state(FuzzerWorldStateManager &ws_mgr, FuzzerContractDB &contract_db, const FuzzerTxData &tx_data)
size_t mutate_tx_data(FuzzerContext &context, uint8_t *serialized_fuzzer_data, size_t serialized_fuzzer_data_size, size_t max_size, unsigned int seed)
void fund_fee_payer(FuzzerWorldStateManager &ws_mgr, const Tx &tx)
TxDataMutationType
std::tuple< Bytecode, ContractClass, ContractInstance > ContractArtifacts
std::pair< uint8_t *, size_t > msgpack_encode_buffer(auto &&obj, uint8_t *scratch_buf=nullptr, size_t scratch_size=0)
void mutate_tx(Tx &tx, std::vector< AztecAddress > &contract_addresses, std::mt19937_64 &rng)
Definition tx_data.cpp:75
FuzzerData generate_fuzzer_data(std::mt19937_64 &rng, const FuzzerContext &context)
void mutate_fuzzer_data_vec(const FuzzerContext &context, std::vector< FuzzerData > &enqueued_calls, std::mt19937_64 &rng, size_t max_size)
Definition tx_data.cpp:201
FF compute_public_bytecode_commitment(std::span< const uint8_t > bytecode)
FF compute_contract_class_id(const FF &artifact_hash, const FF &private_fn_root, const FF &public_bytecode_commitment)
FF compute_calldata_hash(std::span< const FF > calldata)
FF compute_contract_address(const ContractInstance &contract_instance)
AvmFlavorSettings::FF FF
Definition field.hpp:10
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
bool compare_simulator_results(SimulatorResult &result1, SimulatorResult &result2)
Tx create_default_tx(const AztecAddress &contract_address, const AztecAddress &sender_address, const std::vector< FF > &calldata, const FF &transaction_fee, bool is_static_call, const Gas &gas_limit)
#define AVM_TRACK_TIME_V(key, body)
Definition stats.hpp:18
describes the data which will be used for fuzzing Should contain instructions, calldata,...
ReturnOptions return_options
std::vector< CFGInstruction > cfg_instructions
std::vector< std::vector< FuzzInstruction > > instruction_blocks
std::vector< AztecAddress > contract_addresses
std::vector< ContractClass > contract_classes
std::vector< FuzzerData > input_programs
std::vector< ContractInstance > contract_instances
GlobalVariables global_variables
PublicInputs public_inputs
Definition avm_io.hpp:418
uint128_t fee_per_da_gas
std::vector< PublicCallRequestWithCalldata > setup_enqueued_calls
Definition avm_io.hpp:337
std::vector< PublicCallRequestWithCalldata > app_logic_enqueued_calls
Definition avm_io.hpp:338
std::optional< ExecutionHints > hints
Definition avm_io.hpp:558
std::optional< PublicInputs > public_inputs
Definition avm_io.hpp:557