@aztec/world-state 0.0.1-commit.001888fc → 0.0.1-commit.017a351

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 (38) hide show
  1. package/dest/native/ipc_world_state_instance.d.ts +45 -0
  2. package/dest/native/ipc_world_state_instance.d.ts.map +1 -0
  3. package/dest/native/ipc_world_state_instance.js +750 -0
  4. package/dest/native/merkle_trees_facade.d.ts +8 -4
  5. package/dest/native/merkle_trees_facade.d.ts.map +1 -1
  6. package/dest/native/merkle_trees_facade.js +34 -15
  7. package/dest/native/message.d.ts +7 -8
  8. package/dest/native/message.d.ts.map +1 -1
  9. package/dest/native/native_world_state.d.ts +19 -7
  10. package/dest/native/native_world_state.d.ts.map +1 -1
  11. package/dest/native/native_world_state.js +89 -24
  12. package/dest/native/native_world_state_instance.d.ts +21 -39
  13. package/dest/native/native_world_state_instance.d.ts.map +1 -1
  14. package/dest/native/native_world_state_instance.js +8 -202
  15. package/dest/native/world_state_ops_queue.js +5 -5
  16. package/dest/synchronizer/config.d.ts +1 -1
  17. package/dest/synchronizer/config.d.ts.map +1 -1
  18. package/dest/synchronizer/config.js +9 -10
  19. package/dest/synchronizer/factory.d.ts +4 -4
  20. package/dest/synchronizer/factory.d.ts.map +1 -1
  21. package/dest/synchronizer/factory.js +7 -6
  22. package/dest/synchronizer/server_world_state_synchronizer.d.ts +1 -1
  23. package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
  24. package/dest/synchronizer/server_world_state_synchronizer.js +40 -11
  25. package/dest/testing.d.ts +4 -3
  26. package/dest/testing.d.ts.map +1 -1
  27. package/dest/testing.js +10 -6
  28. package/package.json +13 -10
  29. package/src/native/ipc_world_state_instance.ts +863 -0
  30. package/src/native/merkle_trees_facade.ts +41 -19
  31. package/src/native/message.ts +6 -7
  32. package/src/native/native_world_state.ts +98 -35
  33. package/src/native/native_world_state_instance.ts +28 -276
  34. package/src/native/world_state_ops_queue.ts +5 -5
  35. package/src/synchronizer/config.ts +14 -10
  36. package/src/synchronizer/factory.ts +11 -10
  37. package/src/synchronizer/server_world_state_synchronizer.ts +35 -13
  38. package/src/testing.ts +8 -9
