From efc38ef228c56321ac5392a9e38992a6be9c5b1d Mon Sep 17 00:00:00 2001 From: sirasistant Date: Thu, 15 Jan 2026 10:57:11 +0000 Subject: [PATCH 1/4] refactor note hash exists --- .../avm_fuzzer/common/interfaces/dbs.cpp | 13 +++++ .../avm_fuzzer/common/interfaces/dbs.hpp | 1 + .../barretenberg/avm_fuzzer/fuzz_lib/fuzz.cpp | 4 +- .../avm_fuzzer/fuzz_lib/fuzzer_context.cpp | 14 +++++ .../avm_fuzzer/fuzz_lib/fuzzer_context.hpp | 5 ++ .../avm_fuzzer/fuzz_lib/instruction.hpp | 18 ++----- .../avm_fuzzer/fuzz_lib/memory_manager.cpp | 21 -------- .../avm_fuzzer/fuzz_lib/memory_manager.hpp | 10 ---- .../avm_fuzzer/fuzz_lib/program_block.cpp | 24 --------- .../avm_fuzzer/fuzz_lib/simulator.cpp | 14 +++-- .../avm_fuzzer/fuzz_lib/simulator.hpp | 37 ++++++++----- .../barretenberg/avm_fuzzer/fuzzer_lib.cpp | 52 ++++++++++++++++--- .../barretenberg/avm_fuzzer/fuzzer_lib.hpp | 10 ++-- .../avm_fuzzer/mutations/configuration.hpp | 5 +- .../mutations/instructions/instruction.cpp | 43 +++++++++++---- .../mutations/instructions/instruction.hpp | 1 + .../public/fuzzing/avm_fuzzer_simulator.ts | 12 +++++ .../src/public/fuzzing/avm_simulator_bin.ts | 4 ++ 18 files changed, 177 insertions(+), 111 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp index cda0715dc27a..9de850a85dd8 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp @@ -6,6 +6,7 @@ #include "barretenberg/avm_fuzzer/fuzz_lib/constants.hpp" #include "barretenberg/common/throw_or_abort.hpp" #include "barretenberg/crypto/merkle_tree/indexed_tree/indexed_leaf.hpp" +#include "barretenberg/crypto/merkle_tree/response.hpp" #include "barretenberg/crypto/poseidon2/poseidon2.hpp" #include "barretenberg/vm2/common/aztec_constants.hpp" #include "barretenberg/vm2/common/aztec_types.hpp" @@ -194,6 +195,8 @@ void FuzzerWorldStateManager::initialize_world_state() /*thread_pool_size=*/4, DATA_DIR, MAP_SIZE_KB, tree_heights, tree_prefill, initial_header_generator_point); fork_ids.push(ws->create_fork(std::nullopt)); + std::cout << "Items of note hash tree after creation: " + << ws->get_tree_info(get_current_revision(), MerkleTreeId::NOTE_HASH_TREE).meta.size << std::endl; } WorldStateRevision FuzzerWorldStateManager::get_current_revision() const @@ -243,4 +246,14 @@ void FuzzerWorldStateManager::public_data_write(const bb::crypto::merkle_tree::P ws->update_public_data(public_data, fork_id); } +void FuzzerWorldStateManager::append_note_hashes(const std::vector& note_hashes) +{ + auto fork_id = fork_ids.top(); + + uint64_t padding_leaves = MAX_NOTE_HASHES_PER_TX - (note_hashes.size() % MAX_NOTE_HASHES_PER_TX); + + ws->append_leaves(MerkleTreeId::NOTE_HASH_TREE, note_hashes, fork_id); + ws->append_leaves(MerkleTreeId::NOTE_HASH_TREE, std::vector(padding_leaves, FF(0)), fork_id); +} + } // namespace bb::avm2::fuzzer diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.hpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.hpp index e229dfe7b52d..3462bf21ba8f 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.hpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.hpp @@ -89,6 +89,7 @@ class FuzzerWorldStateManager { void register_contract_address(const AztecAddress& contract_address); void write_fee_payer_balance(const AztecAddress& fee_payer, const FF& balance); void public_data_write(const bb::crypto::merkle_tree::PublicDataLeafValue& public_data); + void append_note_hashes(const std::vector& note_hashes); world_state::WorldStateRevision get_current_revision() const; world_state::WorldStateRevision fork(); diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzz.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzz.cpp index b104917ebe5f..4ab5b2ce0a44 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzz.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzz.cpp @@ -42,14 +42,14 @@ SimulatorResult fuzz_against_ts_simulator(FuzzerData& fuzzer_data, FuzzerContext try { ws_mgr->checkpoint(); - cpp_result = cpp_simulator.simulate(*ws_mgr, contract_db, tx, /*public_data_writes=*/{}); + cpp_result = cpp_simulator.simulate(*ws_mgr, contract_db, tx, /*public_data_writes=*/{}, /*note_hashes=*/{}); ws_mgr->revert(); } catch (const std::exception& e) { throw std::runtime_error(std::string("CppSimulator threw an exception: ") + e.what()); } ws_mgr->checkpoint(); - auto js_result = js_simulator->simulate(*ws_mgr, contract_db, tx, /*public_data_writes=*/{}); + auto js_result = js_simulator->simulate(*ws_mgr, contract_db, tx, /*public_data_writes=*/{}, /*note_hashes=*/{}); context.reset(); diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzzer_context.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzzer_context.cpp index 04884770d3f7..5a732a20b90e 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzzer_context.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzzer_context.cpp @@ -71,10 +71,24 @@ FF FuzzerContext::register_contract_from_bytecode(const std::vector& by return contract_address; } +std::optional> FuzzerContext::get_existing_note_hash(size_t index) const +{ + if (existing_note_hashes_.size() == 0) { + return std::nullopt; + } + return existing_note_hashes_[index % existing_note_hashes_.size()]; +} + +void FuzzerContext::set_existing_note_hashes(std::span> note_hashes) +{ + existing_note_hashes_.assign(note_hashes.begin(), note_hashes.end()); +} + void FuzzerContext::reset() { contract_addresses_.clear(); contract_db_ = std::make_unique(); + existing_note_hashes_.clear(); } } // namespace bb::avm2::fuzzer diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzzer_context.hpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzzer_context.hpp index 30126a7e2728..4c02d222cda5 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzzer_context.hpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzzer_context.hpp @@ -53,9 +53,14 @@ class FuzzerContext { /// @brief Get the contract database for simulation FuzzerContractDB& get_contract_db() const { return *contract_db_; } + std::optional> get_existing_note_hash(size_t index) const; + + void set_existing_note_hashes(std::span> note_hashes); + private: std::vector contract_addresses_; std::unique_ptr contract_db_; + std::vector> existing_note_hashes_; }; } // namespace bb::avm2::fuzzer diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/instruction.hpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/instruction.hpp index 2194094604ea..1729dec60ec5 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/instruction.hpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/instruction.hpp @@ -501,21 +501,11 @@ struct EMITNOTEHASH_Instruction { MSGPACK_FIELDS(note_hash_address, note_hash); }; -/// @brief NOTEHASHEXISTS: M[result_offset] = NOTEHASHEXISTS(M[notehash_offset], M[leaf_index_offset]) -/// len = length(memory_manager.emitted_note_hashes); -/// M[notehash_offset] = unique_note_hash(CONTRACT_ADDRESS, memory_manager.emitted_note_hashes[notehash_index % len]); -/// M[leaf_index_offset] = notehash_index % len; -/// M[result_offset] = NOTEHASHEXISTS(M[notehash_offset], M[leaf_index_offset]); struct NOTEHASHEXISTS_Instruction { - // index of the note hash in the memory_manager.emitted_note_hashes - uint16_t notehash_index; - // absolute address where the note hash will be stored - AddressRef notehash_address; - // absolute address where the leaf index will be stored - AddressRef leaf_index_address; - // absolute address where the result will be stored - AddressRef result_address; - MSGPACK_FIELDS(notehash_index, notehash_address, leaf_index_address, result_address); + ParamRef notehash_address; + ParamRef leaf_index_address; + AddressRef result_address; + MSGPACK_FIELDS(notehash_address, leaf_index_address, result_address); }; /// @brief CALLDATACOPY: M[dstOffset:dstOffset+M[copySizeOffset]] = diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/memory_manager.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/memory_manager.cpp index 242f340d3091..e4becb1d4947 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/memory_manager.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/memory_manager.cpp @@ -315,27 +315,6 @@ std::optional MemoryManager::get_slot(uint16_t slot_offset_index) return storage_addresses[slot_offset_index % storage_addresses.size()]; } -void MemoryManager::append_emitted_note_hash(bb::avm2::FF note_hash) -{ - emitted_note_hashes.push_back(note_hash); -} - -std::optional MemoryManager::get_emitted_note_hash(uint16_t note_hash_index) -{ - if (emitted_note_hashes.empty()) { - return std::nullopt; - } - return emitted_note_hashes[note_hash_index % emitted_note_hashes.size()]; -} - -std::optional MemoryManager::get_leaf_index(uint16_t note_hash_index) -{ - if (emitted_note_hashes.empty()) { - return std::nullopt; - } - return note_hash_index % emitted_note_hashes.size(); -} - void MemoryManager::set_base_offset(uint32_t base_offset) { this->base_offset = base_offset; diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/memory_manager.hpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/memory_manager.hpp index a4f3a7b116df..59a564f42e90 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/memory_manager.hpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/memory_manager.hpp @@ -19,8 +19,6 @@ class MemoryManager { // Public Storage used addresses std::vector storage_addresses; - std::vector emitted_note_hashes; - bb::avm2::testing::OperandBuilder get_memory_address_operand(bb::avm2::testing::OperandBuilder operand, AddressingMode mode); ResolvedAddress resolve_address(VariableRef address, uint32_t absolute_address, uint32_t max_operand_address); @@ -62,13 +60,5 @@ class MemoryManager { // Get slot from storage_addresses std::optional get_slot(uint16_t slot_offset_index); - // Append emitted note hash to emitted_note_hashes - void append_emitted_note_hash(bb::avm2::FF note_hash); - // Get emitted note hash from emitted_note_hashes - std::optional get_emitted_note_hash(uint16_t note_hash_index); - // Get leaf index from emitted_note_hashes, nullopt if emitted_note_hashes is empty - // note_hash_index % length(emitted_note_hashes) - std::optional get_leaf_index(uint16_t note_hash_index); - void set_base_offset(uint32_t base_offset); }; diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/program_block.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/program_block.cpp index f579abad6ba1..f94f23164dfc 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/program_block.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/program_block.cpp @@ -1070,7 +1070,6 @@ void ProgramBlock::process_emitnotehash_instruction(EMITNOTEHASH_Instruction ins .operand(note_hash_address_operand.value().second) .build(); instructions.push_back(emitnotehash_instruction); - memory_manager.append_emitted_note_hash(instruction.note_hash); } void ProgramBlock::process_notehashexists_instruction(NOTEHASHEXISTS_Instruction instruction) @@ -1078,29 +1077,6 @@ void ProgramBlock::process_notehashexists_instruction(NOTEHASHEXISTS_Instruction #ifdef DISABLE_NOTEHASHEXISTS_INSTRUCTION return; #endif - auto note_hash = memory_manager.get_emitted_note_hash(instruction.notehash_index); - if (!note_hash.has_value()) { - return; - } - auto leaf_index = memory_manager.get_leaf_index(instruction.notehash_index); - if (!leaf_index.has_value()) { - return; - } - auto contract_address = CONTRACT_ADDRESS; - auto note_hash_counter = static_cast(*leaf_index); - auto siloed_note_computed_hash = bb::avm2::simulation::unconstrained_silo_note_hash(contract_address, *note_hash); - auto unique_note_computed_hash = bb::avm2::simulation::unconstrained_make_unique_note_hash( - siloed_note_computed_hash, FIRST_NULLIFIER, note_hash_counter); - - auto set_note_hash_instruction = SET_FF_Instruction{ .value_tag = bb::avm2::MemoryTag::FF, - .result_address = instruction.notehash_address, - .value = unique_note_computed_hash }; - this->process_set_ff_instruction(set_note_hash_instruction); - auto set_leaf_index_instruction = SET_FF_Instruction{ .value_tag = bb::avm2::MemoryTag::U64, - .result_address = instruction.leaf_index_address, - .value = *leaf_index }; - this->process_set_ff_instruction(set_leaf_index_instruction); - auto notehash_address_operand = memory_manager.get_resolved_address_and_operand_16(instruction.notehash_address); auto leaf_index_address_operand = memory_manager.get_resolved_address_and_operand_16(instruction.leaf_index_address); diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/simulator.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/simulator.cpp index 87773722977a..cb8386b8eb62 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/simulator.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/simulator.cpp @@ -38,7 +38,8 @@ std::string serialize_simulation_request( const Tx& tx, const GlobalVariables& globals, const FuzzerContractDB& contract_db, - const std::vector& public_data_writes) + const std::vector& public_data_writes, + const std::vector& note_hashes) { // Build vectors from contract_db std::vector classes_vec = contract_db.get_contract_classes(); @@ -52,6 +53,7 @@ std::string serialize_simulation_request( .contract_classes = std::move(classes_vec), .contract_instances = std::move(instances_vec), .public_data_writes = public_data_writes, + .note_hashes = note_hashes, }; auto [buffer, size] = msgpack_encode_buffer(request); @@ -79,9 +81,10 @@ SimulatorResult CppSimulator::simulate( fuzzer::FuzzerWorldStateManager& ws_mgr, fuzzer::FuzzerContractDB& contract_db, const Tx& tx, - [[maybe_unused]] const std::vector& public_data_writes) + [[maybe_unused]] const std::vector& public_data_writes, + [[maybe_unused]] const std::vector& note_hashes) { - // Note: public_data_writes are already applied to C++ world state in setup_fuzzer_state + // Note: public_data_writes and note_hashes are already applied to C++ world state in setup_fuzzer_state const PublicSimulatorConfig config{ .skip_fee_enforcement = false, @@ -153,11 +156,12 @@ SimulatorResult JsSimulator::simulate( [[maybe_unused]] fuzzer::FuzzerWorldStateManager& ws_mgr, fuzzer::FuzzerContractDB& contract_db, const Tx& tx, - const std::vector& public_data_writes) + const std::vector& public_data_writes, + const std::vector& note_hashes) { auto globals = create_default_globals(); - std::string serialized = serialize_simulation_request(tx, globals, contract_db, public_data_writes); + std::string serialized = serialize_simulation_request(tx, globals, contract_db, public_data_writes, note_hashes); // Send the request process.write_line(serialized); diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/simulator.hpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/simulator.hpp index 97c1c2a02512..540fffcd6ee9 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/simulator.hpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/simulator.hpp @@ -27,9 +27,17 @@ struct FuzzerSimulationRequest { std::vector> contract_instances; // Public data tree writes to apply before simulation (e.g., for bytecode upgrades) std::vector public_data_writes; - - MSGPACK_CAMEL_CASE_FIELDS( - ws_data_dir, ws_map_size_kb, tx, globals, contract_classes, contract_instances, public_data_writes); + // Note hashes to be applied before simulation + std::vector note_hashes; + + MSGPACK_CAMEL_CASE_FIELDS(ws_data_dir, + ws_map_size_kb, + tx, + globals, + contract_classes, + contract_instances, + public_data_writes, + note_hashes); }; struct SimulatorResult { @@ -53,17 +61,18 @@ class Simulator { fuzzer::FuzzerWorldStateManager& ws_mgr, fuzzer::FuzzerContractDB& contract_db, const Tx& tx, - const std::vector& public_data_writes) = 0; + const std::vector& public_data_writes, + const std::vector& note_hashes) = 0; }; /// @brief uses barretenberg/vm2 to simulate the bytecode class CppSimulator : public Simulator { public: - SimulatorResult simulate( - fuzzer::FuzzerWorldStateManager& ws_mgr, - fuzzer::FuzzerContractDB& contract_db, - const Tx& tx, - const std::vector& public_data_writes) override; + SimulatorResult simulate(fuzzer::FuzzerWorldStateManager& ws_mgr, + fuzzer::FuzzerContractDB& contract_db, + const Tx& tx, + const std::vector& public_data_writes, + const std::vector& note_hashes) override; }; /// @brief uses the yarn-project/simulator to simulate the bytecode @@ -85,11 +94,11 @@ class JsSimulator : public Simulator { static JsSimulator* getInstance(); static void initialize(std::string& simulator_path); - SimulatorResult simulate( - fuzzer::FuzzerWorldStateManager& ws_mgr, - fuzzer::FuzzerContractDB& contract_db, - const Tx& tx, - const std::vector& public_data_writes) override; + SimulatorResult simulate(fuzzer::FuzzerWorldStateManager& ws_mgr, + fuzzer::FuzzerContractDB& contract_db, + const Tx& tx, + const std::vector& public_data_writes, + const std::vector& note_hashes) override; }; GlobalVariables create_default_globals(); diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.cpp index 1d5f8a923cb2..9bdea9293140 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.cpp @@ -11,6 +11,7 @@ #include "barretenberg/avm_fuzzer/fuzz_lib/control_flow.hpp" #include "barretenberg/avm_fuzzer/fuzz_lib/fuzz.hpp" #include "barretenberg/avm_fuzzer/fuzzer_comparison_helper.hpp" +#include "barretenberg/avm_fuzzer/mutations/basic_types/field.hpp" #include "barretenberg/avm_fuzzer/mutations/fuzzer_data.hpp" #include "barretenberg/avm_fuzzer/mutations/tx_data.hpp" #include "barretenberg/avm_fuzzer/mutations/tx_types/gas.hpp" @@ -53,6 +54,8 @@ void setup_fuzzer_state(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB& contr for (const auto& write : tx_data.public_data_writes) { ws_mgr.public_data_write(write); } + + ws_mgr.append_note_hashes(tx_data.note_hashes); } void fund_fee_payer(FuzzerWorldStateManager& ws_mgr, const Tx& tx) @@ -78,7 +81,8 @@ SimulatorResult fuzz_tx(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB& contr try { ws_mgr.checkpoint(); - cpp_result = cpp_simulator.simulate(ws_mgr, contract_db, tx_data.tx, tx_data.public_data_writes); + cpp_result = + cpp_simulator.simulate(ws_mgr, contract_db, tx_data.tx, tx_data.public_data_writes, tx_data.note_hashes); fuzz_info("CppSimulator completed without exception"); fuzz_info("CppSimulator result: ", cpp_result); ws_mgr.revert(); @@ -94,7 +98,8 @@ SimulatorResult fuzz_tx(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB& contr } ws_mgr.checkpoint(); - auto js_result = js_simulator->simulate(ws_mgr, contract_db, tx_data.tx, tx_data.public_data_writes); + auto js_result = + js_simulator->simulate(ws_mgr, contract_db, tx_data.tx, tx_data.public_data_writes, tx_data.note_hashes); // If the results do not match if (!compare_simulator_results(cpp_result, js_result)) { @@ -197,11 +202,9 @@ int fuzz_prover(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB& contract_db, } // Initialize FuzzerTxData with sensible defaults -FuzzerTxData create_default_tx_data(std::mt19937_64& rng, const FuzzerContext& context) +FuzzerTxData create_default_tx_data(std::mt19937_64& rng, FuzzerContext& context) { - FuzzerData fuzzer_data = generate_fuzzer_data(rng, context); FuzzerTxData tx_data = { - .input_programs = { fuzzer_data }, .tx = create_default_tx(MSG_SENDER, MSG_SENDER, {}, TRANSACTION_FEE, IS_STATIC_CALL, GAS_LIMIT), .global_variables = { .chain_id = CHAIN_ID, .version = VERSION, @@ -213,11 +216,21 @@ FuzzerTxData create_default_tx_data(std::mt19937_64& rng, const FuzzerContext& c .gas_fees = GasFees{ .fee_per_da_gas = FEE_PER_DA_GAS, .fee_per_l2_gas = FEE_PER_L2_GAS } }, .protocol_contracts = {}, + // We can write proper mutations for this. However it might not be of much value since we have variability from + // nonrevertible note hashes. + .note_hashes = { generate_random_field(rng), generate_random_field(rng), generate_random_field(rng) } }; + + // TODO(alvaro): This is messy, we mutate when creating default tx data. Maybe we should just remove the mutation + // from generate_fuzzer_data altogether. + populate_context_from_tx_data(context, tx_data); + FuzzerData fuzzer_data = generate_fuzzer_data(rng, context); + tx_data.input_programs.push_back(fuzzer_data); + return tx_data; } -FuzzerTxData create_default_tx_data(const FuzzerContext& context) +FuzzerTxData create_default_tx_data(FuzzerContext& context) { std::mt19937_64 rng(0); return create_default_tx_data(rng, context); @@ -272,6 +285,8 @@ size_t mutate_tx_data(FuzzerContext& context, tx_data = create_default_tx_data(rng, context); } + populate_context_from_tx_data(context, tx_data); + // Mutate the fuzzer data multiple times for better bytecode variety auto num_mutations = std::uniform_int_distribution(1, 5)(rng); for (uint8_t i = 0; i < num_mutations; i++) { @@ -374,3 +389,28 @@ size_t mutate_tx_data(FuzzerContext& context, return mutated_serialized_fuzzer_data_size; } + +void populate_context_from_tx_data(FuzzerContext& context, const FuzzerTxData& tx_data) +{ + std::vector> note_hash_leaf_index_pairs; + note_hash_leaf_index_pairs.reserve(tx_data.note_hashes.size() + + tx_data.tx.non_revertible_accumulated_data.note_hashes.size()); + + // For note hashes we assume we start from an empty tree. We could read size everytime but it'd slow things down. + // If you're having trouble with that check initialization on FuzzerWorldStateManager::initialize() + uint64_t leaf_offset = 0; + + for (uint64_t i = 0; i < tx_data.note_hashes.size(); ++i) { + note_hash_leaf_index_pairs.push_back({ tx_data.note_hashes[i], leaf_offset + i }); + } + + uint64_t padding_leaves = MAX_NOTE_HASHES_PER_TX - (tx_data.note_hashes.size() % MAX_NOTE_HASHES_PER_TX); + leaf_offset += tx_data.note_hashes.size() + padding_leaves; + + for (uint64_t i = 0; i < tx_data.tx.non_revertible_accumulated_data.note_hashes.size(); ++i) { + note_hash_leaf_index_pairs.push_back( + { tx_data.tx.non_revertible_accumulated_data.note_hashes[i], leaf_offset + i }); + } + + context.set_existing_note_hashes(note_hash_leaf_index_pairs); +} diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.hpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.hpp index 8592a9aa118c..ad8b8b77c163 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.hpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.hpp @@ -33,6 +33,7 @@ struct FuzzerTxData { // Public data tree writes to be applied during state setup (e.g., for bytecode upgrades) std::vector public_data_writes; + std::vector note_hashes; MSGPACK_FIELDS(input_programs, contract_classes, @@ -41,7 +42,8 @@ struct FuzzerTxData { tx, global_variables, protocol_contracts, - public_data_writes); + public_data_writes, + note_hashes); }; inline std::ostream& operator<<(std::ostream& os, const FuzzerTxData& data) @@ -78,8 +80,8 @@ constexpr FuzzerTxDataMutationConfig FUZZER_TX_DATA_MUTATION_CONFIGURATION = Fuz ContractArtifacts build_bytecode_and_artifacts(FuzzerData& fuzzer_data); // Create a default FuzzerTxData with sensible defaults -FuzzerTxData create_default_tx_data(std::mt19937_64& rng, const FuzzerContext& context); -FuzzerTxData create_default_tx_data(const FuzzerContext& context); +FuzzerTxData create_default_tx_data(std::mt19937_64& rng, FuzzerContext& context); +FuzzerTxData create_default_tx_data(FuzzerContext& context); // Setup fuzzer state: register contracts and addresses in the world state void setup_fuzzer_state(bb::avm2::fuzzer::FuzzerWorldStateManager& ws_mgr, @@ -108,4 +110,6 @@ size_t mutate_tx_data(FuzzerContext& context, size_t max_size, unsigned int seed); +void populate_context_from_tx_data(FuzzerContext& context, const FuzzerTxData& tx_data); + bool compare_cpp_simulator_results(const std::vector& results); diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/configuration.hpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/configuration.hpp index bcc1195cf302..27f2e2b4cb51 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/configuration.hpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/configuration.hpp @@ -392,11 +392,10 @@ constexpr EmitNoteHashMutationConfig BASIC_EMITNOTEHASH_MUTATION_CONFIGURATION = { EmitNoteHashMutationOptions::note_hash, 1 }, }); -enum class NoteHashExistsMutationOptions { notehash_index, notehash_address, leaf_index_address, result_address }; -using NoteHashExistsMutationConfig = WeightedSelectionConfig; +enum class NoteHashExistsMutationOptions { notehash_address, leaf_index_address, result_address }; +using NoteHashExistsMutationConfig = WeightedSelectionConfig; constexpr NoteHashExistsMutationConfig BASIC_NOTEHASHEXISTS_MUTATION_CONFIGURATION = NoteHashExistsMutationConfig({ - { NoteHashExistsMutationOptions::notehash_index, 1 }, { NoteHashExistsMutationOptions::notehash_address, 1 }, { NoteHashExistsMutationOptions::leaf_index_address, 1 }, { NoteHashExistsMutationOptions::result_address, 1 }, diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/instructions/instruction.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/instructions/instruction.cpp index c36276809372..81a3c55f6439 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/instructions/instruction.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/instructions/instruction.cpp @@ -238,10 +238,7 @@ std::vector InstructionMutator::generate_instruction(std::mt199 return { EMITNOTEHASH_Instruction{ .note_hash_address = generate_address_ref(rng, MAX_16BIT_OPERAND), .note_hash = generate_random_field(rng) } }; case InstructionGenerationOptions::NOTEHASHEXISTS: - return { NOTEHASHEXISTS_Instruction{ .notehash_index = generate_random_uint16(rng), - .notehash_address = generate_address_ref(rng, MAX_16BIT_OPERAND), - .leaf_index_address = generate_address_ref(rng, MAX_16BIT_OPERAND), - .result_address = generate_address_ref(rng, MAX_16BIT_OPERAND) } }; + return generate_notehashexists_instruction(rng); case InstructionGenerationOptions::CALLDATACOPY: return { CALLDATACOPY_Instruction{ .dst_address = generate_address_ref(rng, MAX_16BIT_OPERAND), .copy_size = generate_random_uint8(rng), @@ -879,6 +876,37 @@ std::vector InstructionMutator::generate_getcontractinstance_in return instructions; } +std::vector InstructionMutator::generate_notehashexists_instruction(std::mt19937_64& rng) +{ + bool use_backfill = std::uniform_int_distribution(0, 4)(rng) != 0; + if (!use_backfill) { + return { NOTEHASHEXISTS_Instruction{ .notehash_address = generate_variable_ref(rng), + .leaf_index_address = generate_variable_ref(rng), + .result_address = generate_address_ref(rng, MAX_16BIT_OPERAND) } }; + } + auto existing_note_hash = context.get_existing_note_hash(generate_random_uint16(rng)); + FF note_hash = existing_note_hash.has_value() ? existing_note_hash.value().first : generate_random_field(rng); + uint64_t leaf_index = + existing_note_hash.has_value() ? existing_note_hash.value().second : generate_random_uint64(rng); + AddressRef note_hash_address = generate_address_ref(rng, MAX_16BIT_OPERAND); + AddressRef leaf_index_address = generate_address_ref(rng, MAX_16BIT_OPERAND); + + std::vector instructions; + instructions.reserve(3); + + instructions.push_back(SET_FF_Instruction{ + .value_tag = bb::avm2::MemoryTag::FF, .result_address = note_hash_address, .value = note_hash }); + instructions.push_back(SET_64_Instruction{ + .value_tag = bb::avm2::MemoryTag::U64, .result_address = leaf_index_address, .value = leaf_index }); + + instructions.push_back( + NOTEHASHEXISTS_Instruction{ .notehash_address = note_hash_address, + .leaf_index_address = leaf_index_address, + .result_address = generate_address_ref(rng, MAX_16BIT_OPERAND) }); + + return instructions; +} + void InstructionMutator::mutate_param_ref(ParamRef& param, std::mt19937_64& rng, std::optional default_tag, @@ -1215,14 +1243,11 @@ void InstructionMutator::mutate_note_hash_exists_instruction(NOTEHASHEXISTS_Inst { NoteHashExistsMutationOptions option = BASIC_NOTEHASHEXISTS_MUTATION_CONFIGURATION.select(rng); switch (option) { - case NoteHashExistsMutationOptions::notehash_index: - mutate_uint16_t(instruction.notehash_index, rng, BASIC_UINT16_T_MUTATION_CONFIGURATION); - break; case NoteHashExistsMutationOptions::notehash_address: - mutate_address_ref(instruction.notehash_address, rng, MAX_16BIT_OPERAND); + mutate_param_ref(instruction.notehash_address, rng, MemoryTag::FF, MAX_16BIT_OPERAND); break; case NoteHashExistsMutationOptions::leaf_index_address: - mutate_address_ref(instruction.leaf_index_address, rng, MAX_16BIT_OPERAND); + mutate_param_ref(instruction.leaf_index_address, rng, MemoryTag::U64, MAX_16BIT_OPERAND); break; case NoteHashExistsMutationOptions::result_address: mutate_address_ref(instruction.result_address, rng, MAX_16BIT_OPERAND); diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/instructions/instruction.hpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/instructions/instruction.hpp index 589f9ee58956..e07bb9c53e58 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/instructions/instruction.hpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/mutations/instructions/instruction.hpp @@ -46,6 +46,7 @@ class InstructionMutator { std::vector generate_emitunencryptedlog_instruction(std::mt19937_64& rng); std::vector generate_call_instruction(std::mt19937_64& rng); std::vector generate_getcontractinstance_instruction(std::mt19937_64& rng); + std::vector generate_notehashexists_instruction(std::mt19937_64& rng); void mutate_variable_ref(VariableRef& variable, std::mt19937_64& rng, std::optional default_tag); void mutate_address_ref(AddressRef& address, std::mt19937_64& rng, uint32_t max_operand_value); diff --git a/yarn-project/simulator/src/public/fuzzing/avm_fuzzer_simulator.ts b/yarn-project/simulator/src/public/fuzzing/avm_fuzzer_simulator.ts index 40ca179cb68d..bac801cd87ad 100644 --- a/yarn-project/simulator/src/public/fuzzing/avm_fuzzer_simulator.ts +++ b/yarn-project/simulator/src/public/fuzzing/avm_fuzzer_simulator.ts @@ -41,6 +41,7 @@ export class FuzzerSimulationRequest { public readonly contractClasses: any[], // Raw, processed by addContractClassFromCpp public readonly contractInstances: [any, any][], // Raw pairs [address, instance] public readonly publicDataWrites: any[], // Raw public data tree writes to apply before simulation + public readonly noteHashes: any[], // Raw note hashes to apply before simulation ) {} static fromPlainObject(obj: any): FuzzerSimulationRequest { @@ -55,6 +56,7 @@ export class FuzzerSimulationRequest { obj.contractClasses, obj.contractInstances, obj.publicDataWrites ?? [], + obj.noteHashes ?? [], ); } } @@ -250,4 +252,14 @@ export class AvmFuzzerSimulator extends BaseAvmSimulationTester { await this.merkleTrees.sequentialInsert(MerkleTreeId.PUBLIC_DATA_TREE, [leaf.toBuffer()]); } } + + /** + * Apply note hashes from C++ raw msgpack data. + * This is used to pre-populate the note hash tree before simulation. + */ + public async applyNoteHashes(rawNoteHashes: any[]): Promise { + const paddingLeaves = MAX_NOTE_HASHES_PER_TX - (rawNoteHashes.length % MAX_NOTE_HASHES_PER_TX); + const paddedNoteHashes = [...rawNoteHashes, ...Array(paddingLeaves).fill(Fr.ZERO)]; + await this.merkleTrees.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, paddedNoteHashes); + } } diff --git a/yarn-project/simulator/src/public/fuzzing/avm_simulator_bin.ts b/yarn-project/simulator/src/public/fuzzing/avm_simulator_bin.ts index 95cfa07c4e03..423aeb9e95e0 100644 --- a/yarn-project/simulator/src/public/fuzzing/avm_simulator_bin.ts +++ b/yarn-project/simulator/src/public/fuzzing/avm_simulator_bin.ts @@ -56,6 +56,7 @@ async function simulateWithFuzzer( rawContractClasses: any[], // Replace these when we are moving contract classes to TS rawContractInstances: [any, any][], // Replace these when we are moving contract instances to TS rawPublicDataWrites: any[], // Public data tree writes to apply before simulation + rawNoteHashes: any[], // Note hashes to apply before simulation ): Promise<{ reverted: boolean; output: Fr[]; revertReason?: string; publicInputs: AvmCircuitPublicInputs }> { const worldStateService = await openExistingWorldState(dataDir, mapSizeKb); @@ -64,6 +65,8 @@ async function simulateWithFuzzer( // Apply public data writes before simulation (e.g., for bytecode upgrades) await simulator.applyPublicDataWrites(rawPublicDataWrites); + await simulator.applyNoteHashes(rawNoteHashes); + // Register contract classes from C++ for (const rawClass of rawContractClasses) { await simulator.addContractClassFromCpp(rawClass); @@ -104,6 +107,7 @@ async function execute(base64Line: string): Promise { request.contractClasses, request.contractInstances, request.publicDataWrites, + request.noteHashes, ); // Serialize the result to msgpack and encode it in base64 for output From 223656f7182ce779fc8aed4000372b9420f2337c Mon Sep 17 00:00:00 2001 From: sirasistant Date: Thu, 15 Jan 2026 11:20:30 +0000 Subject: [PATCH 2/4] fix tests --- .../avm_fuzzer/fuzz_lib/fuzz.test.cpp | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzz.test.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzz.test.cpp index ce8b4efa795a..6eb296962ba9 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzz.test.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzz.test.cpp @@ -43,9 +43,18 @@ class FuzzTest : public ::testing::Test { void TearDown() override { ws_mgr->reset_world_state(); } SimulatorResult simulate_with_default_tx(std::vector& bytecode, std::vector calldata) + { + return simulate_with_default_tx(bytecode, calldata, {}); + } + + SimulatorResult simulate_with_default_tx(std::vector& bytecode, + std::vector calldata, + const std::vector& note_hashes) { ws_mgr->checkpoint(); + ws_mgr->append_note_hashes(note_hashes); + auto contract_address = context.register_contract_from_bytecode(bytecode); FuzzerContractDB contract_db = context.get_contract_db(); @@ -55,7 +64,7 @@ class FuzzTest : public ::testing::Test { ws_mgr->write_fee_payer_balance(tx.fee_payer, fee_required_da + fee_required_l2); auto cpp_simulator = CppSimulator(); - auto result = cpp_simulator.simulate(*ws_mgr, contract_db, tx); + auto result = cpp_simulator.simulate(*ws_mgr, contract_db, tx, {}, {}); ws_mgr->revert(); @@ -1187,28 +1196,29 @@ TEST_F(FuzzTest, EmitNullifierThenNullifierExistsOverwritingPreviousNullifier) TEST_F(FuzzTest, EmitNoteHashThenNoteHashExists) { - auto emit_note_hash_instruction = - EMITNOTEHASH_Instruction{ .note_hash_address = AddressRef{ .address = 0, .mode = AddressingMode::Direct }, - .note_hash = 1 }; + FF note_hash = 42; + uint64_t leaf_index = 0; + auto set_note_hash_instruction = + SET_FF_Instruction{ .value_tag = bb::avm2::MemoryTag::FF, + .result_address = AddressRef{ .address = 0, .mode = AddressingMode::Direct }, + .value = note_hash }; + auto set_leaf_index_instruction = + SET_64_Instruction{ .value_tag = bb::avm2::MemoryTag::U64, + .result_address = AddressRef{ .address = 1, .mode = AddressingMode::Direct }, + .value = leaf_index }; auto note_hash_exists_instruction = - NOTEHASHEXISTS_Instruction{ .notehash_index = 0, - .notehash_address = AddressRef{ .address = 0, .mode = AddressingMode::Direct }, + NOTEHASHEXISTS_Instruction{ .notehash_address = AddressRef{ .address = 0, .mode = AddressingMode::Direct }, .leaf_index_address = AddressRef{ .address = 1, .mode = AddressingMode::Direct }, .result_address = AddressRef{ .address = 2, .mode = AddressingMode::Direct } }; auto instruction_blocks = std::vector{ InstructionBlock{ - .instructions = { emit_note_hash_instruction, note_hash_exists_instruction } } }; + .instructions = { set_note_hash_instruction, set_leaf_index_instruction, note_hash_exists_instruction } } }; auto control_flow = ControlFlow(instruction_blocks); control_flow.process_cfg_instruction(InsertSimpleInstructionBlock{ .instruction_block_idx = 0 }); auto bytecode = control_flow.build_bytecode( ReturnOptions{ .return_size = 1, .return_value_tag = bb::avm2::MemoryTag::U1, .return_value_offset_index = 0 }); - auto result = simulate_with_default_tx(bytecode, {}); + auto result = simulate_with_default_tx(bytecode, {}, { note_hash }); EXPECT_FALSE(result.reverted); - // TODO(defkit): fix notehashexists - // Right now we cannot know the contract address during bytecode construction - // because contract address depends on bytecode commitment - // So we cannot compute actual unique_note_hash for NOTEHASHEXISTS instruction - // - // EXPECT_EQ(result.output.at(0), 1); + EXPECT_EQ(result.output.at(0), 1); } } // namespace notes_and_nullifiers From bd3212f83fa1add07c85e8c3d9a00326487a6d9e Mon Sep 17 00:00:00 2001 From: sirasistant Date: Thu, 15 Jan 2026 11:29:17 +0000 Subject: [PATCH 3/4] remove print --- .../cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp index 9de850a85dd8..2ddf80e64ee8 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp @@ -195,8 +195,6 @@ void FuzzerWorldStateManager::initialize_world_state() /*thread_pool_size=*/4, DATA_DIR, MAP_SIZE_KB, tree_heights, tree_prefill, initial_header_generator_point); fork_ids.push(ws->create_fork(std::nullopt)); - std::cout << "Items of note hash tree after creation: " - << ws->get_tree_info(get_current_revision(), MerkleTreeId::NOTE_HASH_TREE).meta.size << std::endl; } WorldStateRevision FuzzerWorldStateManager::get_current_revision() const From 38c6b5f2414712ab75315210d849276f2acfc038 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Thu, 15 Jan 2026 11:30:17 +0000 Subject: [PATCH 4/4] remove extraneous import --- .../cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp b/barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp index 2ddf80e64ee8..c3ac164c7fa9 100644 --- a/barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp +++ b/barretenberg/cpp/src/barretenberg/avm_fuzzer/common/interfaces/dbs.cpp @@ -6,7 +6,6 @@ #include "barretenberg/avm_fuzzer/fuzz_lib/constants.hpp" #include "barretenberg/common/throw_or_abort.hpp" #include "barretenberg/crypto/merkle_tree/indexed_tree/indexed_leaf.hpp" -#include "barretenberg/crypto/merkle_tree/response.hpp" #include "barretenberg/crypto/poseidon2/poseidon2.hpp" #include "barretenberg/vm2/common/aztec_constants.hpp" #include "barretenberg/vm2/common/aztec_types.hpp"