Skip to content

Commit 00fa4a9

Browse files
authored
chore: merge next into merge-train/fairies (resolve conflicts, unblock #24951) (#24965)
1 parent 16933d5 commit 00fa4a9

88 files changed

Lines changed: 3930 additions & 1109 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/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
@@ -65,6 +65,11 @@ int parse_and_run_wsdb(int argc, char* argv[])
6565
msgpack_run_command->add_option(
6666
"--prefilled-public-data", prefilled_public_data_json, "Prefilled public data as JSON array");
6767

68+
// Prefilled nullifiers as JSON array of nullifier_hex strings
69+
std::string prefilled_nullifiers_json;
70+
msgpack_run_command->add_option(
71+
"--prefilled-nullifiers", prefilled_nullifiers_json, "Prefilled genesis nullifiers as JSON array");
72+
6873
uint64_t genesis_timestamp = 0;
6974
msgpack_run_command->add_option("--genesis-timestamp", genesis_timestamp, "Genesis block timestamp (default: 0)");
7075

@@ -98,6 +103,7 @@ int parse_and_run_wsdb(int argc, char* argv[])
98103
threads,
99104
initial_header_generator_point,
100105
prefilled_public_data_json,
106+
prefilled_nullifiers_json,
101107
genesis_timestamp,
102108
request_ring_size,
103109
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
@@ -130,6 +130,34 @@ static std::vector<PublicDataLeafValue> parse_prefilled_public_data(const std::s
130130
return result;
131131
}
132132

133+
// ---------------------------------------------------------------------------
134+
// Parse prefilled nullifiers from JSON: ["nullifier_hex",...]
135+
// Each hex string is a 64-char (32-byte) hex-encoded field element.
136+
// ---------------------------------------------------------------------------
137+
138+
static std::vector<fr> parse_prefilled_nullifiers(const std::string& json)
139+
{
140+
std::vector<fr> result;
141+
if (json.empty() || json == "[]") {
142+
return result;
143+
}
144+
145+
std::string current;
146+
bool in_string = false;
147+
148+
for (char c : json) {
149+
if (c == '"') {
150+
in_string = !in_string;
151+
} else if (in_string) {
152+
current += c;
153+
} else if ((c == ',' || c == ']') && !current.empty()) {
154+
result.push_back(hex_to_fr(current));
155+
current.clear();
156+
}
157+
}
158+
return result;
159+
}
160+
133161
// ---------------------------------------------------------------------------
134162
// IPC server execution
135163
// ---------------------------------------------------------------------------
@@ -142,6 +170,7 @@ int execute_wsdb_server(const std::string& input_path,
142170
uint32_t threads,
143171
uint32_t initial_header_generator_point,
144172
const std::string& prefilled_public_data_json,
173+
const std::string& prefilled_nullifiers_json,
145174
uint64_t genesis_timestamp,
146175
size_t request_ring_size,
147176
size_t response_ring_size)
@@ -173,6 +202,14 @@ int execute_wsdb_server(const std::string& input_path,
173202
std::cerr << "Parsed " << prefilled_public_data.size() << " prefilled public data entries" << '\n';
174203
}
175204

205+
// Parse prefilled nullifiers: JSON array of "nullifier_hex" strings. The caller (TS world-state) passes the same
206+
// canonical genesis nullifiers it seeds via the napi path, so the IPC genesis nullifier-tree root matches.
207+
std::vector<bb::fr> prefilled_nullifiers;
208+
if (!prefilled_nullifiers_json.empty()) {
209+
prefilled_nullifiers = parse_prefilled_nullifiers(prefilled_nullifiers_json);
210+
std::cerr << "Parsed " << prefilled_nullifiers.size() << " prefilled nullifiers" << '\n';
211+
}
212+
176213
// Create WorldState
177214
std::cerr << "Creating WorldState at " << data_dir << " with " << threads << " threads" << '\n';
178215
auto ws = std::make_unique<WorldState>(threads,
@@ -181,6 +218,7 @@ int execute_wsdb_server(const std::string& input_path,
181218
tree_height,
182219
tree_prefill,
183220
prefilled_public_data,
221+
prefilled_nullifiers,
184222
initial_header_generator_point,
185223
genesis_timestamp);
186224

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/ts/bb.js/src/bb_backends/node/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export async function createAsyncBackend(
2626
throw new Error('Native backend requires bb binary.');
2727
}
2828
logger(`Using native Unix socket backend: ${bbPath}`);
29-
return new BarretenbergNativeSocketAsyncBackend(bbPath, options.threads, options.logger, options.unref);
29+
return await BarretenbergNativeSocketAsyncBackend.new(bbPath, options.threads, options.logger, options.unref);
3030
}
3131

3232
case BackendType.NativeSharedMemory: {
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { jest } from '@jest/globals';
2+
import * as fs from 'fs';
3+
import * as os from 'os';
4+
import * as path from 'path';
5+
6+
import { BarretenbergNativeSocketAsyncBackend } from './native_socket.js';
7+
8+
jest.setTimeout(30_000);
9+
10+
// Echo server speaking the bb msgpack socket protocol (4-byte LE length prefix), started after
11+
// an optional delay to simulate bb's startup time on a loaded machine.
12+
const ECHO_SERVER_JS = `
13+
const net = require('net');
14+
const socketPath = process.argv[2];
15+
const server = net.createServer(sock => {
16+
let buf = Buffer.alloc(0);
17+
sock.on('data', d => {
18+
buf = Buffer.concat([buf, d]);
19+
while (buf.length >= 4) {
20+
const len = buf.readUInt32LE(0);
21+
if (buf.length < 4 + len) break;
22+
const payload = buf.subarray(4, 4 + len);
23+
const out = Buffer.alloc(4);
24+
out.writeUInt32LE(payload.length, 0);
25+
sock.write(out);
26+
sock.write(payload);
27+
buf = buf.subarray(4 + len);
28+
}
29+
});
30+
});
31+
server.listen(socketPath);
32+
`;
33+
34+
// A fake bb binary: a bash script that optionally sleeps, then runs the echo server on the
35+
// socket path bb receives via `msgpack run --input <path>` ($4).
36+
function writeFakeBb(startupDelaySecs: number): string {
37+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'fake-bb-'));
38+
const serverJs = path.join(dir, 'echo_server.cjs');
39+
fs.writeFileSync(serverJs, ECHO_SERVER_JS);
40+
const file = path.join(dir, 'bb');
41+
const sleep = startupDelaySecs > 0 ? `sleep ${startupDelaySecs}\n` : '';
42+
fs.writeFileSync(file, `#!/bin/bash\n${sleep}exec node ${serverJs} "$4"\n`, { mode: 0o755 });
43+
return file;
44+
}
45+
46+
function writeFakeBbScript(script: string): string {
47+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'fake-bb-'));
48+
const file = path.join(dir, 'bb');
49+
fs.writeFileSync(file, script, { mode: 0o755 });
50+
return file;
51+
}
52+
53+
describe('BarretenbergNativeSocketAsyncBackend', () => {
54+
it('connects and echoes when bb starts promptly', async () => {
55+
const fakeBb = writeFakeBb(0);
56+
const backend = await BarretenbergNativeSocketAsyncBackend.new(fakeBb);
57+
const response = await backend.call(new Uint8Array([1, 2, 3, 4]));
58+
expect(response).toEqual(new Uint8Array([1, 2, 3, 4]));
59+
await backend.destroy();
60+
});
61+
62+
it('connects even when bb takes longer than 5s to create its socket', async () => {
63+
const fakeBb = writeFakeBb(7);
64+
const backend = await BarretenbergNativeSocketAsyncBackend.new(fakeBb);
65+
const response = await backend.call(new Uint8Array([42]));
66+
expect(response).toEqual(new Uint8Array([42]));
67+
await backend.destroy();
68+
});
69+
70+
it('fails with the exit cause when bb dies before creating its socket', async () => {
71+
const fakeBb = writeFakeBbScript(`#!/bin/bash\nexit 17\n`);
72+
await expect(BarretenbergNativeSocketAsyncBackend.new(fakeBb)).rejects.toThrow(
73+
/exited before socket connection was established \(code=17/,
74+
);
75+
});
76+
77+
it('fails with the spawn error when the bb binary does not exist', async () => {
78+
await expect(BarretenbergNativeSocketAsyncBackend.new('/nonexistent/bb-binary')).rejects.toThrow(
79+
/Native backend process error/,
80+
);
81+
});
82+
});

0 commit comments

Comments
 (0)