Skip to content

Commit 8443e1a

Browse files
committed
refactor: add in-memory MemoryMerkleDB and drop world_state from vm2
Adds a self-contained in-memory MemoryMerkleDB (LowLevelMerkleDBInterface) and migrates vm2 simulation, the public-tx test tester, and the AVM fuzzer's FuzzerWorldStateManager off the on-disk world_state onto it (prod continues to use the WSDB-IPC-backed DB). This leaves the AVM fuzzer + vm2 test paths free of world_state, which the native-packages extraction depends on.
1 parent 9ca4f31 commit 8443e1a

47 files changed

Lines changed: 1127 additions & 668 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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

Lines changed: 11 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
using namespace bb::avm2::simulation;
1616
using Poseidon2 = bb::crypto::Poseidon2<bb::crypto::Poseidon2Bn254ScalarFieldParams>;
17-
using namespace bb::world_state;
1817

1918
namespace bb::avm2::fuzzer {
2019

@@ -185,50 +184,23 @@ FuzzerWorldStateManager* FuzzerWorldStateManager::instance = nullptr;
185184

186185
void FuzzerWorldStateManager::initialize_world_state()
187186
{
188-
std::unordered_map<simulation::MerkleTreeId, uint32_t> tree_heights{
189-
{ simulation::MerkleTreeId::NULLIFIER_TREE, NULLIFIER_TREE_HEIGHT },
190-
{ simulation::MerkleTreeId::NOTE_HASH_TREE, NOTE_HASH_TREE_HEIGHT },
191-
{ simulation::MerkleTreeId::PUBLIC_DATA_TREE, PUBLIC_DATA_TREE_HEIGHT },
192-
{ simulation::MerkleTreeId::L1_TO_L2_MESSAGE_TREE, L1_TO_L2_MSG_TREE_HEIGHT },
193-
{ simulation::MerkleTreeId::ARCHIVE, ARCHIVE_HEIGHT },
194-
};
195-
std::unordered_map<simulation::MerkleTreeId, index_t> tree_prefill{
196-
{ simulation::MerkleTreeId::NULLIFIER_TREE, 128 },
197-
{ simulation::MerkleTreeId::PUBLIC_DATA_TREE, 128 },
198-
};
199-
uint32_t initial_header_generator_point = 2064783670; // DomainSeparator.BLOCK_HEADER_HASH
200-
ws = std::make_unique<world_state::WorldState>(
201-
/*thread_pool_size=*/4, DATA_DIR, MAP_SIZE_KB, tree_heights, tree_prefill, initial_header_generator_point);
202-
203-
fork_ids.push(ws->create_fork(std::nullopt));
187+
mem_db = std::make_unique<simulation::MemoryMerkleDB>(
188+
/*nullifier_tree_prefill=*/128, /*public_data_tree_prefill=*/128);
204189
}
205190

206-
WorldStateRevision FuzzerWorldStateManager::get_current_revision() const
191+
void FuzzerWorldStateManager::reseed_to_genesis()
207192
{
208-
return WorldStateRevision{ .forkId = fork_ids.top(), .includeUncommitted = true };
193+
// Called once per transaction, before applying that transaction's genesis state, so each input starts
194+
// from the same state.
195+
initialize_world_state();
209196
}
210197

211-
WorldStateRevision FuzzerWorldStateManager::fork()
212-
{
213-
auto fork_id = ws->create_fork(std::nullopt);
214-
fork_ids.push(fork_id);
215-
return WorldStateRevision{ .forkId = fork_id, .includeUncommitted = true };
216-
}
217-
void FuzzerWorldStateManager::reset_world_state()
218-
{
219-
// We keep the initial fork, so pop until only one remains
220-
while (fork_ids.size() != 1) {
221-
ws->delete_fork(fork_ids.top());
222-
fork_ids.pop();
223-
}
224-
}
225198
void FuzzerWorldStateManager::register_contract_address(const AztecAddress& contract_address)
226199
{
227200
NullifierLeafValue contract_nullifier =
228201
unconstrained_silo_nullifier(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, contract_address);
229202
fuzz_info("Registering contract address in world state: ", contract_nullifier.nullifier);
230-
auto fork_id = fork_ids.top();
231-
ws->insert_indexed_leaves<NullifierLeafValue>(MerkleTreeId::NULLIFIER_TREE, { contract_nullifier }, fork_id);
203+
mem_db->insert_indexed_leaves_nullifier_tree(contract_nullifier);
232204
}
233205

234206
void FuzzerWorldStateManager::write_fee_payer_balance(const AztecAddress& fee_payer, const FF& balance)
@@ -240,25 +212,20 @@ void FuzzerWorldStateManager::write_fee_payer_balance(const AztecAddress& fee_pa
240212
Poseidon2::hash({ DOM_SEP__PUBLIC_STORAGE_MAP_SLOT, FEE_JUICE_BALANCES_SLOT, fee_payer });
241213
FF leaf_slot = Poseidon2::hash({ DOM_SEP__PUBLIC_LEAF_SLOT, FF(FEE_JUICE_ADDRESS), fee_juice_balance_slot });
242214

243-
// Write to public data tree using current fork
244-
auto fork_id = fork_ids.top();
245-
ws->update_public_data(PublicDataLeafValue(leaf_slot, balance), fork_id);
215+
mem_db->insert_indexed_leaves_public_data_tree(PublicDataLeafValue(leaf_slot, balance));
246216
}
247217

248218
void FuzzerWorldStateManager::public_data_write(const bb::crypto::merkle_tree::PublicDataLeafValue& public_data)
249219
{
250-
auto fork_id = fork_ids.top();
251-
ws->update_public_data(public_data, fork_id);
220+
mem_db->insert_indexed_leaves_public_data_tree(public_data);
252221
}
253222

254223
void FuzzerWorldStateManager::append_note_hashes(const std::vector<FF>& note_hashes)
255224
{
256-
auto fork_id = fork_ids.top();
257-
258225
uint64_t padding_leaves = MAX_NOTE_HASHES_PER_TX - (note_hashes.size() % MAX_NOTE_HASHES_PER_TX);
259226

260-
ws->append_leaves(MerkleTreeId::NOTE_HASH_TREE, note_hashes, fork_id);
261-
ws->append_leaves(MerkleTreeId::NOTE_HASH_TREE, std::vector<FF>(padding_leaves, FF(0)), fork_id);
227+
mem_db->append_leaves(simulation::MerkleTreeId::NOTE_HASH_TREE, note_hashes);
228+
mem_db->append_leaves(simulation::MerkleTreeId::NOTE_HASH_TREE, std::vector<FF>(padding_leaves, FF(0)));
262229
}
263230

264231
} // namespace bb::avm2::fuzzer

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

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
#include <memory>
44
#include <stack>
55

