1414#include " barretenberg/avm_fuzzer/mutations/basic_types/uint64_t.hpp"
1515#include " barretenberg/avm_fuzzer/mutations/configuration.hpp"
1616#include " barretenberg/avm_fuzzer/mutations/fuzzer_data.hpp"
17+ #include " barretenberg/avm_fuzzer/mutations/protocol_contracts.hpp"
1718#include " barretenberg/avm_fuzzer/mutations/tx_data.hpp"
1819#include " barretenberg/avm_fuzzer/mutations/tx_types/gas.hpp"
1920#include " barretenberg/common/log.hpp"
@@ -47,6 +48,22 @@ void setup_fuzzer_state(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB& contr
4748 contract_db.add_contract_instance (contract_address, contract_instance);
4849 }
4950
51+ // For protocol contracts, also add instances keyed by canonical address (1-11).
52+ // This is needed because protocol contracts are looked up by canonical address,
53+ // but the derived address in protocol_contracts.derived_addresses maps to the actual instance.
54+ for (size_t i = 0 ; i < tx_data.protocol_contracts .derived_addresses .size (); ++i) {
55+ const auto & derived_address = tx_data.protocol_contracts .derived_addresses [i];
56+ if (!derived_address.is_zero ()) {
57+ // Canonical address is index + 1 (addresses 1-11 map to indices 0-10)
58+ AztecAddress canonical_address (static_cast <uint256_t >(i + 1 ));
59+ // Find the instance for this derived address and also add it by canonical address
60+ auto maybe_instance = contract_db.get_contract_instance (derived_address);
61+ if (maybe_instance.has_value ()) {
62+ contract_db.add_contract_instance (canonical_address, maybe_instance.value ());
63+ }
64+ }
65+ }
66+
5067 // Register contract addresses in the world state
5168 for (const auto & addr : tx_data.contract_addresses ) {
5269 ws_mgr.register_contract_address (addr);
@@ -81,8 +98,12 @@ SimulatorResult fuzz_tx(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB& contr
8198
8299 try {
83100 ws_mgr.checkpoint ();
84- cpp_result = cpp_simulator.simulate (
85- ws_mgr, contract_db, tx_data.tx , tx_data.global_variables , tx_data.public_data_writes );
101+ cpp_result = cpp_simulator.simulate (ws_mgr,
102+ contract_db,
103+ tx_data.tx ,
104+ tx_data.global_variables ,
105+ tx_data.public_data_writes ,
106+ tx_data.protocol_contracts );
86107 fuzz_info (" CppSimulator completed without exception" );
87108 fuzz_info (" CppSimulator result: " , cpp_result);
88109 ws_mgr.revert ();
@@ -98,8 +119,12 @@ SimulatorResult fuzz_tx(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB& contr
98119 }
99120
100121 ws_mgr.checkpoint ();
101- auto js_result =
102- js_simulator->simulate (ws_mgr, contract_db, tx_data.tx , tx_data.global_variables , tx_data.public_data_writes );
122+ auto js_result = js_simulator->simulate (ws_mgr,
123+ contract_db,
124+ tx_data.tx ,
125+ tx_data.global_variables ,
126+ tx_data.public_data_writes ,
127+ tx_data.protocol_contracts );
103128
104129 // If the results do not match
105130 if (!compare_simulator_results (cpp_result, js_result)) {
@@ -122,7 +147,7 @@ SimulatorResult fuzz_tx(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB& contr
122147// / @throws An exception if simulation results differ or check_circuit fails
123148int fuzz_prover (FuzzerWorldStateManager& ws_mgr, FuzzerContractDB& contract_db, FuzzerTxData& tx_data)
124149{
125- ProtocolContracts protocol_contracts{} ;
150+ ProtocolContracts& protocol_contracts = tx_data. protocol_contracts ;
126151 WorldState& ws = ws_mgr.get_world_state ();
127152 WorldStateRevision ws_rev = ws_mgr.get_current_revision ();
128153 AvmSimulationHelper helper;
@@ -311,6 +336,15 @@ size_t mutate_tx_data(FuzzerContext& context,
311336
312337 tx_data.contract_addresses = contract_addresses;
313338
339+ // Clear any protocol contract derived addresses that reference addresses no longer in the contract set.
340+ // This can happen when bytecode mutation causes contract addresses to change between mutation rounds.
341+ // note: we shouldnt have to aggressively do this if we modify only 1 fuzzer data at a time.
342+ for (auto & derived_address : tx_data.protocol_contracts .derived_addresses ) {
343+ if (!derived_address.is_zero () && !seen_addresses.contains (derived_address)) {
344+ derived_address = AztecAddress (0 );
345+ }
346+ }
347+
314348 // Ensure all enqueued calls have valid contract addresses (not placeholders)
315349 // We may add more advanced mutation to change contract addresses later, right now we just ensure they are valid
316350 auto idx_dist = std::uniform_int_distribution<size_t >(0 , contract_addresses.size () - 1 );
@@ -352,14 +386,10 @@ size_t mutate_tx_data(FuzzerContext& context,
352386 // This is just mutating the gas values and timestamp
353387 mutate_uint64_t (tx_data.global_variables .timestamp , rng, BASIC_UINT64_T_MUTATION_CONFIGURATION);
354388 mutate_gas_fees (tx_data.global_variables .gas_fees , rng);
355- // This must be less than or equal to the tx max fees per gas
356- tx_data.global_variables .gas_fees .fee_per_da_gas = std::min (
357- tx_data.global_variables .gas_fees .fee_per_da_gas , tx_data.tx .gas_settings .max_fees_per_gas .fee_per_da_gas );
358- tx_data.global_variables .gas_fees .fee_per_l2_gas = std::min (
359- tx_data.global_variables .gas_fees .fee_per_l2_gas , tx_data.tx .gas_settings .max_fees_per_gas .fee_per_l2_gas );
360389 break ;
361- // case TxDataMutationType::ProtocolContractsMutation:
362- // break;
390+ case FuzzerTxDataMutationType::ProtocolContractsMutation:
391+ mutate_protocol_contracts (tx_data.protocol_contracts , tx_data.tx , tx_data.contract_addresses , rng);
392+ break ;
363393 }
364394
365395 // todo: do we need to ensure this or are should we able to process 0 enqueued calls?
@@ -376,6 +406,13 @@ size_t mutate_tx_data(FuzzerContext& context,
376406 .calldata = calldata });
377407 }
378408
409+ // Ensure global gas_fees <= max_fees_per_gas (required for compute_effective_gas_fees)
410+ // This must run after ANY mutation since TxMutation can reduce max_fees_per_gas
411+ tx_data.global_variables .gas_fees .fee_per_da_gas = std::min (
412+ tx_data.global_variables .gas_fees .fee_per_da_gas , tx_data.tx .gas_settings .max_fees_per_gas .fee_per_da_gas );
413+ tx_data.global_variables .gas_fees .fee_per_l2_gas = std::min (
414+ tx_data.global_variables .gas_fees .fee_per_l2_gas , tx_data.tx .gas_settings .max_fees_per_gas .fee_per_l2_gas );
415+
379416 // Compute effective gas fees matching TS computeEffectiveGasFees
380417 // This must be done after any mutation that could affect gas settings or global variables
381418 tx_data.tx .effective_gas_fees =
0 commit comments