Skip to content

Commit 0302b85

Browse files
authored
feat: merge-train/spartan-v5 (#24606)
BEGIN_COMMIT_OVERRIDE docs(e2e): update READMEs for the consolidated suite layout (#24518) test(e2e): instrument and diagnose bot suite setup cost (#24534) perf(e2e): warp dead waits in multi-node recovery and proving tests (#24566) perf(e2e): shrink e2e slot times (#24570) perf(e2e): seed BananaFPC fee juice at genesis instead of bridging (#24564) perf(bot): parallelize independent bot factory setup steps (#24581) feat(world-state): support prefilled nullifiers in genesis state (#24567) perf(e2e): seed standard contracts at genesis (#24568) feat(prover): revive cancelled proving jobs from a persisted aborted state (#24578) chore: add writing-e2e-tests skill (#24597) perf(e2e): overlap and batch setup txs in e2e harnesses (#24569) docs: revert threat model for node (#24465) (#24610) fix(sol): use pinned compiler for bb sol build (#24620) END_COMMIT_OVERRIDE
2 parents a0c11ef + 3adb172 commit 0302b85

58 files changed

Lines changed: 1516 additions & 614 deletions

File tree

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/nodejs_module/world_state/world_state.cpp

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ WorldStateWrapper::WorldStateWrapper(const Napi::CallbackInfo& info)
4848
std::unordered_map<MerkleTreeId, uint32_t> tree_height;
4949
std::unordered_map<MerkleTreeId, index_t> tree_prefill;
5050
std::vector<PublicDataLeafValue> prefilled_public_data;
51+
std::vector<bb::fr> prefilled_nullifiers;
5152
std::vector<MerkleTreeId> tree_ids{
5253
MerkleTreeId::NULLIFIER_TREE, MerkleTreeId::NOTE_HASH_TREE, MerkleTreeId::PUBLIC_DATA_TREE,
5354
MerkleTreeId::L1_TO_L2_MESSAGE_TREE, MerkleTreeId::ARCHIVE,
@@ -112,15 +113,36 @@ WorldStateWrapper::WorldStateWrapper(const Napi::CallbackInfo& info)
112113
throw Napi::TypeError::New(env, "Prefilled public data must be an array");
113114
}
114115

115-
size_t initial_header_generator_point_index = 4;
116+
size_t prefilled_nullifiers_index = 4;
117+
if (info.Length() > prefilled_nullifiers_index && info[prefilled_nullifiers_index].IsArray()) {
118+
Napi::Array arr = info[prefilled_nullifiers_index].As<Napi::Array>();
119+
for (uint32_t i = 0; i < arr.Length(); ++i) {
120+
if (!arr.Get(i).IsBuffer()) {
121+
throw Napi::TypeError::New(env, "Prefilled nullifier value must be a buffer");
122+
}
123+
Napi::Buffer<uint8_t> nullifier_buf = arr.Get(i).As<Napi::Buffer<uint8_t>>();
124+
if (nullifier_buf.Length() != 32) {
125+
throw Napi::TypeError::New(env, "Prefilled nullifier value must be a 32-byte buffer");
126+
}
127+
uint256_t nullifier = 0;
128+
for (size_t j = 0; j < 32; ++j) {
129+
nullifier = (nullifier << 8) | nullifier_buf[j];
130+
}
131+
prefilled_nullifiers.emplace_back(nullifier);
132+
}
133+
} else {
134+
throw Napi::TypeError::New(env, "Prefilled nullifiers must be an array");
135+
}
136+
137+
size_t initial_header_generator_point_index = 5;
116138
if (info.Length() > initial_header_generator_point_index && info[initial_header_generator_point_index].IsNumber()) {
117139
initial_header_generator_point = info[initial_header_generator_point_index].As<Napi::Number>().Uint32Value();
118140
} else {
119141
throw Napi::TypeError::New(env, "Header generator point needs to be a number");
120142
}
121143

122144
uint64_t genesis_timestamp = 0;
123-
size_t genesis_timestamp_index = 5;
145+
size_t genesis_timestamp_index = 6;
124146
if (info.Length() > genesis_timestamp_index) {
125147
if (info[genesis_timestamp_index].IsNumber()) {
126148
genesis_timestamp = static_cast<uint64_t>(info[genesis_timestamp_index].As<Napi::Number>().Int64Value());
@@ -130,7 +152,7 @@ WorldStateWrapper::WorldStateWrapper(const Napi::CallbackInfo& info)
130152
}
131153

132154
// optional parameters
133-
size_t map_size_index = 6;
155+
size_t map_size_index = 7;
134156
if (info.Length() > map_size_index) {
135157
if (info[map_size_index].IsObject()) {
136158
Napi::Object obj = info[map_size_index].As<Napi::Object>();
@@ -160,7 +182,7 @@ WorldStateWrapper::WorldStateWrapper(const Napi::CallbackInfo& info)
160182
}
161183
}
162184

163-
size_t thread_pool_size_index = 7;
185+
size_t thread_pool_size_index = 8;
164186
if (info.Length() > thread_pool_size_index) {
165187
if (!info[thread_pool_size_index].IsNumber()) {
166188
throw Napi::TypeError::New(env, "Thread pool size must be a number");
@@ -173,7 +195,7 @@ WorldStateWrapper::WorldStateWrapper(const Napi::CallbackInfo& info)
173195
// commits never block on fsync, files stay sparse, and a crash mid-write yields an
174196
// unrecoverable env. Intended for throwaway scratch state (TXE test sessions).
175197
bool ephemeral = false;
176-
size_t ephemeral_index = 8;
198+
size_t ephemeral_index = 9;
177199
if (info.Length() > ephemeral_index) {
178200
if (!info[ephemeral_index].IsBoolean()) {
179201
throw Napi::TypeError::New(env, "Ephemeral flag must be a boolean");
@@ -187,6 +209,7 @@ WorldStateWrapper::WorldStateWrapper(const Napi::CallbackInfo& info)
187209
tree_height,
188210
tree_prefill,
189211
prefilled_public_data,
212+
prefilled_nullifiers,
190213
initial_header_generator_point,
191214
genesis_timestamp,
192215
ephemeral);

barretenberg/cpp/src/barretenberg/world_state/world_state.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ WorldState::WorldState(uint64_t thread_pool_size,
3939
const std::unordered_map<MerkleTreeId, uint32_t>& tree_heights,
4040
const std::unordered_map<MerkleTreeId, index_t>& tree_prefill,
4141
const std::vector<PublicDataLeafValue>& prefilled_public_data,
42+
const std::vector<bb::fr>& prefilled_nullifiers,
4243
uint32_t initial_header_generator_point,
4344
uint64_t genesis_timestamp,
4445
bool ephemeral)
@@ -51,7 +52,7 @@ WorldState::WorldState(uint64_t thread_pool_size,
5152
{
5253
// We set the max readers to be high, at least the number of given threads or the default if higher
5354
uint64_t maxReaders = std::max(thread_pool_size, DEFAULT_MIN_NUMBER_OF_READERS);
54-
create_canonical_fork(data_dir, map_size, prefilled_public_data, maxReaders, ephemeral);
55+
create_canonical_fork(data_dir, map_size, prefilled_public_data, prefilled_nullifiers, maxReaders, ephemeral);
5556
try {
5657
attempt_tree_resync();
5758
} catch (std::exception& e) {
@@ -73,6 +74,7 @@ WorldState::WorldState(uint64_t thread_pool_size,
7374
tree_heights,
7475
tree_prefill,
7576
std::vector<PublicDataLeafValue>(),
77+
std::vector<bb::fr>(),
7678
initial_header_generator_point,
7779
genesis_timestamp,
7880
ephemeral)
@@ -84,6 +86,7 @@ WorldState::WorldState(uint64_t thread_pool_size,
8486
const std::unordered_map<MerkleTreeId, uint32_t>& tree_heights,
8587
const std::unordered_map<MerkleTreeId, index_t>& tree_prefill,
8688
const std::vector<PublicDataLeafValue>& prefilled_public_data,
89+
const std::vector<bb::fr>& prefilled_nullifiers,
8790
uint32_t initial_header_generator_point,
8891
uint64_t genesis_timestamp,
8992
bool ephemeral)
@@ -99,6 +102,7 @@ WorldState::WorldState(uint64_t thread_pool_size,
99102
tree_heights,
100103
tree_prefill,
101104
prefilled_public_data,
105+
prefilled_nullifiers,
102106
initial_header_generator_point,
103107
genesis_timestamp,
104108
ephemeral)
@@ -118,6 +122,7 @@ WorldState::WorldState(uint64_t thread_pool_size,
118122
tree_heights,
119123
tree_prefill,
120124
std::vector<PublicDataLeafValue>(),
125+
std::vector<bb::fr>(),
121126
initial_header_generator_point,
122127
genesis_timestamp,
123128
ephemeral)
@@ -126,6 +131,7 @@ WorldState::WorldState(uint64_t thread_pool_size,
126131
void WorldState::create_canonical_fork(const std::string& dataDir,
127132
const std::unordered_map<MerkleTreeId, uint64_t>& dbSize,
128133
const std::vector<PublicDataLeafValue>& prefilled_public_data,
134+
const std::vector<bb::fr>& prefilled_nullifiers,
129135
uint64_t maxReaders,
130136
bool ephemeral)
131137
{
@@ -148,9 +154,15 @@ void WorldState::create_canonical_fork(const std::string& dataDir,
148154
{
149155
uint32_t levels = _tree_heights.at(MerkleTreeId::NULLIFIER_TREE);
150156
index_t initial_size = _initial_tree_size.at(MerkleTreeId::NULLIFIER_TREE);
157+
std::vector<NullifierLeafValue> prefilled_nullifier_leaves;
158+
prefilled_nullifier_leaves.reserve(prefilled_nullifiers.size());
159+
for (const auto& nullifier : prefilled_nullifiers) {
160+
prefilled_nullifier_leaves.emplace_back(nullifier);
161+
}
151162
auto store = std::make_unique<NullifierStore>(
152163
getMerkleTreeName(MerkleTreeId::NULLIFIER_TREE), levels, _persistentStores->nullifierStore);
153-
auto tree = std::make_unique<NullifierTree>(std::move(store), _workers, initial_size);
164+
auto tree =
165+
std::make_unique<NullifierTree>(std::move(store), _workers, initial_size, prefilled_nullifier_leaves);
154166
fork->_trees.insert({ MerkleTreeId::NULLIFIER_TREE, TreeWithStore(std::move(tree)) });
155167
}
156168
{

barretenberg/cpp/src/barretenberg/world_state/world_state.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,15 @@ class WorldState {
8282
const std::unordered_map<MerkleTreeId, uint32_t>& tree_heights,
8383
const std::unordered_map<MerkleTreeId, index_t>& tree_prefill,
8484
const std::vector<PublicDataLeafValue>& prefilled_public_data,
85+
const std::vector<bb::fr>& prefilled_nullifiers,
8586
uint32_t initial_header_generator_point,
8687
uint64_t genesis_timestamp = 0,
8788
bool ephemeral = false);
8889

8990
/**
91+
* @param prefilled_nullifiers Nullifier leaves to pre-insert into the genesis nullifier tree (e.g. the protocol
92+
* contract registration nullifiers). Must be unique and strictly increasing in field value, and
93+
* distinct from the padding leaves implied by the nullifier tree prefill size.
9094
* @param ephemeral When true, every underlying LMDB env opens with `MDB_NOSYNC |
9195
* MDB_NOMETASYNC`. Commits return without waiting for fsync; the kernel
9296
* flushes lazily, files stay sparse. Intended for throwaway scratch
@@ -99,6 +103,7 @@ class WorldState {
99103
const std::unordered_map<MerkleTreeId, uint32_t>& tree_heights,
100104
const std::unordered_map<MerkleTreeId, index_t>& tree_prefill,
101105
const std::vector<PublicDataLeafValue>& prefilled_public_data,
106+
const std::vector<bb::fr>& prefilled_nullifiers,
102107
uint32_t initial_header_generator_point,
103108
uint64_t genesis_timestamp = 0,
104109
bool ephemeral = false);
@@ -326,6 +331,7 @@ class WorldState {
326331
void create_canonical_fork(const std::string& dataDir,
327332
const std::unordered_map<MerkleTreeId, uint64_t>& dbSize,
328333
const std::vector<PublicDataLeafValue>& prefilled_public_data,
334+
const std::vector<bb::fr>& prefilled_nullifiers,
329335
uint64_t maxReaders,
330336
bool ephemeral);
331337

barretenberg/cpp/src/barretenberg/world_state/world_state.test.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,57 @@ TEST_F(WorldStateTest, GetInitialTreeInfoForAllTrees)
211211
}
212212
}
213213

214+
TEST_F(WorldStateTest, GetInitialTreeInfoWithPrefilledNullifiers)
215+
{
216+
// Prefilled nullifier leaves must be unique and strictly increasing, and larger than the padding leaves that fill
217+
// the initial 128-leaf prefill region (whose keys are the low integers 0..127), so we use full-size field values.
218+
std::vector<bb::fr> prefilled_nullifiers = {
219+
bb::fr("0x073b5e41abe9d7f8466bca9c81c9572b558f953bbd70081317f6a80ac65f3dd5"),
220+
bb::fr("0x0d99507b7ecac720c73bf197a0e7366a5ed80c1c1b0afe8ff8c6ecc7b5a7aefe"),
221+
bb::fr("0x1c0bf82e0c51834780e61ef091b17e3a1d39ae891db7a70bfdb5221f134996ac"),
222+
};
223+
224+
std::string data_dir_prefilled = random_temp_directory();
225+
std::filesystem::create_directories(data_dir_prefilled);
226+
227+
WorldState ws_prefilled(thread_pool_size,
228+
data_dir_prefilled,
229+
map_size,
230+
tree_heights,
231+
tree_prefill,
232+
std::vector<PublicDataLeafValue>(),
233+
prefilled_nullifiers,
234+
initial_header_generator_point);
235+
236+
// Baseline world state with no prefilled nullifiers (the canonical empty genesis).
237+
WorldState ws(thread_pool_size, data_dir, map_size, tree_heights, tree_prefill, initial_header_generator_point);
238+
239+
auto prefilled = ws_prefilled.get_tree_info(WorldStateRevision::committed(), MerkleTreeId::NULLIFIER_TREE);
240+
auto info = ws.get_tree_info(WorldStateRevision::committed(), MerkleTreeId::NULLIFIER_TREE);
241+
242+
// The prefilled nullifiers occupy the last slots of the 128-leaf initial prefill region (they replace padding
243+
// leaves rather than being appended), so the tree size stays 128 for both.
244+
EXPECT_EQ(prefilled.meta.size, 128);
245+
EXPECT_EQ(info.meta.size, 128);
246+
247+
// Seeding the nullifiers changes the nullifier-tree root away from the empty-genesis baseline.
248+
EXPECT_NE(prefilled.meta.root, info.meta.root);
249+
// The empty-genesis baseline root is unchanged from the canonical value, confirming that a default (empty)
250+
// prefilled-nullifiers list leaves the genesis nullifier-tree root bit-identical to today.
251+
EXPECT_EQ(info.meta.root, bb::fr("0x18935581a8ed73d08ffd00386fba55ba6c89f3ab848a76b8fedfa9034cee0454"));
252+
253+
// The seeded nullifiers are present in the tree.
254+
for (const auto& nullifier : prefilled_nullifiers) {
255+
assert_leaf_exists<NullifierLeafValue>(ws_prefilled,
256+
WorldStateRevision::committed(),
257+
MerkleTreeId::NULLIFIER_TREE,
258+
NullifierLeafValue(nullifier),
259+
true);
260+
}
261+
262+
std::filesystem::remove_all(data_dir_prefilled);
263+
}
264+
214265
TEST_F(WorldStateTest, GetInitialTreeInfoWithPrefilledPublicData)
215266
{
216267
std::string data_dir_prefilled = random_temp_directory();
@@ -225,6 +276,7 @@ TEST_F(WorldStateTest, GetInitialTreeInfoWithPrefilledPublicData)
225276
tree_heights,
226277
tree_prefill,
227278
prefilled_values,
279+
std::vector<bb::fr>(),
228280
initial_header_generator_point);
229281

230282
WorldState ws(thread_pool_size, data_dir, map_size, tree_heights, tree_prefill, initial_header_generator_point);

barretenberg/cpp/src/barretenberg/wsdb/cli.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ int parse_and_run_wsdb(int argc, char* argv[])
8383
msgpack_run_command->add_option(
8484
"--prefilled-public-data", prefilled_public_data_json, "Prefilled public data as JSON array");
8585

86+
// Prefilled nullifiers as JSON array of nullifier_hex strings
87+
std::string prefilled_nullifiers_json;
88+
msgpack_run_command->add_option(
89+
"--prefilled-nullifiers", prefilled_nullifiers_json, "Prefilled genesis nullifiers as JSON array");
90+
8691
uint64_t genesis_timestamp = 0;
8792
msgpack_run_command->add_option("--genesis-timestamp", genesis_timestamp, "Genesis block timestamp (default: 0)");
8893

@@ -121,6 +126,7 @@ int parse_and_run_wsdb(int argc, char* argv[])
121126
threads,
122127
initial_header_generator_point,
123128
prefilled_public_data_json,
129+
prefilled_nullifiers_json,
124130
genesis_timestamp,
125131
request_ring_size,
126132
response_ring_size);

barretenberg/cpp/src/barretenberg/wsdb/wsdb_ipc_server.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,34 @@ static std::vector<PublicDataLeafValue> parse_prefilled_public_data(const std::s
168168
return result;
169169
}
170170

171+
// ---------------------------------------------------------------------------
172+
// Parse prefilled nullifiers from JSON: ["nullifier_hex",...]
173+
// Each hex string is a 64-char (32-byte) hex-encoded field element.
174+
// ---------------------------------------------------------------------------
175+
176+
static std::vector<fr> parse_prefilled_nullifiers(const std::string& json)
177+
{
178+
std::vector<fr> result;
179+
if (json.empty() || json == "[]") {
180+
return result;
181+
}
182+
183+
std::string current;
184+
bool in_string = false;
185+
186+
for (char c : json) {
187+
if (c == '"') {
188+
in_string = !in_string;
189+
} else if (in_string) {
190+
current += c;
191+
} else if ((c == ',' || c == ']') && !current.empty()) {
192+
result.push_back(hex_to_fr(current));
193+
current.clear();
194+
}
195+
}
196+
return result;
197+
}
198+
171199
// ---------------------------------------------------------------------------
172200
// IPC server execution
173201
// ---------------------------------------------------------------------------
@@ -180,6 +208,7 @@ int execute_wsdb_server(const std::string& input_path,
180208
uint32_t threads,
181209
uint32_t initial_header_generator_point,
182210
const std::string& prefilled_public_data_json,
211+
const std::string& prefilled_nullifiers_json,
183212
uint64_t genesis_timestamp,
184213
size_t request_ring_size,
185214
size_t response_ring_size)
@@ -211,6 +240,14 @@ int execute_wsdb_server(const std::string& input_path,
211240
std::cerr << "Parsed " << prefilled_public_data.size() << " prefilled public data entries" << '\n';
212241
}
213242

243+
// Parse prefilled nullifiers: JSON array of "nullifier_hex" strings. The caller (TS world-state) passes the same
244+
// canonical genesis nullifiers it seeds via the napi path, so the IPC genesis nullifier-tree root matches.
245+
std::vector<bb::fr> prefilled_nullifiers;
246+
if (!prefilled_nullifiers_json.empty()) {
247+
prefilled_nullifiers = parse_prefilled_nullifiers(prefilled_nullifiers_json);
248+
std::cerr << "Parsed " << prefilled_nullifiers.size() << " prefilled nullifiers" << '\n';
249+
}
250+
214251
// Create WorldState
215252
std::cerr << "Creating WorldState at " << data_dir << " with " << threads << " threads" << '\n';
216253
auto ws = std::make_unique<WorldState>(threads,
@@ -219,6 +256,7 @@ int execute_wsdb_server(const std::string& input_path,
219256
tree_height,
220257
tree_prefill,
221258
prefilled_public_data,
259+
prefilled_nullifiers,
222260
initial_header_generator_point,
223261
genesis_timestamp);
224262

barretenberg/cpp/src/barretenberg/wsdb/wsdb_ipc_server.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ int execute_wsdb_server(const std::string& input_path,
2020
uint32_t threads,
2121
uint32_t initial_header_generator_point,
2222
const std::string& prefilled_public_data_json,
23+
const std::string& prefilled_nullifiers_json,
2324
uint64_t genesis_timestamp,
2425
size_t request_ring_size,
2526
size_t response_ring_size);

barretenberg/sol/foundry.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ gas_limit = 900000000000000000
99
bytecode_hash = "none"
1010
evm_version = "cancun"
1111

12-
[fuzz]
13-
runs = 2
14-
1512
# Use the pre-downloaded solc binary from l1-contracts. l1-contracts' build is
1613
# the single owner of the svm download; pointing other forge projects at the
1714
# same binary avoids parallel svm downloads racing on ~/.svm.
1815
solc = "../../l1-contracts/solc-0.8.30"
1916

17+
[fuzz]
18+
runs = 2
19+
2020
[lint]
2121
ignore = ["./lib/**"]
2222
exclude_lints = [

0 commit comments

Comments
 (0)