6+
#include "barretenberg/crypto/merkle_tree/indexed_tree/indexed_leaf.hpp"
67
#include "barretenberg/vm2/common/aztec_types.hpp"
78
#include "barretenberg/vm2/simulation/interfaces/db.hpp"
8-
#include "barretenberg/world_state/types.hpp"
9-
#include "barretenberg/world_state/world_state.hpp"
9+
#include "barretenberg/vm2/simulation/lib/memory_merkle_db.hpp"
1010

1111
namespace bb::avm2::fuzzer {
1212

@@ -55,20 +55,13 @@ class FuzzerContractDB : public simulation::ContractDBInterface {
5555
std::stack<Checkpoint> checkpoints;
5656
};
5757

58-
// Set up and manage a world state for the fuzzer, the plan is to use this to set up different world states
59-
// This is a bit of hack since we need to access the world state in both cpp and ts. Normally, ws is instantiated
60-
// inside ts and we use napi to access it from cpp, but for the fuzzer we want to instantiate it in cpp and access it
61-
// from ts. The simplest way is to use the same database files from both cpp and ts, this is fine for now since we know
62-
// only one thing will be writing to it at a time.
63-
// FIXME(ilyas): This won't work with multiple concurrent fuzzing processes, but that's ok for now.
58+
// Seeds and manages the genesis state the fuzzer simulates against. The C++ simulator runs against a copy
59+
// of the in-memory MemoryMerkleDB seeded here, so the seed is never mutated by simulation. The genesis
60+
// uses a fixed 128 nullifier/public-data tree prefill so every input starts from the same state, with no
61+
// shared on-disk database.
6462
class FuzzerWorldStateManager {
6563
public:
66-
// Shared constants for C++ and TypeScript to use the same database
67-
// Note: TypeScript expects trees in {DATA_DIR}/world_state/, so we include that subdirectory
68-
static constexpr const char* DATA_DIR = "/tmp/avm_fuzzer_ws/world_state";
69-
static constexpr uint64_t MAP_SIZE_KB = 10240; // 10 MB
70-
71-
// Static singleton instance management
64+
// Static instance management (similar to JsSimulator pattern)
7265
static void initialize()
7366
{
7467
if (instance == nullptr) {
@@ -85,33 +78,26 @@ class FuzzerWorldStateManager {
8578
return instance;
8679
}
8780

88-
void reset_world_state();
8981
void register_contract_address(const AztecAddress& contract_address);
9082
void write_fee_payer_balance(const AztecAddress& fee_payer, const FF& balance);
9183
void public_data_write(const bb::crypto::merkle_tree::PublicDataLeafValue& public_data);
9284
void append_note_hashes(const std::vector<FF>& note_hashes);
9385

94-
world_state::WorldStateRevision get_current_revision() const;
95-
world_state::WorldStateRevision fork();
96-
world_state::WorldState& get_world_state() { return *ws; }
97-
98-
void checkpoint() { ws->checkpoint(fork_ids.top()); }
99-
100-
void commit() { ws->commit_checkpoint(fork_ids.top()); }
101-
102-
void revert() { ws->revert_checkpoint(fork_ids.top()); }
103-
104-
static const char* get_data_dir() { return DATA_DIR; }
86+
// Re-seeds the in-memory merkle DB to a fresh genesis. The fuzzer calls this once per transaction
87+
// (before applying that transaction's genesis state) so each input starts from the same state.
88+
void reseed_to_genesis();
10589

106-
static uint64_t get_map_size_kb() { return MAP_SIZE_KB; }
90+
// The in-memory merkle DB holds the genesis state plus every mutation applied for the current
91+
// transaction. The C++ simulator runs against a copy of this DB so the genesis state is preserved
92+
// across the fast and hint-collecting simulations.
93+
const simulation::MemoryMerkleDB& get_memory_merkle_db() const { return *mem_db; }
10794

10895
private:
10996
static FuzzerWorldStateManager* instance;
11097

11198
void initialize_world_state();
11299

113-
std::unique_ptr<world_state::WorldState> ws;
114-
std::stack<uint64_t> fork_ids;
100+
std::unique_ptr<simulation::MemoryMerkleDB> mem_db;
115101
};
116102

117103
} // namespace bb::avm2::fuzzer

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,11 @@ class FuzzTest : public ::testing::Test {
3434
if (ws_mgr == nullptr) {
3535
ws_mgr = FuzzerWorldStateManager::getInstance();
3636
}
37-
ws_mgr->fork();
37+
ws_mgr->reseed_to_genesis();
3838
context = FuzzerContext();
3939
register_functions(context);
4040
}
4141

42-
void TearDown() override { ws_mgr->reset_world_state(); }
43-
4442
SimulatorResult simulate_with_default_tx(std::vector<uint8_t>& bytecode, std::vector<FF> calldata)
4543
{
4644
return simulate_with_default_tx(bytecode, calldata, {});
@@ -50,8 +48,6 @@ class FuzzTest : public ::testing::Test {
5048
std::vector<FF> calldata,
5149
const std::vector<FF>& note_hashes)
5250
{
53-
ws_mgr->checkpoint();
54-
5551
ws_mgr->append_note_hashes(note_hashes);
5652

5753
auto contract_address = context.register_contract_from_bytecode(bytecode);
@@ -72,8 +68,6 @@ class FuzzTest : public ::testing::Test {
7268
/*note_hashes=*/{},
7369
/*protocol_contracts=*/{});
7470

75-
ws_mgr->revert();
76-
7771
return result;
7872
}
7973

barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzz_lib/simulator.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "barretenberg/avm_fuzzer/fuzz_lib/simulator.hpp"
2+
#include <thread>
23

34
#include <cstdint>
45
#include <vector>
@@ -16,14 +17,11 @@
1617
#include "barretenberg/vm2/simulation/lib/contract_crypto.hpp"
1718
#include "barretenberg/vm2/simulation/lib/serialization.hpp"
1819
#include "barretenberg/vm2/simulation_helper.hpp"
19-
#include "barretenberg/world_state/types.hpp"
20-
#include "barretenberg/world_state/world_state.hpp"
2120

2221
using bb::avm2::GlobalVariables;
2322
using namespace bb::avm2;
2423
using namespace bb::avm2::simulation;
2524
using namespace bb::avm2::fuzzer;
26-
using namespace bb::world_state;
2725

2826
constexpr auto MAX_RETURN_DATA_SIZE_IN_FIELDS = 1024;
2927

@@ -62,12 +60,12 @@ SimulatorResult CppSimulator::simulate(
6260
},
6361
};
6462

65-
WorldState& ws = ws_mgr.get_world_state();
66-
WorldStateRevision ws_rev = ws_mgr.get_current_revision();
67-
63+
// Run against a fresh copy of the genesis-seeded in-memory merkle DB so the simulation's tree
64+
// mutations don't leak into later simulations of the same transaction.
65+
simulation::MemoryMerkleDB merkle_db = ws_mgr.get_memory_merkle_db();
6866
AvmSimulationHelper helper;
6967
TxSimulationResult result =
70-
helper.simulate_fast_with_existing_ws(contract_db, ws_rev, ws, config, tx, globals, protocol_contracts);
68+
helper.simulate_fast_internal(contract_db, merkle_db, config, tx, globals, protocol_contracts);
7169
bool reverted = result.revert_code != RevertCode::OK;
7270
// Just process the top level call's output
7371
vinfo(

barretenberg/cpp/src/barretenberg/avm_fuzzer/fuzzer_lib.cpp

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "barretenberg/avm_fuzzer/fuzzer_lib.hpp"
2+
#include "barretenberg/serialize/msgpack_impl.hpp"
23

34
#include <cstdint>
45
#include <string>
@@ -26,9 +27,9 @@
2627
#include "barretenberg/vm2/tooling/stats.hpp"
2728
#include "barretenberg/vm2/tracegen_helper.hpp"
2829

30+
using namespace bb;
2931
using namespace bb::avm2::fuzzer;
3032
using namespace bb::avm2::simulation;
31-
using namespace bb::world_state;
3233

3334
extern size_t LLVMFuzzerMutate(uint8_t* Data, size_t Size, size_t MaxSize);
3435

@@ -104,7 +105,6 @@ SimulatorResult fuzz_tx(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB& contr
104105
SimulatorResult cpp_result;
105106

106107
try {
107-
ws_mgr.checkpoint();
108108
cpp_result = cpp_simulator.simulate(ws_mgr,
109109
contract_db,
110110
tx_data.tx,
@@ -114,7 +114,6 @@ SimulatorResult fuzz_tx(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB& contr
114114
tx_data.protocol_contracts);
115115
fuzz_info("CppSimulator completed without exception");
116116
fuzz_info("CppSimulator result: ", cpp_result);
117-
ws_mgr.revert();
118117
} catch (const std::exception& e) {
119118
fuzz_info("CppSimulator threw an exception: ", e.what());
120119
cpp_result = SimulatorResult{
@@ -123,23 +122,20 @@ SimulatorResult fuzz_tx(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB& contr
123122
.end_tree_snapshots = TreeSnapshots(),
124123
.revert_reason = e.what(),
125124
};
126-
ws_mgr.revert();
127125
}
128126

129127
return cpp_result;
130128
}
131129

132130
/// @brief Run the prover fuzzer: fast simulation, hint collection, comparison, and check_circuit
133-
/// @param ws_mgr The world state manager (should already be forked)
131+
/// @param ws_mgr The world state manager (should already be reseeded to genesis)
134132
/// @param contract_db The contract database
135133
/// @param tx_data The transaction data
136134
/// @returns the simulation result
137135
/// @throws An exception if simulation results differ or check_circuit fails
138136
TxSimulationResult fuzz_prover(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB& contract_db, FuzzerTxData& tx_data)
139137
{
140138
ProtocolContracts& protocol_contracts = tx_data.protocol_contracts;
141-
WorldState& ws = ws_mgr.get_world_state();
142-
WorldStateRevision ws_rev = ws_mgr.get_current_revision();
143139
AvmSimulationHelper helper;
144140

145141
// Reset stats for this iteration
@@ -158,29 +154,32 @@ TxSimulationResult fuzz_prover(FuzzerWorldStateManager& ws_mgr, FuzzerContractDB
158154
.collect_public_inputs = true,
159155
};
160156

161-
// 1. Run simulate_fast_with_existing_ws
162-
// It is the only one that may throw, so we wrap it in try-catch. If it fails, we do not proceed
157+
// Each simulation mutates its merkle DB, so we run each against a fresh copy of the
158+
// genesis-seeded in-memory merkle DB to keep them all starting from the same state.
159+
160+
// 1. Run fast simulation against the in-memory merkle DB.
161+
// It is the only one that may throw, so we wrap it in try-catch. If it fails, we do not proceed.
163162
TxSimulationResult fast_result;
164163
try {
165-
ws_mgr.checkpoint();
166-
fast_result = AVM_TRACK_TIME_V(
167-
"fuzzer/simulate_fast",
168-
helper.simulate_fast_with_existing_ws(
169-
contract_db, ws_rev, ws, sim_fast_config, tx_data.tx, tx_data.global_variables, protocol_contracts));
170-
ws_mgr.revert();
164+
simulation::MemoryMerkleDB fast_merkle_db = ws_mgr.get_memory_merkle_db();
165+
fast_result = AVM_TRACK_TIME_V("fuzzer/simulate_fast",
166+
helper.simulate_fast_internal(contract_db,
167+
fast_merkle_db,
168+
sim_fast_config,
169+
tx_data.tx,
170+
tx_data.global_variables,
171+
protocol_contracts));
171172
} catch (const std::exception& e) {
172-
ws_mgr.revert();
173-
fuzz_info("simulate_fast_with_existing_ws threw an exception: ", e.what());
173+
fuzz_info("simulate_fast_internal threw an exception: ", e.what());
174174
return {};
175175
}
176176

177-
// 2. Run simulate_for_hint_collection
178-
ws_mgr.checkpoint();
177+
// 2. Run hint-collecting simulation against a fresh copy of the in-memory merkle DB.
178+
simulation::MemoryMerkleDB hint_merkle_db = ws_mgr.get_memory_merkle_db();
179179
TxSimulationResult hint_result = AVM_TRACK_TIME_V(
180180
"fuzzer/simulate_hints",
181-
helper.simulate_for_hint_collection(
182-
contract_db, ws_rev, ws, sim_hint_config, tx_data.tx, tx_data.global_variables, protocol_contracts));
183-
ws_mgr.revert();
181+
helper.simulate_for_hint_collection_internal(
182+
contract_db, hint_merkle_db, sim_hint_config, tx_data.tx, tx_data.global_variables, protocol_contracts));
184183

185184
// 2a. Construct proving inputs from hint result
186185
bb::avm2::AvmProvingInputs proving_inputs{

barretenberg/cpp/src/barretenberg/avm_fuzzer/harness/emit_public_log.fuzzer.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -305,15 +305,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
305305
if (!error) {
306306
// TODO(MW): use below to check values:
307307
// auto public_logs = side_effect_tracker.get_side_effects().public_logs;
308-
trace.set(avm2::Column::public_inputs_cols_0_, pi_row, log_fields.size());
309-
trace.set(avm2::Column::public_inputs_sel, pi_row, 1);
308+
trace.set(bb::avm2::Column::public_inputs_cols_0_, pi_row, log_fields.size());
309+
trace.set(bb::avm2::Column::public_inputs_sel, pi_row, 1);
310310

311311
// Set public input columns
312312
for (FF log_field : log_fields) {
313313
pi_row++;
314-
trace.set(avm2::Column::public_inputs_sel, pi_row, 1);
314+
trace.set(bb::avm2::Column::public_inputs_sel, pi_row, 1);
315315
// Logs only use cols_0
316-
trace.set(avm2::Column::public_inputs_cols_0_, pi_row, log_field);
316+
trace.set(bb::avm2::Column::public_inputs_cols_0_, pi_row, log_field);
317317
}
318318
}
319319

@@ -330,10 +330,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
330330
.inputs = { MemoryValue::from<uint32_t>(input.log_size) },
331331
.after_context_event = fill_context_event(context) };
332332
ex_builder.process({ ex_event }, trace);
333-
auto exec_log_row = trace.get_column_rows(avm2::Column::execution_sel_exec_dispatch_emit_public_log);
334-
trace.set(avm2::Column::execution_rop_1_, exec_log_row - 1, input.log_offset);
335-
trace.set(avm2::Column::execution_register_0_, exec_log_row - 1, input.log_size);
336-
trace.set(avm2::Column::execution_sel_opcode_error, exec_log_row - 1, error ? 1 : 0);
333+
auto exec_log_row = trace.get_column_rows(bb::avm2::Column::execution_sel_exec_dispatch_emit_public_log);
334+
trace.set(bb::avm2::Column::execution_rop_1_, exec_log_row - 1, input.log_offset);
335+
trace.set(bb::avm2::Column::execution_register_0_, exec_log_row - 1, input.log_size);
336+
trace.set(bb::avm2::Column::execution_sel_opcode_error, exec_log_row - 1, error ? 1 : 0);
337337

338338
range_check_builder.process(context_helper.range_check_emitter.dump_events(), trace);
339339
field_gt_builder.process(context_helper.field_gt_emitter.dump_events(), trace);

0 commit comments

Comments
 (0)