@@ -0,0 +1,750 @@
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 assert from 'assert';
6
+ import { cpus } from 'node:os';
7
+ import { WorldStateMessageType, isWithCanonical, isWithForkId, isWithRevision } from './message.js';
8
+ import { WorldStateOpsQueue } from './world_state_ops_queue.js';
9
+ // ————— Request conversion helpers —————
10
+ function toWsdbRevision(rev) {
11
+ return {
12
+ forkId: rev.forkId,
13
+ blockNumber: Number(rev.blockNumber),
14
+ includeUncommitted: rev.includeUncommitted
15
+ };
16
+ }
17
+ function blockStateRefToMap(ref) {
18
+ return [
19
+ ...ref.entries()
20
+ ].map(([treeId, [root, size]])=>({
21
+ treeId,
22
+ root: new Uint8Array(root),
23
+ size: Number(size)
24
+ }));
25
+ }
26
+ function toPublicDataLeaf(leaf) {
27
+ if (leaf instanceof Buffer || !('slot' in leaf)) {
28
+ throw new Error('Expected public data leaf');
29
+ }
30
+ return {
31
+ slot: new Uint8Array(leaf.slot),
32
+ value: new Uint8Array(leaf.value)
33
+ };
34
+ }
35
+ function toNullifierLeaf(leaf) {
36
+ if (leaf instanceof Buffer || !('nullifier' in leaf)) {
37
+ throw new Error('Expected nullifier leaf');
38
+ }
39
+ return {
40
+ nullifier: new Uint8Array(leaf.nullifier)
41
+ };
42
+ }
43
+ function fromPublicDataLeaf(leaf) {
44
+ return {
45
+ slot: Buffer.from(leaf.slot),
46
+ value: Buffer.from(leaf.value)
47
+ };
48
+ }
49
+ function fromNullifierLeaf(leaf) {
50
+ return {
51
+ nullifier: Buffer.from(leaf.nullifier)
52
+ };
53
+ }
54
+ function fromIndexedLeaf(leaf, convertLeaf) {
55
+ return {
56
+ leaf: convertLeaf(leaf.leaf),
57
+ nextIndex: leaf.nextIndex,
58
+ nextKey: Buffer.from(leaf.nextKey)
59
+ };
60
+ }
61
+ function fromLeafUpdateWitnessData(data, convertLeaf) {
62
+ return {
63
+ leaf: fromIndexedLeaf(data.leaf, convertLeaf),
64
+ index: data.index,
65
+ path: data.path.map((p)=>Buffer.from(p))
66
+ };
67
+ }
68
+ function fromBatchInsertionResult(result, convertLeaf) {
69
+ return {
70
+ lowLeafWitnessData: result.lowLeafWitnessData.map((data)=>fromLeafUpdateWitnessData(data, convertLeaf)),
71
+ sortedLeaves: result.sortedLeaves.map(({ leaf, index })=>[
72
+ convertLeaf(leaf),
73
+ index
74
+ ]),
75
+ subtreePath: result.subtreePath.map((p)=>Buffer.from(p))
76
+ };
77
+ }
78
+ function fromSequentialInsertionResult(result, convertLeaf) {
79
+ return {
80
+ lowLeafWitnessData: result.lowLeafWitnessData.map((data)=>fromLeafUpdateWitnessData(data, convertLeaf)),
81
+ insertionWitnessData: result.insertionWitnessData.map((data)=>fromLeafUpdateWitnessData(data, convertLeaf))
82
+ };
83
+ }
84
+ function toFrLeaf(leaf) {
85
+ if (!(leaf instanceof Buffer)) {
86
+ throw new Error('Expected field leaf');
87
+ }
88
+ return new Uint8Array(leaf);
89
+ }
90
+ function formatMap(map) {
91
+ if (!map || Object.keys(map).length === 0) {
92
+ return undefined;
93
+ }
94
+ return `{${Object.entries(map).map(([key, value])=>`${key}:${value}`).join(',')}}`;
95
+ }
96
+ function getWsdbThreadCount() {
97
+ return Math.min(16, cpus().length);
98
+ }
99
+ function getWsdbExtraArgs(dataDir, wsTreeMapSizes, genesis, threads) {
100
+ const options = getWsdbOptions(dataDir, wsTreeMapSizes);
101
+ const args = [
102
+ '--data-dir',
103
+ dataDir,
104
+ '--threads',
105
+ threads.toString()
106
+ ];
107
+ const treeHeights = formatMap(options.treeHeights);
108
+ if (treeHeights) {
109
+ args.push('--tree-heights', treeHeights);
110
+ }
111
+ const treePrefill = formatMap(options.treePrefill);
112
+ if (treePrefill) {
113
+ args.push('--tree-prefill', treePrefill);
114
+ }
115
+ const mapSizes = formatMap(options.mapSizes);
116
+ if (mapSizes) {
117
+ args.push('--map-sizes', mapSizes);
118
+ }
119
+ args.push('--initial-header-generator-point', options.initialHeaderGeneratorPoint.toString());
120
+ if (genesis.prefilledPublicData.length > 0) {
121
+ const pairs = genesis.prefilledPublicData.map((data)=>[
122
+ data.slot.toBuffer().toString('hex'),
123
+ data.value.toBuffer().toString('hex')
124
+ ]);
125
+ args.push('--prefilled-public-data', JSON.stringify(pairs));
126
+ }
127
+ const genesisTimestamp = Number(genesis.genesisTimestamp);
128
+ if (genesisTimestamp !== 0) {
129
+ args.push('--genesis-timestamp', genesisTimestamp.toString());
130
+ }
131
+ return args;
132
+ }
133
+ // ————— Response conversion helpers —————
134
+ /** Convert Uint8Array fields to Buffer recursively (for opaque blob responses). */ function convertUint8ArraysToBuffers(obj) {
135
+ if (obj instanceof Uint8Array) {
136
+ return Buffer.from(obj);
137
+ }
138
+ if (Array.isArray(obj)) {
139
+ return obj.map(convertUint8ArraysToBuffers);
140
+ }
141
+ if (obj !== null && typeof obj === 'object') {
142
+ const result = {};
143
+ for (const [key, value] of Object.entries(obj)){
144
+ result[key] = convertUint8ArraysToBuffers(value);
145
+ }
146
+ return result;
147
+ }
148
+ return obj;
149
+ }
150
+ /** Convert Wsdb state reference (Record<number, [Uint8Array, number]>) to NAPI format. */ function convertStateRef(state) {
151
+ const result = {};
152
+ for (const { treeId, root, size } of state){
153
+ result[treeId] = [
154
+ Buffer.from(root),
155
+ BigInt(size)
156
+ ];
157
+ }
158
+ return result;
159
+ }
160
+ /** Convert Wsdb WorldStateStatusSummary to the native world-state format. */ function convertStatusSummary(s) {
161
+ return {
162
+ unfinalizedBlockNumber: s.unfinalizedBlockNumber,
163
+ finalizedBlockNumber: s.finalizedBlockNumber,
164
+ oldestHistoricalBlock: s.oldestHistoricalBlock,
165
+ treesAreSynched: s.treesAreSynched
166
+ };
167
+ }
168
+ function convertDBStats(s) {
169
+ return {
170
+ name: s.name,
171
+ numDataItems: s.numDataItems,
172
+ totalUsedSize: s.totalUsedSize
173
+ };
174
+ }
175
+ function convertTreeDBStats(s) {
176
+ return {
177
+ mapSize: s.mapSize,
178
+ physicalFileSize: s.physicalFileSize,
179
+ blocksDBStats: convertDBStats(s.blocksDBStats),
180
+ nodesDBStats: convertDBStats(s.nodesDBStats),
181
+ leafPreimagesDBStats: convertDBStats(s.leafPreimagesDBStats),
182
+ leafIndicesDBStats: convertDBStats(s.leafIndicesDBStats),
183
+ blockIndicesDBStats: convertDBStats(s.blockIndicesDBStats)
184
+ };
185
+ }
186
+ function convertWorldStateDBStats(s) {
187
+ return {
188
+ noteHashTreeStats: convertTreeDBStats(s.noteHashTreeStats),
189
+ messageTreeStats: convertTreeDBStats(s.messageTreeStats),
190
+ archiveTreeStats: convertTreeDBStats(s.archiveTreeStats),
191
+ publicDataTreeStats: convertTreeDBStats(s.publicDataTreeStats),
192
+ nullifierTreeStats: convertTreeDBStats(s.nullifierTreeStats)
193
+ };
194
+ }
195
+ function convertTreeMeta(m) {
196
+ return {
197
+ name: m.name,
198
+ depth: m.depth,
199
+ size: m.size,
200
+ committedSize: m.committedSize,
201
+ root: m.root,
202
+ initialSize: m.initialSize,
203
+ initialRoot: m.initialRoot,
204
+ oldestHistoricBlock: m.oldestHistoricBlock,
205
+ unfinalizedBlockHeight: m.unfinalizedBlockHeight,
206
+ finalizedBlockHeight: m.finalizedBlockHeight
207
+ };
208
+ }
209
+ function convertWorldStateMeta(m) {
210
+ return {
211
+ noteHashTreeMeta: convertTreeMeta(m.noteHashTreeMeta),
212
+ messageTreeMeta: convertTreeMeta(m.messageTreeMeta),
213
+ archiveTreeMeta: convertTreeMeta(m.archiveTreeMeta),
214
+ publicDataTreeMeta: convertTreeMeta(m.publicDataTreeMeta),
215
+ nullifierTreeMeta: convertTreeMeta(m.nullifierTreeMeta)
216
+ };
217
+ }
218
+ function convertStatusFull(s) {
219
+ return {
220
+ summary: convertStatusSummary(s.summary),
221
+ dbStats: convertWorldStateDBStats(s.dbStats),
222
+ meta: convertWorldStateMeta(s.meta)
223
+ };
224
+ }
225
+ /** Convert Wsdb SiblingPathAndIndex to NAPI format. */ function convertSiblingPathAndIndex(s) {
226
+ if (!s) {
227
+ return undefined;
228
+ }
229
+ return {
230
+ index: BigInt(s.index),
231
+ path: s.path.map((p)=>Buffer.from(p))
232
+ };
233
+ }
234
+ // ————— Public API —————
235
+ /**
236
+ * IPC-backed world state instance.
237
+ * Uses WsdbService (spawns aztec-wsdb binary) and the generated AsyncApi
238
+ * to communicate via the NamedUnion IPC protocol.
239
+ */ export class IpcWorldState {
240
+ wsdb;
241
+ instrumentation;
242
+ log;
243
+ open;
244
+ queues;
245
+ api;
246
+ /** Tracks checkpoint depth per fork (WSDB IPC doesn't return depth in response). */ checkpointDepths;
247
+ constructor(wsdb, instrumentation, bindings, log = createLogger('world-state:ipc-database', bindings)){
248
+ this.wsdb = wsdb;
249
+ this.instrumentation = instrumentation;
250
+ this.log = log;
251
+ this.open = true;
252
+ this.queues = new Map();
253
+ this.checkpointDepths = new Map();
254
+ this.api = wsdb;
255
+ this.queues.set(0, new WorldStateOpsQueue());
256
+ this.log.info('Created IPC-backed world state instance');
257
+ }
258
+ /**
259
+ * Spawn an `aztec-wsdb` subprocess and return an IPC-backed world state wrapping it.
260
+ * Encapsulates wsdb binary discovery, service construction, and readiness wait.
261
+ */ static async spawn(dataDir, wsTreeMapSizes, genesis, instrumentation, bindings) {
262
+ const threads = getWsdbThreadCount();
263
+ const wsdb = await WsdbService.spawn({
264
+ transport: 'uds',
265
+ extraArgs: getWsdbExtraArgs(dataDir, wsTreeMapSizes, genesis, threads),
266
+ env: {
267
+ HARDWARE_CONCURRENCY: threads.toString()
268
+ }
269
+ });
270
+ return new IpcWorldState(wsdb, instrumentation, bindings);
271
+ }
272
+ /** Returns the socket path of the underlying wsdb server. */ getSocketPath() {
273
+ return this.wsdb.getIpcPath();
274
+ }
275
+ async call(messageType, body, responseHandler = (response)=>response, errorHandler = (_)=>{}) {
276
+ let forkId = -1;
277
+ let committedOnly = false;
278
+ if (isWithCanonical(body)) {
279
+ forkId = 0;
280
+ } else if (isWithForkId(body)) {
281
+ forkId = body.forkId;
282
+ } else if (isWithRevision(body)) {
283
+ forkId = body.revision.forkId;
284
+ committedOnly = body.revision.includeUncommitted === false;
285
+ } else {
286
+ const _ = body;
287
+ throw new Error(`Unable to determine forkId for message=${WorldStateMessageType[messageType]}`);
288
+ }
289
+ let requestQueue = this.queues.get(forkId);
290
+ if (requestQueue === undefined) {
291
+ requestQueue = new WorldStateOpsQueue();
292
+ this.queues.set(forkId, requestQueue);
293
+ }
294
+ try {
295
+ return await requestQueue.execute(async ()=>{
296
+ assert.notEqual(messageType, WorldStateMessageType.CLOSE, 'Use close() to close the IPC instance');
297
+ assert.equal(this.open, true, 'IPC instance is closed');
298
+ let response;
299
+ try {
300
+ response = await this._sendMessage(messageType, body);
301
+ } catch (error) {
302
+ errorHandler(error.message);
303
+ throw error;
304
+ }
305
+ return responseHandler(response);
306
+ }, messageType, committedOnly);
307
+ } finally{
308
+ if (messageType === WorldStateMessageType.DELETE_FORK) {
309
+ await requestQueue.stop();
310
+ this.queues.delete(forkId);
311
+ }
312
+ }
313
+ }
314
+ async close() {
315
+ if (!this.open) {
316
+ return;
317
+ }
318
+ this.open = false;
319
+ const queue = this.queues.get(0);
320
+ await queue.stop();
321
+ await this.wsdb.destroy();
322
+ }
323
+ async _sendMessage(messageType, body) {
324
+ const start = performance.now();
325
+ try {
326
+ const response = await this.dispatch(messageType, body);
327
+ const durationMs = performance.now() - start;
328
+ this.log.trace(`Call ${WorldStateMessageType[messageType]} took (ms)`, {
329
+ duration: durationMs
330
+ });
331
+ this.instrumentation.recordRoundTrip(durationMs * 1000, messageType);
332
+ return response;
333
+ } catch (error) {
334
+ this.log.error(`Call ${WorldStateMessageType[messageType]} failed: ${error}`, error);
335
+ throw error;
336
+ }
337
+ }
338
+ async dispatch(messageType, body) {
339
+ switch(messageType){
340
+ // ——— Tree info & state reference ———
341
+ case WorldStateMessageType.GET_TREE_INFO:
342
+ {
343
+ const b = body;
344
+ const resp = await this.api.getTreeInfo({
345
+ treeId: b.treeId,
346
+ revision: toWsdbRevision(b.revision)
347
+ });
348
+ return {
349
+ treeId: resp.treeId,
350
+ root: Buffer.from(resp.root),
351
+ size: resp.size,
352
+ depth: resp.depth
353
+ };
354
+ }
355
+ case WorldStateMessageType.GET_STATE_REFERENCE:
356
+ {
357
+ const b = body;
358
+ const resp = await this.api.getStateReference({
359
+ revision: toWsdbRevision(b.revision)
360
+ });
361
+ return {
362
+ state: convertStateRef(resp.state)
363
+ };
364
+ }
365
+ case WorldStateMessageType.GET_INITIAL_STATE_REFERENCE:
366
+ {
367
+ const resp = await this.api.getInitialStateReference({});
368
+ return {
369
+ state: convertStateRef(resp.state)
370
+ };
371
+ }
372
+ // ——— Leaf queries ———
373
+ case WorldStateMessageType.GET_LEAF_VALUE:
374
+ {
375
+ const b = body;
376
+ const revision = toWsdbRevision(b.revision);
377
+ const leafIndex = Number(b.leafIndex);
378
+ if (b.treeId === MerkleTreeId.PUBLIC_DATA_TREE) {
379
+ const resp = await this.api.getPublicDataLeafValue({
380
+ revision,
381
+ leafIndex
382
+ });
383
+ return resp.value ? fromPublicDataLeaf(resp.value) : undefined;
384
+ }
385
+ if (b.treeId === MerkleTreeId.NULLIFIER_TREE) {
386
+ const resp = await this.api.getNullifierLeafValue({
387
+ revision,
388
+ leafIndex
389
+ });
390
+ return resp.value ? fromNullifierLeaf(resp.value) : undefined;
391
+ }
392
+ const resp = await this.api.getLeafValue({
393
+ treeId: b.treeId,
394
+ revision,
395
+ leafIndex
396
+ });
397
+ if (!resp.value) {
398
+ return undefined;
399
+ }
400
+ return Buffer.from(resp.value);
401
+ }
402
+ case WorldStateMessageType.GET_LEAF_PREIMAGE:
403
+ {
404
+ const b = body;
405
+ const resp = b.treeId === MerkleTreeId.PUBLIC_DATA_TREE ? await this.api.getPublicDataLeafPreimage({
406
+ revision: toWsdbRevision(b.revision),
407
+ leafIndex: Number(b.leafIndex)
408
+ }) : await this.api.getNullifierLeafPreimage({
409
+ revision: toWsdbRevision(b.revision),
410
+ leafIndex: Number(b.leafIndex)
411
+ });
412
+ if (!resp.preimage) {
413
+ return undefined;
414
+ }
415
+ return convertUint8ArraysToBuffers(resp.preimage);
416
+ }
417
+ case WorldStateMessageType.GET_SIBLING_PATH:
418
+ {
419
+ const b = body;
420
+ const resp = await this.api.getSiblingPath({
421
+ treeId: b.treeId,
422
+ revision: toWsdbRevision(b.revision),
423
+ leafIndex: Number(b.leafIndex)
424
+ });
425
+ return resp.path.map((p)=>Buffer.from(p));
426
+ }
427
+ case WorldStateMessageType.GET_BLOCK_NUMBERS_FOR_LEAF_INDICES:
428
+ {
429
+ const b = body;
430
+ const resp = await this.api.getBlockNumbersForLeafIndices({
431
+ treeId: b.treeId,
432
+ revision: toWsdbRevision(b.revision),
433
+ leafIndices: b.leafIndices.map(Number)
434
+ });
435
+ return {
436
+ blockNumbers: resp.blockNumbers.map((n)=>n != null ? BigInt(n) : undefined)
437
+ };
438
+ }
439
+ // ——— Find operations ———
440
+ case WorldStateMessageType.FIND_LEAF_INDICES:
441
+ {
442
+ const b = body;
443
+ const revision = toWsdbRevision(b.revision);
444
+ const startIndex = Number(b.startIndex);
445
+ const resp = b.treeId === MerkleTreeId.PUBLIC_DATA_TREE ? await this.api.findPublicDataLeafIndices({
446
+ revision,
447
+ leaves: b.leaves.map(toPublicDataLeaf),
448
+ startIndex
449
+ }) : b.treeId === MerkleTreeId.NULLIFIER_TREE ? await this.api.findNullifierLeafIndices({
450
+ revision,
451
+ leaves: b.leaves.map(toNullifierLeaf),
452
+ startIndex
453
+ }) : await this.api.findLeafIndices({
454
+ treeId: b.treeId,
455
+ revision,
456
+ leaves: b.leaves.map(toFrLeaf),
457
+ startIndex
458
+ });
459
+ return {
460
+ indices: resp.indices.map((n)=>n != null ? BigInt(n) : undefined)
461
+ };
462
+ }
463
+ case WorldStateMessageType.FIND_LOW_LEAF:
464
+ {
465
+ const b = body;
466
+ const resp = await this.api.findLowLeaf({
467
+ treeId: b.treeId,
468
+ revision: toWsdbRevision(b.revision),
469
+ key: new Uint8Array(b.key.toBuffer())
470
+ });
471
+ return {
472
+ alreadyPresent: resp.alreadyPresent,
473
+ index: BigInt(resp.index)
474
+ };
475
+ }
476
+ case WorldStateMessageType.FIND_SIBLING_PATHS:
477
+ {
478
+ const b = body;
479
+ const revision = toWsdbRevision(b.revision);
480
+ const resp = b.treeId === MerkleTreeId.PUBLIC_DATA_TREE ? await this.api.findPublicDataSiblingPaths({
481
+ revision,
482
+ leaves: b.leaves.map(toPublicDataLeaf)
483
+ }) : b.treeId === MerkleTreeId.NULLIFIER_TREE ? await this.api.findNullifierSiblingPaths({
484
+ revision,
485
+ leaves: b.leaves.map(toNullifierLeaf)
486
+ }) : await this.api.findSiblingPaths({
487
+ treeId: b.treeId,
488
+ revision,
489
+ leaves: b.leaves.map(toFrLeaf)
490
+ });
491
+ return {
492
+ paths: resp.paths.map(convertSiblingPathAndIndex)
493
+ };
494
+ }
495
+ // ——— Mutations ———
496
+ case WorldStateMessageType.APPEND_LEAVES:
497
+ {
498
+ const b = body;
499
+ if (b.treeId === MerkleTreeId.PUBLIC_DATA_TREE) {
500
+ await this.api.appendPublicDataLeaves({
501
+ leaves: b.leaves.map(toPublicDataLeaf),
502
+ forkId: b.forkId
503
+ });
504
+ } else if (b.treeId === MerkleTreeId.NULLIFIER_TREE) {
505
+ await this.api.appendNullifierLeaves({
506
+ leaves: b.leaves.map(toNullifierLeaf),
507
+ forkId: b.forkId
508
+ });
509
+ } else {
510
+ await this.api.appendLeaves({
511
+ treeId: b.treeId,
512
+ leaves: b.leaves.map(toFrLeaf),
513
+ forkId: b.forkId
514
+ });
515
+ }
516
+ return undefined;
517
+ }
518
+ case WorldStateMessageType.BATCH_INSERT:
519
+ {
520
+ const b = body;
521
+ const resp = b.treeId === MerkleTreeId.PUBLIC_DATA_TREE ? await this.api.batchInsertPublicData({
522
+ leaves: b.leaves.map(toPublicDataLeaf),
523
+ subtreeDepth: b.subtreeDepth,
524
+ forkId: b.forkId
525
+ }) : await this.api.batchInsertNullifier({
526
+ leaves: b.leaves.map(toNullifierLeaf),
527
+ subtreeDepth: b.subtreeDepth,
528
+ forkId: b.forkId
529
+ });
530
+ return b.treeId === MerkleTreeId.PUBLIC_DATA_TREE ? fromBatchInsertionResult(resp.result, fromPublicDataLeaf) : fromBatchInsertionResult(resp.result, fromNullifierLeaf);
531
+ }
532
+ case WorldStateMessageType.SEQUENTIAL_INSERT:
533
+ {
534
+ const b = body;
535
+ const resp = b.treeId === MerkleTreeId.PUBLIC_DATA_TREE ? await this.api.sequentialInsertPublicData({
536
+ leaves: b.leaves.map(toPublicDataLeaf),
537
+ forkId: b.forkId
538
+ }) : await this.api.sequentialInsertNullifier({
539
+ leaves: b.leaves.map(toNullifierLeaf),
540
+ forkId: b.forkId
541
+ });
542
+ return b.treeId === MerkleTreeId.PUBLIC_DATA_TREE ? fromSequentialInsertionResult(resp.result, fromPublicDataLeaf) : fromSequentialInsertionResult(resp.result, fromNullifierLeaf);
543
+ }
544
+ case WorldStateMessageType.UPDATE_ARCHIVE:
545
+ {
546
+ const b = body;
547
+ await this.api.updateArchive({
548
+ blockStateRef: blockStateRefToMap(b.blockStateRef),
549
+ blockHeaderHash: new Uint8Array(b.blockHeaderHash),
550
+ forkId: b.forkId
551
+ });
552
+ return undefined;
553
+ }
554
+ // ——— Commit / Rollback ———
555
+ case WorldStateMessageType.COMMIT:
556
+ {
557
+ await this.api.commit({});
558
+ return undefined;
559
+ }
560
+ case WorldStateMessageType.ROLLBACK:
561
+ {
562
+ await this.api.rollback({});
563
+ return undefined;
564
+ }
565
+ // ——— Block sync ———
566
+ case WorldStateMessageType.SYNC_BLOCK:
567
+ {
568
+ const b = body;
569
+ const resp = await this.api.syncBlock({
570
+ blockNumber: Number(b.blockNumber),
571
+ blockStateRef: blockStateRefToMap(b.blockStateRef),
572
+ blockHeaderHash: new Uint8Array(b.blockHeaderHash),
573
+ paddedNoteHashes: b.paddedNoteHashes.map(toFrLeaf),
574
+ paddedL1ToL2Messages: b.paddedL1ToL2Messages.map(toFrLeaf),
575
+ paddedNullifiers: b.paddedNullifiers.map(toNullifierLeaf),
576
+ publicDataWrites: b.publicDataWrites.map(toPublicDataLeaf)
577
+ });
578
+ return convertStatusFull(resp.status);
579
+ }
580
+ // ——— Fork management ———
581
+ case WorldStateMessageType.CREATE_FORK:
582
+ {
583
+ const b = body;
584
+ const resp = await this.api.createFork({
585
+ latest: b.latest,
586
+ blockNumber: Number(b.blockNumber)
587
+ });
588
+ return {
589
+ forkId: resp.forkId
590
+ };
591
+ }
592
+ case WorldStateMessageType.DELETE_FORK:
593
+ {
594
+ const b = body;
595
+ await this.api.deleteFork({
596
+ forkId: b.forkId
597
+ });
598
+ return undefined;
599
+ }
600
+ // ——— Block finalization ———
601
+ case WorldStateMessageType.FINALIZE_BLOCKS:
602
+ {
603
+ const b = body;
604
+ const resp = await this.api.finalizeBlocks({
605
+ toBlockNumber: Number(b.toBlockNumber)
606
+ });
607
+ return convertStatusSummary(resp.status);
608
+ }
609
+ case WorldStateMessageType.UNWIND_BLOCKS:
610
+ {
611
+ const b = body;
612
+ const resp = await this.api.unwindBlocks({
613
+ toBlockNumber: Number(b.toBlockNumber)
614
+ });
615
+ return convertStatusFull(resp.status);
616
+ }
617
+ case WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS:
618
+ {
619
+ const b = body;
620
+ const resp = await this.api.removeHistoricalBlocks({
621
+ toBlockNumber: Number(b.toBlockNumber)
622
+ });
623
+ return convertStatusFull(resp.status);
624
+ }
625
+ // ——— Status ———
626
+ case WorldStateMessageType.GET_STATUS:
627
+ {
628
+ const resp = await this.api.getStatus({});
629
+ return convertStatusSummary(resp.status);
630
+ }
631
+ // ——— Checkpoints ———
632
+ case WorldStateMessageType.CREATE_CHECKPOINT:
633
+ {
634
+ const b = body;
635
+ await this.api.createCheckpoint({
636
+ forkId: b.forkId
637
+ });
638
+ const depth = (this.checkpointDepths.get(b.forkId) ?? 0) + 1;
639
+ this.checkpointDepths.set(b.forkId, depth);
640
+ return {
641
+ depth
642
+ };
643
+ }
644
+ case WorldStateMessageType.COMMIT_CHECKPOINT:
645
+ {
646
+ const b = body;
647
+ await this.api.commitCheckpoint({
648
+ forkId: b.forkId
649
+ });
650
+ const depth = Math.max(0, (this.checkpointDepths.get(b.forkId) ?? 0) - 1);
651
+ this.checkpointDepths.set(b.forkId, depth);
652
+ return undefined;
653
+ }
654
+ case WorldStateMessageType.REVERT_CHECKPOINT:
655
+ {
656
+ const b = body;
657
+ await this.api.revertCheckpoint({
658
+ forkId: b.forkId
659
+ });
660
+ const depth = Math.max(0, (this.checkpointDepths.get(b.forkId) ?? 0) - 1);
661
+ this.checkpointDepths.set(b.forkId, depth);
662
+ return undefined;
663
+ }
664
+ case WorldStateMessageType.COMMIT_ALL_CHECKPOINTS:
665
+ {
666
+ const b = body;
667
+ const targetDepth = b.depth ?? 0;
668
+ const currentDepth = this.checkpointDepths.get(b.forkId) ?? 0;
669
+ if (targetDepth === 0) {
670
+ // Commit everything — use the bulk operation
671
+ await this.api.commitAllCheckpoints({
672
+ forkId: b.forkId
673
+ });
674
+ } else {
675
+ // Commit one level at a time down to target depth
676
+ for(let d = currentDepth; d > targetDepth; d--){
677
+ await this.api.commitCheckpoint({
678
+ forkId: b.forkId
679
+ });
680
+ }
681
+ }
682
+ this.checkpointDepths.set(b.forkId, targetDepth);
683
+ return undefined;
684
+ }
685
+ case WorldStateMessageType.REVERT_ALL_CHECKPOINTS:
686
+ {
687
+ const b = body;
688
+ const targetDepth = b.depth ?? 0;
689
+ const currentDepth = this.checkpointDepths.get(b.forkId) ?? 0;
690
+ if (targetDepth === 0) {
691
+ // Revert everything — use the bulk operation
692
+ await this.api.revertAllCheckpoints({
693
+ forkId: b.forkId
694
+ });
695
+ } else {
696
+ // Revert one level at a time down to target depth
697
+ for(let d = currentDepth; d > targetDepth; d--){
698
+ await this.api.revertCheckpoint({
699
+ forkId: b.forkId
700
+ });
701
+ }
702
+ }
703
+ this.checkpointDepths.set(b.forkId, targetDepth);
704
+ return undefined;
705
+ }
706
+ // ——— Misc ———
707
+ case WorldStateMessageType.COPY_STORES:
708
+ {
709
+ const b = body;
710
+ await this.api.copyStores({
711
+ dstPath: b.dstPath,
712
+ compact: b.compact
713
+ });
714
+ return undefined;
715
+ }
716
+ case WorldStateMessageType.CLOSE:
717
+ {
718
+ return undefined;
719
+ }
720
+ default:
721
+ throw new Error(`Unknown message type: ${messageType}`);
722
+ }
723
+ }
724
+ }
725
+ /**
726
+ * Helper to create WsdbOptions from standard world state config.
727
+ * Returns the options needed to construct a wsdb service command line.
728
+ */ export function getWsdbOptions(dataDir, wsTreeMapSizes) {
729
+ return {
730
+ treeHeights: {
731
+ [MerkleTreeId.NULLIFIER_TREE]: NULLIFIER_TREE_HEIGHT,
732
+ [MerkleTreeId.NOTE_HASH_TREE]: NOTE_HASH_TREE_HEIGHT,
733
+ [MerkleTreeId.PUBLIC_DATA_TREE]: PUBLIC_DATA_TREE_HEIGHT,
734
+ [MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: L1_TO_L2_MSG_TREE_HEIGHT,
735
+ [MerkleTreeId.ARCHIVE]: ARCHIVE_HEIGHT
736
+ },
737
+ treePrefill: {
738
+ [MerkleTreeId.NULLIFIER_TREE]: 2 * MAX_NULLIFIERS_PER_TX,
739
+ [MerkleTreeId.PUBLIC_DATA_TREE]: 2 * MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
740
+ },
741
+ mapSizes: {
742
+ [MerkleTreeId.NULLIFIER_TREE]: wsTreeMapSizes.nullifierTreeMapSizeKb,
743
+ [MerkleTreeId.NOTE_HASH_TREE]: wsTreeMapSizes.noteHashTreeMapSizeKb,
744
+ [MerkleTreeId.PUBLIC_DATA_TREE]: wsTreeMapSizes.publicDataTreeMapSizeKb,
745
+ [MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: wsTreeMapSizes.messageTreeMapSizeKb,
746
+ [MerkleTreeId.ARCHIVE]: wsTreeMapSizes.archiveTreeMapSizeKb
747
+ },
748
+ initialHeaderGeneratorPoint: DomainSeparator.BLOCK_HEADER_HASH
749
+ };
750
+ }