@aztec/world-state 0.0.1-commit.c2eed6949 → 0.0.1-commit.c52d6e7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dest/index.d.ts +2 -1
  2. package/dest/index.d.ts.map +1 -1
  3. package/dest/index.js +1 -0
  4. package/dest/instrumentation/instrumentation.d.ts +4 -3
  5. package/dest/instrumentation/instrumentation.d.ts.map +1 -1
  6. package/dest/instrumentation/instrumentation.js +4 -9
  7. package/dest/native/index.d.ts +2 -1
  8. package/dest/native/index.d.ts.map +1 -1
  9. package/dest/native/index.js +1 -0
  10. package/dest/native/ipc_world_state_instance.d.ts +89 -0
  11. package/dest/native/ipc_world_state_instance.d.ts.map +1 -0
  12. package/dest/native/ipc_world_state_instance.js +670 -0
  13. package/dest/native/merkle_trees_facade.d.ts +8 -4
  14. package/dest/native/merkle_trees_facade.d.ts.map +1 -1
  15. package/dest/native/merkle_trees_facade.js +60 -115
  16. package/dest/native/message.d.ts +14 -230
  17. package/dest/native/message.d.ts.map +1 -1
  18. package/dest/native/message.js +0 -42
  19. package/dest/native/native_world_state.d.ts +19 -7
  20. package/dest/native/native_world_state.d.ts.map +1 -1
  21. package/dest/native/native_world_state.js +120 -58
  22. package/dest/native/native_world_state_instance.d.ts +56 -42
  23. package/dest/native/native_world_state_instance.d.ts.map +1 -1
  24. package/dest/native/native_world_state_instance.js +1 -203
  25. package/dest/native/world_state_operation.d.ts +7 -0
  26. package/dest/native/world_state_operation.d.ts.map +1 -0
  27. package/dest/native/world_state_operation.js +5 -0
  28. package/dest/synchronizer/config.d.ts +1 -1
  29. package/dest/synchronizer/config.d.ts.map +1 -1
  30. package/dest/synchronizer/config.js +9 -10
  31. package/dest/synchronizer/errors.d.ts +8 -1
  32. package/dest/synchronizer/errors.d.ts.map +1 -1
  33. package/dest/synchronizer/errors.js +8 -1
  34. package/dest/synchronizer/factory.d.ts +4 -4
  35. package/dest/synchronizer/factory.d.ts.map +1 -1
  36. package/dest/synchronizer/factory.js +7 -6
  37. package/dest/synchronizer/index.d.ts +2 -1
  38. package/dest/synchronizer/index.d.ts.map +1 -1
  39. package/dest/synchronizer/index.js +1 -0
  40. package/dest/synchronizer/server_world_state_synchronizer.d.ts +11 -6
  41. package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
  42. package/dest/synchronizer/server_world_state_synchronizer.js +67 -40
  43. package/dest/test/utils.d.ts +1 -1
  44. package/dest/test/utils.d.ts.map +1 -1
  45. package/dest/test/utils.js +6 -1
  46. package/dest/testing.d.ts +4 -3
  47. package/dest/testing.d.ts.map +1 -1
  48. package/dest/testing.js +22 -7
  49. package/package.json +13 -10
  50. package/src/index.ts +1 -0
  51. package/src/instrumentation/instrumentation.ts +6 -18
  52. package/src/native/index.ts +1 -0
  53. package/src/native/ipc_world_state_instance.ts +847 -0
  54. package/src/native/merkle_trees_facade.ts +85 -112
  55. package/src/native/message.ts +13 -297
  56. package/src/native/native_world_state.ts +144 -97
  57. package/src/native/native_world_state_instance.ts +94 -281
  58. package/src/native/world_state_operation.ts +33 -0
  59. package/src/synchronizer/config.ts +14 -10
  60. package/src/synchronizer/errors.ts +8 -0
  61. package/src/synchronizer/factory.ts +11 -10
  62. package/src/synchronizer/index.ts +1 -0
  63. package/src/synchronizer/server_world_state_synchronizer.ts +73 -36
  64. package/src/test/utils.ts +10 -1
  65. package/src/testing.ts +19 -10
  66. package/dest/native/world_state_ops_queue.d.ts +0 -19
  67. package/dest/native/world_state_ops_queue.d.ts.map +0 -1
  68. package/dest/native/world_state_ops_queue.js +0 -146
  69. package/src/native/world_state_ops_queue.ts +0 -190
