Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,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<FF>& 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<FF>(padding_leaves, FF(0)), fork_id);
}

} // namespace bb::avm2::fuzzer
Original file line number Diff line number Diff line change
Expand Up @@ -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<FF>& note_hashes);

world_state::WorldStateRevision get_current_revision() const;
world_state::WorldStateRevision fork();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
38 changes: 24 additions & 14 deletions barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/fuzz.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,18 @@ class FuzzTest : public ::testing::Test {
void TearDown() override { ws_mgr->reset_world_state(); }

SimulatorResult simulate_with_default_tx(std::vector<uint8_t>& bytecode, std::vector<FF> calldata)
{
return simulate_with_default_tx(bytecode, calldata, {});
}

SimulatorResult simulate_with_default_tx(std::vector<uint8_t>& bytecode,
std::vector<FF> calldata,
const std::vector<FF>& 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();

Expand All @@ -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();

Expand Down Expand Up @@ -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>{ 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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,24 @@ FF FuzzerContext::register_contract_from_bytecode(const std::vector<uint8_t>& by
return contract_address;
}

std::optional<std::pair<FF, uint64_t>> 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<const std::pair<FF, uint64_t>> note_hashes)
{
existing_note_hashes_.assign(note_hashes.begin(), note_hashes.end());
}

void FuzzerContext::reset()
{
contract_addresses_.clear();
contract_db_ = std::make_unique<FuzzerContractDB>();
existing_note_hashes_.clear();
}

} // namespace bb::avm2::fuzzer
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,14 @@ class FuzzerContext {
/// @brief Get the contract database for simulation
FuzzerContractDB& get_contract_db() const { return *contract_db_; }

std::optional<std::pair<FF, uint64_t>> get_existing_note_hash(size_t index) const;

void set_existing_note_hashes(std::span<const std::pair<FF, uint64_t>> note_hashes);

private:
std::vector<FF> contract_addresses_;
std::unique_ptr<FuzzerContractDB> contract_db_;
std::vector<std::pair<FF, uint64_t>> existing_note_hashes_;
};

} // namespace bb::avm2::fuzzer
Original file line number Diff line number Diff line change
Expand Up @@ -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]] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,27 +315,6 @@ std::optional<bb::avm2::FF> 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<bb::avm2::FF> 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<uint16_t> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ class MemoryManager {
// Public Storage used addresses
std::vector<bb::avm2::FF> storage_addresses;

std::vector<bb::avm2::FF> 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);
Expand Down Expand Up @@ -62,13 +60,5 @@ class MemoryManager {
// Get slot from storage_addresses
std::optional<bb::avm2::FF> 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<bb::avm2::FF> 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<uint16_t> get_leaf_index(uint16_t note_hash_index);

void set_base_offset(uint32_t base_offset);
};
Original file line number Diff line number Diff line change
Expand Up @@ -1070,37 +1070,13 @@ 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)
{
#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<uint64_t>(*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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ std::string serialize_simulation_request(
const Tx& tx,
const GlobalVariables& globals,
const FuzzerContractDB& contract_db,
const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes)
const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes,
const std::vector<FF>& note_hashes)
{
// Build vectors from contract_db
std::vector<ContractClass> classes_vec = contract_db.get_contract_classes();
Expand All @@ -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);
Expand Down Expand Up @@ -79,9 +81,10 @@ SimulatorResult CppSimulator::simulate(
fuzzer::FuzzerWorldStateManager& ws_mgr,
fuzzer::FuzzerContractDB& contract_db,
const Tx& tx,
[[maybe_unused]] const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes)
[[maybe_unused]] const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes,
[[maybe_unused]] const std::vector<FF>& 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,
Expand Down Expand Up @@ -153,11 +156,12 @@ SimulatorResult JsSimulator::simulate(
[[maybe_unused]] fuzzer::FuzzerWorldStateManager& ws_mgr,
fuzzer::FuzzerContractDB& contract_db,
const Tx& tx,
const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes)
const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes,
const std::vector<FF>& 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);
Expand Down
37 changes: 23 additions & 14 deletions barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/simulator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,17 @@ struct FuzzerSimulationRequest {
std::vector<std::pair<AztecAddress, ContractInstance>> contract_instances;
// Public data tree writes to apply before simulation (e.g., for bytecode upgrades)
std::vector<bb::crypto::merkle_tree::PublicDataLeafValue> 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<FF> 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 {
Expand All @@ -53,17 +61,18 @@ class Simulator {
fuzzer::FuzzerWorldStateManager& ws_mgr,
fuzzer::FuzzerContractDB& contract_db,
const Tx& tx,
const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes) = 0;
const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes,
const std::vector<FF>& 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<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes) override;
SimulatorResult simulate(fuzzer::FuzzerWorldStateManager& ws_mgr,
fuzzer::FuzzerContractDB& contract_db,
const Tx& tx,
const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes,
const std::vector<FF>& note_hashes) override;
};

/// @brief uses the yarn-project/simulator to simulate the bytecode
Expand All @@ -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<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes) override;
SimulatorResult simulate(fuzzer::FuzzerWorldStateManager& ws_mgr,
fuzzer::FuzzerContractDB& contract_db,
const Tx& tx,
const std::vector<bb::crypto::merkle_tree::PublicDataLeafValue>& public_data_writes,
const std::vector<FF>& note_hashes) override;
};

GlobalVariables create_default_globals();
Expand Down
Loading
Loading