@@ -0,0 +1,670 @@
1
+ import { ARCHIVE_HEIGHT, DomainSeparator, L1_TO_L2_MSG_TREE_HEIGHT, MAX_NULLIFIERS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NOTE_HASH_TREE_HEIGHT, NULLIFIER_TREE_HEIGHT, PUBLIC_DATA_TREE_HEIGHT } from '@aztec/constants';
2
+ import { createLogger } from '@aztec/foundation/log';
3
+ import { MerkleTreeId } from '@aztec/stdlib/trees';
4
+ import { WsdbService } from '@aztec/wsdb';
5
+ import { cpus } from 'node:os';
6
+ // ————— Request conversion helpers —————
7
+ function toWsdbRevision(rev) {
8
+ return {
9
+ forkId: rev.forkId,
10
+ blockNumber: Number(rev.blockNumber),
11
+ includeUncommitted: rev.includeUncommitted
12
+ };
13
+ }
14
+ function blockStateRefToMap(ref) {
15
+ return [
16
+ ...ref.entries()
17
+ ].map(([treeId, [root, size]])=>({
18
+ treeId,
19
+ root: new Uint8Array(root),
20
+ size: Number(size)
21
+ }));
22
+ }
23
+ function toPublicDataLeaf(leaf) {
24
+ if (leaf instanceof Buffer || !('slot' in leaf)) {
25
+ throw new Error('Expected public data leaf');
26
+ }
27
+ return {
28
+ slot: new Uint8Array(leaf.slot),
29
+ value: new Uint8Array(leaf.value)
30
+ };
31
+ }
32
+ function toNullifierLeaf(leaf) {
33
+ if (leaf instanceof Buffer || !('nullifier' in leaf)) {
34
+ throw new Error('Expected nullifier leaf');
35
+ }
36
+ return {
37
+ nullifier: new Uint8Array(leaf.nullifier)
38
+ };
39
+ }
40
+ function fromPublicDataLeaf(leaf) {
41
+ return {
42
+ slot: Buffer.from(leaf.slot),
43
+ value: Buffer.from(leaf.value)
44
+ };
45
+ }
46
+ function fromNullifierLeaf(leaf) {
47
+ return {
48
+ nullifier: Buffer.from(leaf.nullifier)
49
+ };
50
+ }
51
+ function fromIndexedLeaf(leaf, convertLeaf) {
52
+ return {
53
+ leaf: convertLeaf(leaf.leaf),
54
+ nextIndex: leaf.nextIndex,
55
+ nextKey: Buffer.from(leaf.nextKey)
56
+ };
57
+ }
58
+ function fromLeafUpdateWitnessData(data, convertLeaf) {
59
+ return {
60
+ leaf: fromIndexedLeaf(data.leaf, convertLeaf),
61
+ index: data.index,
62
+ path: data.path.map((p)=>Buffer.from(p))
63
+ };
64
+ }
65
+ function fromBatchInsertionResult(result, convertLeaf) {
66
+ return {
67
+ lowLeafWitnessData: result.lowLeafWitnessData.map((data)=>fromLeafUpdateWitnessData(data, convertLeaf)),
68
+ sortedLeaves: result.sortedLeaves.map(({ leaf, index })=>[
69
+ convertLeaf(leaf),
70
+ index
71
+ ]),
72
+ subtreePath: result.subtreePath.map((p)=>Buffer.from(p))
73
+ };
74
+ }
75
+ function fromSequentialInsertionResult(result, convertLeaf) {
76
+ return {
77
+ lowLeafWitnessData: result.lowLeafWitnessData.map((data)=>fromLeafUpdateWitnessData(data, convertLeaf)),
78
+ insertionWitnessData: result.insertionWitnessData.map((data)=>fromLeafUpdateWitnessData(data, convertLeaf))
79
+ };
80
+ }
81
+ function toFrLeaf(leaf) {
82
+ if (!(leaf instanceof Buffer)) {
83
+ throw new Error('Expected field leaf');
84
+ }
85
+ return new Uint8Array(leaf);
86
+ }
87
+ function formatMap(map) {
88
+ if (!map || Object.keys(map).length === 0) {
89
+ return undefined;
90
+ }
91
+ return `{${Object.entries(map).map(([key, value])=>`${key}:${value}`).join(',')}}`;
92
+ }
93
+ /** SHM request/response ring size for the spawned aztec-wsdb process (32 MiB). */ const WSDB_SHM_RING_SIZE = 32 * 1024 * 1024;
94
+ function getWsdbThreadCount() {
95
+ return Math.min(16, cpus().length);
96
+ }
97
+ function getWsdbExtraArgs(dataDir, wsTreeMapSizes, genesis, threads) {
98
+ const options = getWsdbOptions(dataDir, wsTreeMapSizes);
99
+ const args = [
100
+ '--data-dir',
101
+ dataDir,
102
+ '--threads',
103
+ threads.toString()
104
+ ];
105
+ // Size the SHM rings generously. Responses such as batch-insertion witnesses
106
+ // run to a few MB, and an SHM frame is capped at half the ring (the wrap
107
+ // handling needs that headroom), so 32 MiB rings give ~16 MiB per message.
108
+ // Ignored by the UDS transport. Pages are demand-faulted, so unused capacity
109
+ // is cheap.
110
+ args.push('--request-ring-size', WSDB_SHM_RING_SIZE.toString());
111
+ args.push('--response-ring-size', WSDB_SHM_RING_SIZE.toString());
112
+ const treeHeights = formatMap(options.treeHeights);
113
+ if (treeHeights) {
114
+ args.push('--tree-heights', treeHeights);
115
+ }
116
+ const treePrefill = formatMap(options.treePrefill);
117
+ if (treePrefill) {
118
+ args.push('--tree-prefill', treePrefill);
119
+ }
120
+ const mapSizes = formatMap(options.mapSizes);
121
+ if (mapSizes) {
122
+ args.push('--map-sizes', mapSizes);
123
+ }
124
+ args.push('--initial-header-generator-point', options.initialHeaderGeneratorPoint.toString());
125
+ if (genesis.prefilledPublicData.length > 0) {
126
+ const pairs = genesis.prefilledPublicData.map((data)=>[
127
+ data.slot.toBuffer().toString('hex'),
128
+ data.value.toBuffer().toString('hex')
129
+ ]);
130
+ args.push('--prefilled-public-data', JSON.stringify(pairs));
131
+ }
132
+ const prefilledNullifiers = genesis.prefilledNullifiers ?? [];
133
+ for(let i = 1; i < prefilledNullifiers.length; i++){
134
+ if (prefilledNullifiers[i].toBigInt() <= prefilledNullifiers[i - 1].toBigInt()) {
135
+ throw new Error('Prefilled genesis nullifiers must be unique and strictly increasing');
136
+ }
137
+ }
138
+ if (prefilledNullifiers.length > 0) {
139
+ args.push('--prefilled-nullifiers', JSON.stringify(prefilledNullifiers.map((nullifier)=>nullifier.toBuffer().toString('hex'))));
140
+ }
141
+ const genesisTimestamp = Number(genesis.genesisTimestamp);
142
+ if (genesisTimestamp !== 0) {
143
+ args.push('--genesis-timestamp', genesisTimestamp.toString());
144
+ }
145
+ return args;
146
+ }
147
+ // ————— Response conversion helpers —————
148
+ /** Convert Uint8Array fields to Buffer recursively (for opaque blob responses). */ function convertUint8ArraysToBuffers(obj) {
149
+ if (obj instanceof Uint8Array) {
150
+ return Buffer.from(obj);
151
+ }
152
+ if (Array.isArray(obj)) {
153
+ return obj.map(convertUint8ArraysToBuffers);
154
+ }
155
+ if (obj !== null && typeof obj === 'object') {
156
+ const result = {};
157
+ for (const [key, value] of Object.entries(obj)){
158
+ result[key] = convertUint8ArraysToBuffers(value);
159
+ }
160
+ return result;
161
+ }
162
+ return obj;
163
+ }
164
+ /** Convert Wsdb state reference (Record<number, [Uint8Array, number]>) to NAPI format. */ function convertStateRef(state) {
165
+ const result = {};
166
+ for (const { treeId, root, size } of state){
167
+ result[treeId] = [
168
+ Buffer.from(root),
169
+ BigInt(size)
170
+ ];
171
+ }
172
+ return result;
173
+ }
174
+ /** Convert Wsdb WorldStateStatusSummary to the native world-state format. */ function convertStatusSummary(s) {
175
+ return {
176
+ unfinalizedBlockNumber: s.unfinalizedBlockNumber,
177
+ finalizedBlockNumber: s.finalizedBlockNumber,
178
+ oldestHistoricalBlock: s.oldestHistoricalBlock,
179
+ treesAreSynched: s.treesAreSynched
180
+ };
181
+ }
182
+ function convertDBStats(s) {
183
+ return {
184
+ name: s.name,
185
+ numDataItems: s.numDataItems,
186
+ totalUsedSize: s.totalUsedSize
187
+ };
188
+ }
189
+ function convertTreeDBStats(s) {
190
+ return {
191
+ mapSize: s.mapSize,
192
+ physicalFileSize: s.physicalFileSize,
193
+ blocksDBStats: convertDBStats(s.blocksDBStats),
194
+ nodesDBStats: convertDBStats(s.nodesDBStats),
195
+ leafPreimagesDBStats: convertDBStats(s.leafPreimagesDBStats),
196
+ leafIndicesDBStats: convertDBStats(s.leafIndicesDBStats),
197
+ blockIndicesDBStats: convertDBStats(s.blockIndicesDBStats)
198
+ };
199
+ }
200
+ function convertWorldStateDBStats(s) {
201
+ return {
202
+ noteHashTreeStats: convertTreeDBStats(s.noteHashTreeStats),
203
+ messageTreeStats: convertTreeDBStats(s.messageTreeStats),
204
+ archiveTreeStats: convertTreeDBStats(s.archiveTreeStats),
205
+ publicDataTreeStats: convertTreeDBStats(s.publicDataTreeStats),
206
+ nullifierTreeStats: convertTreeDBStats(s.nullifierTreeStats)
207
+ };
208
+ }
209
+ function convertTreeMeta(m) {
210
+ return {
211
+ name: m.name,
212
+ depth: m.depth,
213
+ size: m.size,
214
+ committedSize: m.committedSize,
215
+ root: m.root,
216
+ initialSize: m.initialSize,
217
+ initialRoot: m.initialRoot,
218
+ oldestHistoricBlock: m.oldestHistoricBlock,
219
+ unfinalizedBlockHeight: m.unfinalizedBlockHeight,
220
+ finalizedBlockHeight: m.finalizedBlockHeight
221
+ };
222
+ }
223
+ function convertWorldStateMeta(m) {
224
+ return {
225
+ noteHashTreeMeta: convertTreeMeta(m.noteHashTreeMeta),
226
+ messageTreeMeta: convertTreeMeta(m.messageTreeMeta),
227
+ archiveTreeMeta: convertTreeMeta(m.archiveTreeMeta),
228
+ publicDataTreeMeta: convertTreeMeta(m.publicDataTreeMeta),
229
+ nullifierTreeMeta: convertTreeMeta(m.nullifierTreeMeta)
230
+ };
231
+ }
232
+ function convertStatusFull(s) {
233
+ return {
234
+ summary: convertStatusSummary(s.summary),
235
+ dbStats: convertWorldStateDBStats(s.dbStats),
236
+ meta: convertWorldStateMeta(s.meta)
237
+ };
238
+ }
239
+ /** Convert Wsdb SiblingPathAndIndex to NAPI format. */ function convertSiblingPathAndIndex(s) {
240
+ if (!s) {
241
+ return undefined;
242
+ }
243
+ return {
244
+ index: BigInt(s.index),
245
+ path: s.path.map((p)=>Buffer.from(p))
246
+ };
247
+ }
248
+ // ————— Public API —————
249
+ /**
250
+ * IPC-backed world state instance.
251
+ * Uses WsdbService (spawns aztec-wsdb binary) and the generated AsyncApi
252
+ * to communicate via the NamedUnion IPC protocol.
253
+ */ export class IpcWorldState {
254
+ wsdb;
255
+ instrumentation;
256
+ log;
257
+ open;
258
+ api;
259
+ /** Tracks checkpoint depth per fork (WSDB IPC doesn't return depth in response). */ checkpointDepths;
260
+ constructor(wsdb, instrumentation, bindings, log = createLogger('world-state:ipc-database', bindings)){
261
+ this.wsdb = wsdb;
262
+ this.instrumentation = instrumentation;
263
+ this.log = log;
264
+ this.open = true;
265
+ this.checkpointDepths = new Map();
266
+ this.api = wsdb;
267
+ this.log.info('Created IPC-backed world state instance');
268
+ }
269
+ /**
270
+ * Spawn an `aztec-wsdb` subprocess and return an IPC-backed world state wrapping it.
271
+ * Encapsulates wsdb binary discovery, service construction, and readiness wait.
272
+ */ static async spawn(dataDir, wsTreeMapSizes, genesis, instrumentation, bindings, threads = getWsdbThreadCount()) {
273
+ const transport = process.env.WSDB_TRANSPORT === 'shm' ? 'shm' : 'uds';
274
+ const wsdb = await WsdbService.spawn({
275
+ transport,
276
+ extraArgs: getWsdbExtraArgs(dataDir, wsTreeMapSizes, genesis, threads),
277
+ env: {
278
+ HARDWARE_CONCURRENCY: threads.toString()
279
+ }
280
+ });
281
+ return new IpcWorldState(wsdb, instrumentation, bindings);
282
+ }
283
+ getIpcPath() {
284
+ return this.wsdb.getIpcPath();
285
+ }
286
+ async close() {
287
+ if (!this.open) {
288
+ return;
289
+ }
290
+ this.open = false;
291
+ await this.wsdb.destroy();
292
+ }
293
+ getTreeInfo(treeId, revision) {
294
+ return this.execute('getTreeInfo', revision.forkId, revision.includeUncommitted === false, async ()=>{
295
+ const resp = await this.api.getTreeInfo({
296
+ treeId,
297
+ revision: toWsdbRevision(revision)
298
+ });
299
+ return {
300
+ treeId: resp.treeId,
301
+ root: Buffer.from(resp.root),
302
+ size: BigInt(resp.size),
303
+ depth: resp.depth
304
+ };
305
+ });
306
+ }
307
+ getStateReference(revision) {
308
+ return this.execute('getStateReference', revision.forkId, revision.includeUncommitted === false, async ()=>{
309
+ const resp = await this.api.getStateReference({
310
+ revision: toWsdbRevision(revision)
311
+ });
312
+ return convertStateRef(resp.state);
313
+ });
314
+ }
315
+ getInitialStateReference() {
316
+ return this.execute('getInitialStateReference', 0, false, async ()=>{
317
+ const resp = await this.api.getInitialStateReference({});
318
+ return convertStateRef(resp.state);
319
+ });
320
+ }
321
+ getLeafValue(treeId, revision, leafIndex) {
322
+ return this.execute('getLeafValue', revision.forkId, revision.includeUncommitted === false, async ()=>{
323
+ const wsdbRevision = toWsdbRevision(revision);
324
+ const wsdbLeafIndex = Number(leafIndex);
325
+ if (treeId === MerkleTreeId.PUBLIC_DATA_TREE) {
326
+ const resp = await this.api.getPublicDataLeafValue({
327
+ revision: wsdbRevision,
328
+ leafIndex: wsdbLeafIndex
329
+ });
330
+ return resp.value ? fromPublicDataLeaf(resp.value) : undefined;
331
+ }
332
+ if (treeId === MerkleTreeId.NULLIFIER_TREE) {
333
+ const resp = await this.api.getNullifierLeafValue({
334
+ revision: wsdbRevision,
335
+ leafIndex: wsdbLeafIndex
336
+ });
337
+ return resp.value ? fromNullifierLeaf(resp.value) : undefined;
338
+ }
339
+ const resp = await this.api.getLeafValue({
340
+ treeId,
341
+ revision: wsdbRevision,
342
+ leafIndex: wsdbLeafIndex
343
+ });
344
+ return resp.value ? Buffer.from(resp.value) : undefined;
345
+ });
346
+ }
347
+ getLeafPreimage(treeId, revision, leafIndex) {
348
+ return this.execute('getLeafPreimage', revision.forkId, revision.includeUncommitted === false, async ()=>{
349
+ const resp = treeId === MerkleTreeId.PUBLIC_DATA_TREE ? await this.api.getPublicDataLeafPreimage({
350
+ revision: toWsdbRevision(revision),
351
+ leafIndex: Number(leafIndex)
352
+ }) : await this.api.getNullifierLeafPreimage({
353
+ revision: toWsdbRevision(revision),
354
+ leafIndex: Number(leafIndex)
355
+ });
356
+ return resp.preimage ? convertUint8ArraysToBuffers(resp.preimage) : undefined;
357
+ });
358
+ }
359
+ getSiblingPath(treeId, revision, leafIndex) {
360
+ return this.execute('getSiblingPath', revision.forkId, revision.includeUncommitted === false, async ()=>{
361
+ const resp = await this.api.getSiblingPath({
362
+ treeId,
363
+ revision: toWsdbRevision(revision),
364
+ leafIndex: Number(leafIndex)
365
+ });
366
+ return resp.path.map((p)=>Buffer.from(p));
367
+ });
368
+ }
369
+ getBlockNumbersForLeafIndices(treeId, revision, leafIndices) {
370
+ return this.execute('getBlockNumbersForLeafIndices', revision.forkId, revision.includeUncommitted === false, async ()=>{
371
+ const resp = await this.api.getBlockNumbersForLeafIndices({
372
+ treeId,
373
+ revision: toWsdbRevision(revision),
374
+ leafIndices: leafIndices.map(Number)
375
+ });
376
+ return resp.blockNumbers.map((n)=>n != null ? BigInt(n) : undefined);
377
+ });
378
+ }
379
+ findLeafIndices(treeId, revision, leaves, startIndex) {
380
+ return this.execute('findLeafIndices', revision.forkId, revision.includeUncommitted === false, async ()=>{
381
+ const wsdbRevision = toWsdbRevision(revision);
382
+ const wsdbStartIndex = Number(startIndex);
383
+ const resp = treeId === MerkleTreeId.PUBLIC_DATA_TREE ? await this.api.findPublicDataLeafIndices({
384
+ revision: wsdbRevision,
385
+ leaves: leaves.map(toPublicDataLeaf),
386
+ startIndex: wsdbStartIndex
387
+ }) : treeId === MerkleTreeId.NULLIFIER_TREE ? await this.api.findNullifierLeafIndices({
388
+ revision: wsdbRevision,
389
+ leaves: leaves.map(toNullifierLeaf),
390
+ startIndex: wsdbStartIndex
391
+ }) : await this.api.findLeafIndices({
392
+ treeId,
393
+ revision: wsdbRevision,
394
+ leaves: leaves.map(toFrLeaf),
395
+ startIndex: wsdbStartIndex
396
+ });
397
+ return resp.indices.map((n)=>n != null ? BigInt(n) : undefined);
398
+ });
399
+ }
400
+ findLowLeaf(treeId, revision, key) {
401
+ return this.execute('findLowLeaf', revision.forkId, revision.includeUncommitted === false, async ()=>{
402
+ const resp = await this.api.findLowLeaf({
403
+ treeId,
404
+ revision: toWsdbRevision(revision),
405
+ key: new Uint8Array(key.toBuffer())
406
+ });
407
+ return {
408
+ alreadyPresent: resp.alreadyPresent,
409
+ index: BigInt(resp.index)
410
+ };
411
+ });
412
+ }
413
+ findSiblingPaths(treeId, revision, leaves) {
414
+ return this.execute('findSiblingPaths', revision.forkId, revision.includeUncommitted === false, async ()=>{
415
+ const wsdbRevision = toWsdbRevision(revision);
416
+ const resp = treeId === MerkleTreeId.PUBLIC_DATA_TREE ? await this.api.findPublicDataSiblingPaths({
417
+ revision: wsdbRevision,
418
+ leaves: leaves.map(toPublicDataLeaf)
419
+ }) : treeId === MerkleTreeId.NULLIFIER_TREE ? await this.api.findNullifierSiblingPaths({
420
+ revision: wsdbRevision,
421
+ leaves: leaves.map(toNullifierLeaf)
422
+ }) : await this.api.findSiblingPaths({
423
+ treeId,
424
+ revision: wsdbRevision,
425
+ leaves: leaves.map(toFrLeaf)
426
+ });
427
+ return resp.paths.map(convertSiblingPathAndIndex);
428
+ });
429
+ }
430
+ updateArchive(forkId, blockStateRef, blockHeaderHash) {
431
+ return this.execute('updateArchive', forkId, false, async ()=>{
432
+ await this.api.updateArchive({
433
+ blockStateRef: blockStateRefToMap(blockStateRef),
434
+ blockHeaderHash: new Uint8Array(blockHeaderHash),
435
+ forkId
436
+ });
437
+ });
438
+ }
439
+ appendLeaves(treeId, forkId, leaves) {
440
+ return this.execute('appendLeaves', forkId, false, async ()=>{
441
+ if (treeId === MerkleTreeId.PUBLIC_DATA_TREE) {
442
+ await this.api.appendPublicDataLeaves({
443
+ leaves: leaves.map(toPublicDataLeaf),
444
+ forkId
445
+ });
446
+ } else if (treeId === MerkleTreeId.NULLIFIER_TREE) {
447
+ await this.api.appendNullifierLeaves({
448
+ leaves: leaves.map(toNullifierLeaf),
449
+ forkId
450
+ });
451
+ } else {
452
+ await this.api.appendLeaves({
453
+ treeId,
454
+ leaves: leaves.map(toFrLeaf),
455
+ forkId
456
+ });
457
+ }
458
+ });
459
+ }
460
+ batchInsert(treeId, forkId, leaves, subtreeDepth) {
461
+ return this.execute('batchInsert', forkId, false, async ()=>{
462
+ const resp = treeId === MerkleTreeId.PUBLIC_DATA_TREE ? await this.api.batchInsertPublicData({
463
+ leaves: leaves.map(toPublicDataLeaf),
464
+ subtreeDepth,
465
+ forkId
466
+ }) : await this.api.batchInsertNullifier({
467
+ leaves: leaves.map(toNullifierLeaf),
468
+ subtreeDepth,
469
+ forkId
470
+ });
471
+ return treeId === MerkleTreeId.PUBLIC_DATA_TREE ? fromBatchInsertionResult(resp.result, fromPublicDataLeaf) : fromBatchInsertionResult(resp.result, fromNullifierLeaf);
472
+ });
473
+ }
474
+ sequentialInsert(treeId, forkId, leaves) {
475
+ return this.execute('sequentialInsert', forkId, false, async ()=>{
476
+ const resp = treeId === MerkleTreeId.PUBLIC_DATA_TREE ? await this.api.sequentialInsertPublicData({
477
+ leaves: leaves.map(toPublicDataLeaf),
478
+ forkId
479
+ }) : await this.api.sequentialInsertNullifier({
480
+ leaves: leaves.map(toNullifierLeaf),
481
+ forkId
482
+ });
483
+ return treeId === MerkleTreeId.PUBLIC_DATA_TREE ? fromSequentialInsertionResult(resp.result, fromPublicDataLeaf) : fromSequentialInsertionResult(resp.result, fromNullifierLeaf);
484
+ });
485
+ }
486
+ syncBlock(input) {
487
+ return this.execute('syncBlock', 0, false, async ()=>{
488
+ const resp = await this.api.syncBlock({
489
+ blockNumber: Number(input.blockNumber),
490
+ blockStateRef: blockStateRefToMap(input.blockStateRef),
491
+ blockHeaderHash: new Uint8Array(input.blockHeaderHash),
492
+ expectedArchiveRoot: new Uint8Array(input.expectedArchiveRoot),
493
+ expectedPreviousArchiveRoot: new Uint8Array(input.expectedPreviousArchiveRoot),
494
+ paddedNoteHashes: input.paddedNoteHashes.map(toFrLeaf),
495
+ paddedL1ToL2Messages: input.paddedL1ToL2Messages.map(toFrLeaf),
496
+ paddedNullifiers: input.paddedNullifiers.map(toNullifierLeaf),
497
+ publicDataWrites: input.publicDataWrites.map(toPublicDataLeaf)
498
+ });
499
+ return convertStatusFull(resp.status);
500
+ });
501
+ }
502
+ createFork(input) {
503
+ return this.execute('createFork', 0, false, async ()=>{
504
+ const resp = await this.api.createFork({
505
+ latest: input.latest,
506
+ blockNumber: Number(input.blockNumber)
507
+ });
508
+ return resp.forkId;
509
+ });
510
+ }
511
+ deleteFork(forkId) {
512
+ return this.execute('deleteFork', forkId, false, async ()=>{
513
+ await this.api.deleteFork({
514
+ forkId
515
+ });
516
+ });
517
+ }
518
+ finalizeBlocks(toBlockNumber) {
519
+ return this.execute('finalizeBlocks', 0, false, async ()=>{
520
+ const resp = await this.api.finalizeBlocks({
521
+ toBlockNumber: Number(toBlockNumber)
522
+ });
523
+ return convertStatusSummary(resp.status);
524
+ });
525
+ }
526
+ unwindBlocks(toBlockNumber) {
527
+ return this.execute('unwindBlocks', 0, false, async ()=>{
528
+ const resp = await this.api.unwindBlocks({
529
+ toBlockNumber: Number(toBlockNumber)
530
+ });
531
+ return convertStatusFull(resp.status);
532
+ });
533
+ }
534
+ removeHistoricalBlocks(toBlockNumber) {
535
+ return this.execute('removeHistoricalBlocks', 0, false, async ()=>{
536
+ const resp = await this.api.removeHistoricalBlocks({
537
+ toBlockNumber: Number(toBlockNumber)
538
+ });
539
+ return convertStatusFull(resp.status);
540
+ });
541
+ }
542
+ getStatus() {
543
+ return this.execute('getStatus', 0, false, async ()=>{
544
+ const resp = await this.api.getStatus({});
545
+ return convertStatusSummary(resp.status);
546
+ });
547
+ }
548
+ createCheckpoint(forkId) {
549
+ return this.execute('createCheckpoint', forkId, false, async ()=>{
550
+ await this.api.createCheckpoint({
551
+ forkId
552
+ });
553
+ const depth = (this.checkpointDepths.get(forkId) ?? 0) + 1;
554
+ this.checkpointDepths.set(forkId, depth);
555
+ return depth;
556
+ });
557
+ }
558
+ commitCheckpoint(forkId) {
559
+ return this.execute('commitCheckpoint', forkId, false, async ()=>{
560
+ await this.api.commitCheckpoint({
561
+ forkId
562
+ });
563
+ const depth = Math.max(0, (this.checkpointDepths.get(forkId) ?? 0) - 1);
564
+ this.checkpointDepths.set(forkId, depth);
565
+ });
566
+ }
567
+ revertCheckpoint(forkId) {
568
+ return this.execute('revertCheckpoint', forkId, false, async ()=>{
569
+ await this.api.revertCheckpoint({
570
+ forkId
571
+ });
572
+ const depth = Math.max(0, (this.checkpointDepths.get(forkId) ?? 0) - 1);
573
+ this.checkpointDepths.set(forkId, depth);
574
+ });
575
+ }
576
+ commitAllCheckpoints(forkId, depth) {
577
+ return this.execute('commitAllCheckpoints', forkId, false, async ()=>{
578
+ const currentDepth = this.checkpointDepths.get(forkId) ?? 0;
579
+ if (depth === 0) {
580
+ await this.api.commitAllCheckpoints({
581
+ forkId
582
+ });
583
+ } else {
584
+ for(let d = currentDepth; d > depth; d--){
585
+ await this.api.commitCheckpoint({
586
+ forkId
587
+ });
588
+ }
589
+ }
590
+ this.checkpointDepths.set(forkId, depth);
591
+ });
592
+ }
593
+ revertAllCheckpoints(forkId, depth) {
594
+ return this.execute('revertAllCheckpoints', forkId, false, async ()=>{
595
+ const currentDepth = this.checkpointDepths.get(forkId) ?? 0;
596
+ if (depth === 0) {
597
+ await this.api.revertAllCheckpoints({
598
+ forkId
599
+ });
600
+ } else {
601
+ for(let d = currentDepth; d > depth; d--){
602
+ await this.api.revertCheckpoint({
603
+ forkId
604
+ });
605
+ }
606
+ }
607
+ this.checkpointDepths.set(forkId, depth);
608
+ });
609
+ }
610
+ copyStores(dstPath, compact) {
611
+ return this.execute('copyStores', 0, false, async ()=>{
612
+ await this.api.copyStores({
613
+ dstPath,
614
+ compact
615
+ });
616
+ });
617
+ }
618
+ // Read/write ordering is enforced server-side by the wsdb per-fork scheduler,
619
+ // so the client no longer queues: every op is sent immediately and the server
620
+ // serializes writes per fork while running reads concurrently. `_forkId` and
621
+ // `_committedOnly` are retained in the signature (they describe the op) but are
622
+ // no longer needed for client-side ordering.
623
+ async execute(operation, _forkId, _committedOnly, request) {
624
+ if (!this.open) {
625
+ throw new Error('IPC instance is closed');
626
+ }
627
+ return await this.send(operation, request);
628
+ }
629
+ async send(operation, request) {
630
+ const start = performance.now();
631
+ try {
632
+ const response = await request();
633
+ const durationMs = performance.now() - start;
634
+ this.log.trace(`Call ${operation} took (ms)`, {
635
+ duration: durationMs
636
+ });
637
+ this.instrumentation.recordRoundTrip(durationMs * 1000, operation);
638
+ return response;
639
+ } catch (error) {
640
+ this.log.error(`Call ${operation} failed: ${error}`, error);
641
+ throw error;
642
+ }
643
+ }
644
+ }
645
+ /**
646
+ * Helper to create WsdbOptions from standard world state config.
647
+ * Returns the options needed to construct a wsdb service command line.
648
+ */ export function getWsdbOptions(dataDir, wsTreeMapSizes) {
649
+ return {
650
+ treeHeights: {
651
+ [MerkleTreeId.NULLIFIER_TREE]: NULLIFIER_TREE_HEIGHT,
652
+ [MerkleTreeId.NOTE_HASH_TREE]: NOTE_HASH_TREE_HEIGHT,
653
+ [MerkleTreeId.PUBLIC_DATA_TREE]: PUBLIC_DATA_TREE_HEIGHT,
654
+ [MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: L1_TO_L2_MSG_TREE_HEIGHT,
655
+ [MerkleTreeId.ARCHIVE]: ARCHIVE_HEIGHT
656
+ },
657
+ treePrefill: {
658
+ [MerkleTreeId.NULLIFIER_TREE]: 2 * MAX_NULLIFIERS_PER_TX,
659
+ [MerkleTreeId.PUBLIC_DATA_TREE]: 2 * MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
660
+ },
661
+ mapSizes: {
662
+ [MerkleTreeId.NULLIFIER_TREE]: wsTreeMapSizes.nullifierTreeMapSizeKb,
663
+ [MerkleTreeId.NOTE_HASH_TREE]: wsTreeMapSizes.noteHashTreeMapSizeKb,
664
+ [MerkleTreeId.PUBLIC_DATA_TREE]: wsTreeMapSizes.publicDataTreeMapSizeKb,
665
+ [MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: wsTreeMapSizes.messageTreeMapSizeKb,
666
+ [MerkleTreeId.ARCHIVE]: wsTreeMapSizes.archiveTreeMapSizeKb
667
+ },
668
+ initialHeaderGeneratorPoint: DomainSeparator.BLOCK_HEADER_HASH
669
+ };
670
+ }