@aztec/world-state 0.0.1-commit.96bb3f7 → 0.0.1-commit.993d240

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 (53) hide show
  1. package/dest/instrumentation/instrumentation.d.ts +1 -1
  2. package/dest/instrumentation/instrumentation.d.ts.map +1 -1
  3. package/dest/instrumentation/instrumentation.js +9 -2
  4. package/dest/native/fork_checkpoint.d.ts +7 -1
  5. package/dest/native/fork_checkpoint.d.ts.map +1 -1
  6. package/dest/native/fork_checkpoint.js +15 -3
  7. package/dest/native/ipc_world_state_instance.d.ts +50 -0
  8. package/dest/native/ipc_world_state_instance.d.ts.map +1 -0
  9. package/dest/native/ipc_world_state_instance.js +628 -0
  10. package/dest/native/merkle_trees_facade.d.ts +9 -6
  11. package/dest/native/merkle_trees_facade.d.ts.map +1 -1
  12. package/dest/native/merkle_trees_facade.js +45 -16
  13. package/dest/native/message.d.ts +13 -5
  14. package/dest/native/message.d.ts.map +1 -1
  15. package/dest/native/native_world_state.d.ts +34 -8
  16. package/dest/native/native_world_state.d.ts.map +1 -1
  17. package/dest/native/native_world_state.js +81 -27
  18. package/dest/native/native_world_state_instance.d.ts +6 -5
  19. package/dest/native/native_world_state_instance.d.ts.map +1 -1
  20. package/dest/native/native_world_state_instance.js +33 -26
  21. package/dest/native/world_state_ops_queue.js +5 -5
  22. package/dest/synchronizer/config.d.ts +3 -5
  23. package/dest/synchronizer/config.d.ts.map +1 -1
  24. package/dest/synchronizer/config.js +15 -18
  25. package/dest/synchronizer/factory.d.ts +6 -5
  26. package/dest/synchronizer/factory.d.ts.map +1 -1
  27. package/dest/synchronizer/factory.js +7 -6
  28. package/dest/synchronizer/server_world_state_synchronizer.d.ts +4 -5
  29. package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
  30. package/dest/synchronizer/server_world_state_synchronizer.js +109 -37
  31. package/dest/test/utils.d.ts +7 -7
  32. package/dest/test/utils.d.ts.map +1 -1
  33. package/dest/test/utils.js +5 -5
  34. package/dest/testing.d.ts +4 -3
  35. package/dest/testing.d.ts.map +1 -1
  36. package/dest/testing.js +10 -6
  37. package/dest/world-state-db/merkle_tree_db.d.ts +3 -12
  38. package/dest/world-state-db/merkle_tree_db.d.ts.map +1 -1
  39. package/package.json +13 -12
  40. package/src/instrumentation/instrumentation.ts +9 -1
  41. package/src/native/fork_checkpoint.ts +19 -3
  42. package/src/native/ipc_world_state_instance.ts +717 -0
  43. package/src/native/merkle_trees_facade.ts +51 -17
  44. package/src/native/message.ts +14 -4
  45. package/src/native/native_world_state.ts +108 -32
  46. package/src/native/native_world_state_instance.ts +44 -32
  47. package/src/native/world_state_ops_queue.ts +5 -5
  48. package/src/synchronizer/config.ts +16 -23
  49. package/src/synchronizer/factory.ts +19 -11
  50. package/src/synchronizer/server_world_state_synchronizer.ts +119 -46
  51. package/src/test/utils.ts +6 -6
  52. package/src/testing.ts +8 -9
  53. package/src/world-state-db/merkle_tree_db.ts +2 -12
@@ -0,0 +1,628 @@
1
+ import { AsyncApi } from '@aztec/bb.js/aztec-wsdb';
2
+ 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';
3
+ import { createLogger } from '@aztec/foundation/log';
4
+ import { MerkleTreeId } from '@aztec/stdlib/trees';
5
+ import assert from 'assert';
6
+ import { Decoder, Encoder } from 'msgpackr';
7
+ import { WorldStateMessageType, isWithCanonical, isWithForkId, isWithRevision } from './message.js';
8
+ import { WorldStateOpsQueue } from './world_state_ops_queue.js';
9
+ // ————— Msgpack helpers —————
10
+ const msgpackEncoder = new Encoder({
11
+ useRecords: false
12
+ });
13
+ const msgpackDecoder = new Decoder({
14
+ useRecords: false
15
+ });
16
+ /** Msgpack-encode a SerializedLeafValue into bytes for IPC transport. */ function serializeLeafToBytes(leaf) {
17
+ return Buffer.from(msgpackEncoder.pack(leaf));
18
+ }
19
+ // ————— Request conversion helpers —————
20
+ function toWsdbRevision(rev) {
21
+ return {
22
+ forkid: rev.forkId,
23
+ blocknumber: Number(rev.blockNumber),
24
+ includeuncommitted: rev.includeUncommitted
25
+ };
26
+ }
27
+ function blockStateRefToMap(ref) {
28
+ const result = new Map();
29
+ for (const [treeId, [root, size]] of ref.entries()){
30
+ result.set(treeId, [
31
+ new Uint8Array(root),
32
+ Number(size)
33
+ ]);
34
+ }
35
+ return result;
36
+ }
37
+ // ————— Response conversion helpers —————
38
+ /** Convert Uint8Array fields to Buffer recursively (for opaque blob responses). */ function convertUint8ArraysToBuffers(obj) {
39
+ if (obj instanceof Uint8Array) {
40
+ return Buffer.from(obj);
41
+ }
42
+ if (Array.isArray(obj)) {
43
+ return obj.map(convertUint8ArraysToBuffers);
44
+ }
45
+ if (obj !== null && typeof obj === 'object') {
46
+ const result = {};
47
+ for (const [key, value] of Object.entries(obj)){
48
+ result[key] = convertUint8ArraysToBuffers(value);
49
+ }
50
+ return result;
51
+ }
52
+ return obj;
53
+ }
54
+ /** Decode a msgpack-encoded leaf value blob and convert Uint8Arrays to Buffers. */ function decodeLeafValue(encoded) {
55
+ const decoded = msgpackDecoder.unpack(Buffer.from(encoded));
56
+ return convertUint8ArraysToBuffers(decoded);
57
+ }
58
+ /** Decode a msgpack-encoded indexed leaf preimage blob. */ function decodeLeafPreimage(encoded) {
59
+ const decoded = msgpackDecoder.unpack(Buffer.from(encoded));
60
+ return convertUint8ArraysToBuffers(decoded);
61
+ }
62
+ /** Convert Wsdb state reference (Record<number, [Uint8Array, number]>) to NAPI format. */ function convertStateRef(state) {
63
+ const result = {};
64
+ for (const [key, [root, size]] of Object.entries(state)){
65
+ result[Number(key)] = [
66
+ Buffer.from(root),
67
+ BigInt(size)
68
+ ];
69
+ }
70
+ return result;
71
+ }
72
+ /** Convert Wsdb WorldStateStatusSummary (lowercase) to NAPI format (camelCase). */ function convertStatusSummary(s) {
73
+ return {
74
+ unfinalizedBlockNumber: s.unfinalizedblocknumber,
75
+ finalizedBlockNumber: s.finalizedblocknumber,
76
+ oldestHistoricalBlock: s.oldesthistoricalblock,
77
+ treesAreSynched: s.treesaresynched
78
+ };
79
+ }
80
+ function convertDBStats(s) {
81
+ return {
82
+ name: s.name,
83
+ numDataItems: s.numdataitems,
84
+ totalUsedSize: s.totalusedsize
85
+ };
86
+ }
87
+ function convertTreeDBStats(s) {
88
+ return {
89
+ mapSize: s.mapsize,
90
+ physicalFileSize: s.physicalfilesize,
91
+ blocksDBStats: convertDBStats(s.blocksdbstats),
92
+ nodesDBStats: convertDBStats(s.nodesdbstats),
93
+ leafPreimagesDBStats: convertDBStats(s.leafpreimagesdbstats),
94
+ leafIndicesDBStats: convertDBStats(s.leafindicesdbstats),
95
+ blockIndicesDBStats: convertDBStats(s.blockindicesdbstats)
96
+ };
97
+ }
98
+ function convertWorldStateDBStats(s) {
99
+ return {
100
+ noteHashTreeStats: convertTreeDBStats(s.notehashtreestats),
101
+ messageTreeStats: convertTreeDBStats(s.messagetreestats),
102
+ archiveTreeStats: convertTreeDBStats(s.archivetreestats),
103
+ publicDataTreeStats: convertTreeDBStats(s.publicdatatreestats),
104
+ nullifierTreeStats: convertTreeDBStats(s.nullifiertreestats)
105
+ };
106
+ }
107
+ function convertTreeMeta(m) {
108
+ return {
109
+ name: m.name,
110
+ depth: m.depth,
111
+ size: m.size,
112
+ committedSize: m.committedsize,
113
+ root: m.root,
114
+ initialSize: m.initialsize,
115
+ initialRoot: m.initialroot,
116
+ oldestHistoricBlock: m.oldesthistoricblock,
117
+ unfinalizedBlockHeight: m.unfinalizedblockheight,
118
+ finalizedBlockHeight: m.finalizedblockheight
119
+ };
120
+ }
121
+ function convertWorldStateMeta(m) {
122
+ return {
123
+ noteHashTreeMeta: convertTreeMeta(m.notehashtreemeta),
124
+ messageTreeMeta: convertTreeMeta(m.messagetreemeta),
125
+ archiveTreeMeta: convertTreeMeta(m.archivetreemeta),
126
+ publicDataTreeMeta: convertTreeMeta(m.publicdatatreemeta),
127
+ nullifierTreeMeta: convertTreeMeta(m.nullifiertreemeta)
128
+ };
129
+ }
130
+ function convertStatusFull(s) {
131
+ return {
132
+ summary: convertStatusSummary(s.summary),
133
+ dbStats: convertWorldStateDBStats(s.dbstats),
134
+ meta: convertWorldStateMeta(s.meta)
135
+ };
136
+ }
137
+ /** Convert Wsdb SiblingPathAndIndex to NAPI format. */ function convertSiblingPathAndIndex(s) {
138
+ if (!s) {
139
+ return undefined;
140
+ }
141
+ return {
142
+ index: BigInt(s.index),
143
+ path: s.path.map((p)=>Buffer.from(p))
144
+ };
145
+ }
146
+ /**
147
+ * IPC-backed world state instance.
148
+ * Uses WsdbBackend (spawns aztec-wsdb binary) and the generated AsyncApi
149
+ * to communicate via the NamedUnion IPC protocol.
150
+ */ export class IpcWorldState {
151
+ wsdbBackend;
152
+ instrumentation;
153
+ log;
154
+ open;
155
+ queues;
156
+ api;
157
+ /** Tracks checkpoint depth per fork (WSDB IPC doesn't return depth in response). */ checkpointDepths;
158
+ constructor(wsdbBackend, instrumentation, bindings, log = createLogger('world-state:ipc-database', bindings)){
159
+ this.wsdbBackend = wsdbBackend;
160
+ this.instrumentation = instrumentation;
161
+ this.log = log;
162
+ this.open = true;
163
+ this.queues = new Map();
164
+ this.checkpointDepths = new Map();
165
+ this.api = new AsyncApi(wsdbBackend);
166
+ this.queues.set(0, new WorldStateOpsQueue());
167
+ this.log.info('Created IPC-backed world state instance');
168
+ }
169
+ /** Returns the socket path of the underlying wsdb server. */ getSocketPath() {
170
+ return this.wsdbBackend.getSocketPath();
171
+ }
172
+ /**
173
+ * Required by `NativeWorldStateInstance` for compatibility with the in-process
174
+ * NAPI path. The IPC backend does not expose an in-process pointer; callers that
175
+ * need to reach the WSDB process must use {@link getSocketPath} instead.
176
+ */ getHandle() {
177
+ throw new Error('IpcWorldState has no in-process handle; use getSocketPath() instead');
178
+ }
179
+ async call(messageType, body, responseHandler = (response)=>response, errorHandler = (_)=>{}) {
180
+ let forkId = -1;
181
+ let committedOnly = false;
182
+ if (isWithCanonical(body)) {
183
+ forkId = 0;
184
+ } else if (isWithForkId(body)) {
185
+ forkId = body.forkId;
186
+ } else if (isWithRevision(body)) {
187
+ forkId = body.revision.forkId;
188
+ committedOnly = body.revision.includeUncommitted === false;
189
+ } else {
190
+ const _ = body;
191
+ throw new Error(`Unable to determine forkId for message=${WorldStateMessageType[messageType]}`);
192
+ }
193
+ let requestQueue = this.queues.get(forkId);
194
+ if (requestQueue === undefined) {
195
+ requestQueue = new WorldStateOpsQueue();
196
+ this.queues.set(forkId, requestQueue);
197
+ }
198
+ // The per-fork queue is cleaned up in `finally` even on error, so the JS-side queues map cannot outlive
199
+ // the native fork (e.g. when the native fork was already destroyed by an unwind/historical-prune and
200
+ // DELETE_FORK rejects with "Fork not found").
201
+ try {
202
+ const response = await requestQueue.execute(async ()=>{
203
+ assert.notEqual(messageType, WorldStateMessageType.CLOSE, 'Use close() to close the IPC instance');
204
+ assert.equal(this.open, true, 'IPC instance is closed');
205
+ let response;
206
+ try {
207
+ response = await this._sendMessage(messageType, body);
208
+ } catch (error) {
209
+ errorHandler(error.message);
210
+ throw error;
211
+ }
212
+ return responseHandler(response);
213
+ }, messageType, committedOnly);
214
+ return response;
215
+ } finally{
216
+ if (messageType === WorldStateMessageType.DELETE_FORK) {
217
+ await requestQueue.stop();
218
+ this.queues.delete(forkId);
219
+ }
220
+ }
221
+ }
222
+ async close() {
223
+ if (!this.open) {
224
+ return;
225
+ }
226
+ this.open = false;
227
+ const queue = this.queues.get(0);
228
+ // Send shutdown command. Under normal operation, the WSDB process sends its
229
+ // response before exiting (via ShutdownRequested in ipc_server.hpp). The
230
+ // try/catch is defensive: if the process is killed externally (SIGKILL, OOM)
231
+ // before responding, the pending IPC callback would be rejected by the socket
232
+ // close handler. We proceed to destroy the backend regardless.
233
+ try {
234
+ await queue.execute(async ()=>{
235
+ await this.api.wsdbShutdown({});
236
+ }, WorldStateMessageType.CLOSE, false);
237
+ } catch (err) {
238
+ this.log.debug(`wsdbShutdown completed with error: ${err.message}`);
239
+ }
240
+ await queue.stop();
241
+ if (this.wsdbBackend.destroy) {
242
+ await this.wsdbBackend.destroy();
243
+ }
244
+ }
245
+ async _sendMessage(messageType, body) {
246
+ const start = performance.now();
247
+ try {
248
+ const response = await this.dispatch(messageType, body);
249
+ const durationMs = performance.now() - start;
250
+ this.log.trace(`Call ${WorldStateMessageType[messageType]} took (ms)`, {
251
+ duration: durationMs
252
+ });
253
+ this.instrumentation.recordRoundTrip(durationMs * 1000, messageType);
254
+ return response;
255
+ } catch (error) {
256
+ this.log.error(`Call ${WorldStateMessageType[messageType]} failed: ${error}`, error);
257
+ throw error;
258
+ }
259
+ }
260
+ async dispatch(messageType, body) {
261
+ switch(messageType){
262
+ // ——— Tree info & state reference ———
263
+ case WorldStateMessageType.GET_TREE_INFO:
264
+ {
265
+ const b = body;
266
+ const resp = await this.api.wsdbGetTreeInfo({
267
+ treeid: b.treeId,
268
+ revision: toWsdbRevision(b.revision)
269
+ });
270
+ return {
271
+ treeId: resp.treeid,
272
+ root: Buffer.from(resp.root),
273
+ size: resp.size,
274
+ depth: resp.depth
275
+ };
276
+ }
277
+ case WorldStateMessageType.GET_STATE_REFERENCE:
278
+ {
279
+ const b = body;
280
+ const resp = await this.api.wsdbGetStateReference({
281
+ revision: toWsdbRevision(b.revision)
282
+ });
283
+ return {
284
+ state: convertStateRef(resp.state)
285
+ };
286
+ }
287
+ case WorldStateMessageType.GET_INITIAL_STATE_REFERENCE:
288
+ {
289
+ const resp = await this.api.wsdbGetInitialStateReference({});
290
+ return {
291
+ state: convertStateRef(resp.state)
292
+ };
293
+ }
294
+ // ——— Leaf queries ———
295
+ case WorldStateMessageType.GET_LEAF_VALUE:
296
+ {
297
+ const b = body;
298
+ const resp = await this.api.wsdbGetLeafValue({
299
+ treeid: b.treeId,
300
+ revision: toWsdbRevision(b.revision),
301
+ leafindex: Number(b.leafIndex)
302
+ });
303
+ if (!resp.value) {
304
+ return undefined;
305
+ }
306
+ return decodeLeafValue(resp.value);
307
+ }
308
+ case WorldStateMessageType.GET_LEAF_PREIMAGE:
309
+ {
310
+ const b = body;
311
+ const resp = await this.api.wsdbGetLeafPreimage({
312
+ treeid: b.treeId,
313
+ revision: toWsdbRevision(b.revision),
314
+ leafindex: Number(b.leafIndex)
315
+ });
316
+ if (!resp.preimage) {
317
+ return undefined;
318
+ }
319
+ return decodeLeafPreimage(resp.preimage);
320
+ }
321
+ case WorldStateMessageType.GET_SIBLING_PATH:
322
+ {
323
+ const b = body;
324
+ const resp = await this.api.wsdbGetSiblingPath({
325
+ treeid: b.treeId,
326
+ revision: toWsdbRevision(b.revision),
327
+ leafindex: Number(b.leafIndex)
328
+ });
329
+ return resp.path.map((p)=>Buffer.from(p));
330
+ }
331
+ case WorldStateMessageType.GET_BLOCK_NUMBERS_FOR_LEAF_INDICES:
332
+ {
333
+ const b = body;
334
+ const resp = await this.api.wsdbGetBlockNumbersForLeafIndices({
335
+ treeid: b.treeId,
336
+ revision: toWsdbRevision(b.revision),
337
+ leafindices: b.leafIndices.map(Number)
338
+ });
339
+ return {
340
+ blockNumbers: resp.blocknumbers.map((n)=>n != null ? BigInt(n) : undefined)
341
+ };
342
+ }
343
+ // ——— Find operations ———
344
+ case WorldStateMessageType.FIND_LEAF_INDICES:
345
+ {
346
+ const b = body;
347
+ const resp = await this.api.wsdbFindLeafIndices({
348
+ treeid: b.treeId,
349
+ revision: toWsdbRevision(b.revision),
350
+ leaves: b.leaves.map(serializeLeafToBytes),
351
+ startindex: Number(b.startIndex)
352
+ });
353
+ return {
354
+ indices: resp.indices.map((n)=>n != null ? BigInt(n) : undefined)
355
+ };
356
+ }
357
+ case WorldStateMessageType.FIND_LOW_LEAF:
358
+ {
359
+ const b = body;
360
+ const resp = await this.api.wsdbFindLowLeaf({
361
+ treeid: b.treeId,
362
+ revision: toWsdbRevision(b.revision),
363
+ key: new Uint8Array(b.key.toBuffer())
364
+ });
365
+ return {
366
+ alreadyPresent: resp.alreadypresent,
367
+ index: BigInt(resp.index)
368
+ };
369
+ }
370
+ case WorldStateMessageType.FIND_SIBLING_PATHS:
371
+ {
372
+ const b = body;
373
+ const resp = await this.api.wsdbFindSiblingPaths({
374
+ treeid: b.treeId,
375
+ revision: toWsdbRevision(b.revision),
376
+ leaves: b.leaves.map(serializeLeafToBytes)
377
+ });
378
+ return {
379
+ paths: resp.paths.map(convertSiblingPathAndIndex)
380
+ };
381
+ }
382
+ // ——— Mutations ———
383
+ case WorldStateMessageType.APPEND_LEAVES:
384
+ {
385
+ const b = body;
386
+ await this.api.wsdbAppendLeaves({
387
+ treeid: b.treeId,
388
+ leaves: b.leaves.map(serializeLeafToBytes),
389
+ forkid: b.forkId
390
+ });
391
+ return undefined;
392
+ }
393
+ case WorldStateMessageType.BATCH_INSERT:
394
+ {
395
+ const b = body;
396
+ const resp = await this.api.wsdbBatchInsert({
397
+ treeid: b.treeId,
398
+ leaves: b.leaves.map(serializeLeafToBytes),
399
+ subtreedepth: b.subtreeDepth,
400
+ forkid: b.forkId
401
+ });
402
+ const decoded = msgpackDecoder.unpack(Buffer.from(resp.result));
403
+ return convertUint8ArraysToBuffers(decoded);
404
+ }
405
+ case WorldStateMessageType.SEQUENTIAL_INSERT:
406
+ {
407
+ const b = body;
408
+ const resp = await this.api.wsdbSequentialInsert({
409
+ treeid: b.treeId,
410
+ leaves: b.leaves.map(serializeLeafToBytes),
411
+ forkid: b.forkId
412
+ });
413
+ const decoded = msgpackDecoder.unpack(Buffer.from(resp.result));
414
+ return convertUint8ArraysToBuffers(decoded);
415
+ }
416
+ case WorldStateMessageType.UPDATE_ARCHIVE:
417
+ {
418
+ const b = body;
419
+ await this.api.wsdbUpdateArchive({
420
+ blockstateref: blockStateRefToMap(b.blockStateRef),
421
+ blockheaderhash: new Uint8Array(b.blockHeaderHash),
422
+ forkid: b.forkId
423
+ });
424
+ return undefined;
425
+ }
426
+ // ——— Commit / Rollback ———
427
+ case WorldStateMessageType.COMMIT:
428
+ {
429
+ await this.api.wsdbCommit({});
430
+ return undefined;
431
+ }
432
+ case WorldStateMessageType.ROLLBACK:
433
+ {
434
+ await this.api.wsdbRollback({});
435
+ return undefined;
436
+ }
437
+ // ——— Block sync ———
438
+ case WorldStateMessageType.SYNC_BLOCK:
439
+ {
440
+ const b = body;
441
+ const resp = await this.api.wsdbSyncBlock({
442
+ blocknumber: Number(b.blockNumber),
443
+ blockstateref: blockStateRefToMap(b.blockStateRef),
444
+ blockheaderhash: new Uint8Array(b.blockHeaderHash),
445
+ paddednotehashes: b.paddedNoteHashes.map((l)=>new Uint8Array(l)),
446
+ paddedl1tol2messages: b.paddedL1ToL2Messages.map((l)=>new Uint8Array(l)),
447
+ paddednullifiers: b.paddedNullifiers.map((l)=>({
448
+ nullifier: new Uint8Array(l.nullifier)
449
+ })),
450
+ publicdatawrites: b.publicDataWrites.map((l)=>({
451
+ slot: new Uint8Array(l.slot),
452
+ value: new Uint8Array(l.value)
453
+ }))
454
+ });
455
+ return convertStatusFull(resp.status);
456
+ }
457
+ // ——— Fork management ———
458
+ case WorldStateMessageType.CREATE_FORK:
459
+ {
460
+ const b = body;
461
+ const resp = await this.api.wsdbCreateFork({
462
+ latest: b.latest,
463
+ blocknumber: Number(b.blockNumber)
464
+ });
465
+ return {
466
+ forkId: resp.forkid
467
+ };
468
+ }
469
+ case WorldStateMessageType.DELETE_FORK:
470
+ {
471
+ const b = body;
472
+ await this.api.wsdbDeleteFork({
473
+ forkid: b.forkId
474
+ });
475
+ return undefined;
476
+ }
477
+ // ——— Block finalization ———
478
+ case WorldStateMessageType.FINALIZE_BLOCKS:
479
+ {
480
+ const b = body;
481
+ const resp = await this.api.wsdbFinalizeBlocks({
482
+ toblocknumber: Number(b.toBlockNumber)
483
+ });
484
+ return convertStatusSummary(resp.status);
485
+ }
486
+ case WorldStateMessageType.UNWIND_BLOCKS:
487
+ {
488
+ const b = body;
489
+ const resp = await this.api.wsdbUnwindBlocks({
490
+ toblocknumber: Number(b.toBlockNumber)
491
+ });
492
+ return convertStatusFull(resp.status);
493
+ }
494
+ case WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS:
495
+ {
496
+ const b = body;
497
+ const resp = await this.api.wsdbRemoveHistoricalBlocks({
498
+ toblocknumber: Number(b.toBlockNumber)
499
+ });
500
+ return convertStatusFull(resp.status);
501
+ }
502
+ // ——— Status ———
503
+ case WorldStateMessageType.GET_STATUS:
504
+ {
505
+ const resp = await this.api.wsdbGetStatus({});
506
+ return convertStatusSummary(resp.status);
507
+ }
508
+ // ——— Checkpoints ———
509
+ case WorldStateMessageType.CREATE_CHECKPOINT:
510
+ {
511
+ const b = body;
512
+ await this.api.wsdbCreateCheckpoint({
513
+ forkid: b.forkId
514
+ });
515
+ const depth = (this.checkpointDepths.get(b.forkId) ?? 0) + 1;
516
+ this.checkpointDepths.set(b.forkId, depth);
517
+ return {
518
+ depth
519
+ };
520
+ }
521
+ case WorldStateMessageType.COMMIT_CHECKPOINT:
522
+ {
523
+ const b = body;
524
+ await this.api.wsdbCommitCheckpoint({
525
+ forkid: b.forkId
526
+ });
527
+ const depth = Math.max(0, (this.checkpointDepths.get(b.forkId) ?? 0) - 1);
528
+ this.checkpointDepths.set(b.forkId, depth);
529
+ return undefined;
530
+ }
531
+ case WorldStateMessageType.REVERT_CHECKPOINT:
532
+ {
533
+ const b = body;
534
+ await this.api.wsdbRevertCheckpoint({
535
+ forkid: b.forkId
536
+ });
537
+ const depth = Math.max(0, (this.checkpointDepths.get(b.forkId) ?? 0) - 1);
538
+ this.checkpointDepths.set(b.forkId, depth);
539
+ return undefined;
540
+ }
541
+ case WorldStateMessageType.COMMIT_ALL_CHECKPOINTS:
542
+ {
543
+ const b = body;
544
+ const targetDepth = b.depth ?? 0;
545
+ const currentDepth = this.checkpointDepths.get(b.forkId) ?? 0;
546
+ if (targetDepth === 0) {
547
+ // Commit everything — use the bulk operation
548
+ await this.api.wsdbCommitAllCheckpoints({
549
+ forkid: b.forkId
550
+ });
551
+ } else {
552
+ // Commit one level at a time down to target depth
553
+ for(let d = currentDepth; d > targetDepth; d--){
554
+ await this.api.wsdbCommitCheckpoint({
555
+ forkid: b.forkId
556
+ });
557
+ }
558
+ }
559
+ this.checkpointDepths.set(b.forkId, targetDepth);
560
+ return undefined;
561
+ }
562
+ case WorldStateMessageType.REVERT_ALL_CHECKPOINTS:
563
+ {
564
+ const b = body;
565
+ const targetDepth = b.depth ?? 0;
566
+ const currentDepth = this.checkpointDepths.get(b.forkId) ?? 0;
567
+ if (targetDepth === 0) {
568
+ // Revert everything — use the bulk operation
569
+ await this.api.wsdbRevertAllCheckpoints({
570
+ forkid: b.forkId
571
+ });
572
+ } else {
573
+ // Revert one level at a time down to target depth
574
+ for(let d = currentDepth; d > targetDepth; d--){
575
+ await this.api.wsdbRevertCheckpoint({
576
+ forkid: b.forkId
577
+ });
578
+ }
579
+ }
580
+ this.checkpointDepths.set(b.forkId, targetDepth);
581
+ return undefined;
582
+ }
583
+ // ——— Misc ———
584
+ case WorldStateMessageType.COPY_STORES:
585
+ {
586
+ const b = body;
587
+ await this.api.wsdbCopyStores({
588
+ dstpath: b.dstPath,
589
+ compact: b.compact
590
+ });
591
+ return undefined;
592
+ }
593
+ case WorldStateMessageType.CLOSE:
594
+ {
595
+ await this.api.wsdbShutdown({});
596
+ return undefined;
597
+ }
598
+ default:
599
+ throw new Error(`Unknown message type: ${messageType}`);
600
+ }
601
+ }
602
+ }
603
+ /**
604
+ * Helper to create WsdbOptions from standard world state config.
605
+ * Returns the options needed to construct a WsdbBackend.
606
+ */ export function getWsdbOptions(dataDir, wsTreeMapSizes) {
607
+ return {
608
+ treeHeights: {
609
+ [MerkleTreeId.NULLIFIER_TREE]: NULLIFIER_TREE_HEIGHT,
610
+ [MerkleTreeId.NOTE_HASH_TREE]: NOTE_HASH_TREE_HEIGHT,
611
+ [MerkleTreeId.PUBLIC_DATA_TREE]: PUBLIC_DATA_TREE_HEIGHT,
612
+ [MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: L1_TO_L2_MSG_TREE_HEIGHT,
613
+ [MerkleTreeId.ARCHIVE]: ARCHIVE_HEIGHT
614
+ },
615
+ treePrefill: {
616
+ [MerkleTreeId.NULLIFIER_TREE]: 2 * MAX_NULLIFIERS_PER_TX,
617
+ [MerkleTreeId.PUBLIC_DATA_TREE]: 2 * MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
618
+ },
619
+ mapSizes: {
620
+ [MerkleTreeId.NULLIFIER_TREE]: wsTreeMapSizes.nullifierTreeMapSizeKb,
621
+ [MerkleTreeId.NOTE_HASH_TREE]: wsTreeMapSizes.noteHashTreeMapSizeKb,
622
+ [MerkleTreeId.PUBLIC_DATA_TREE]: wsTreeMapSizes.publicDataTreeMapSizeKb,
623
+ [MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: wsTreeMapSizes.messageTreeMapSizeKb,
624
+ [MerkleTreeId.ARCHIVE]: wsTreeMapSizes.archiveTreeMapSizeKb
625
+ },
626
+ initialHeaderGeneratorPoint: DomainSeparator.BLOCK_HEADER_HASH
627
+ };
628
+ }
@@ -1,6 +1,7 @@
1
1
  import { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  import { Fr } from '@aztec/foundation/curves/bn254';
3
3
  import { type IndexedTreeLeafPreimage, SiblingPath } from '@aztec/foundation/trees';
4
+ import { BlockHash } from '@aztec/stdlib/block';
4
5
  import type { BatchInsertionResult, IndexedTreeId, MerkleTreeLeafType, MerkleTreeReadOperations, MerkleTreeWriteOperations, SequentialInsertionResult, TreeInfo } from '@aztec/stdlib/interfaces/server';
5
6
  import { MerkleTreeId, NullifierLeaf, PublicDataTreeLeaf } from '@aztec/stdlib/trees';
6
7
  import { type BlockHeader, StateReference } from '@aztec/stdlib/tx';
@@ -35,6 +36,7 @@ export declare class MerkleTreesFacade implements MerkleTreeReadOperations {
35
36
  export declare class MerkleTreesForkFacade extends MerkleTreesFacade implements MerkleTreeWriteOperations {
36
37
  private opts;
37
38
  private log;
39
+ private closePromise;
38
40
  constructor(instance: NativeWorldStateInstance, initialHeader: BlockHeader, revision: WorldStateRevision, opts: {
39
41
  closeDelayMs?: number;
40
42
  });
@@ -43,12 +45,13 @@ export declare class MerkleTreesForkFacade extends MerkleTreesFacade implements
43
45
  batchInsert<TreeHeight extends number, SubtreeSiblingPathHeight extends number, ID extends IndexedTreeId>(treeId: ID, rawLeaves: Buffer[], subtreeHeight: number): Promise<BatchInsertionResult<TreeHeight, SubtreeSiblingPathHeight>>;
44
46
  sequentialInsert<TreeHeight extends number, ID extends IndexedTreeId>(treeId: ID, rawLeaves: Buffer[]): Promise<SequentialInsertionResult<TreeHeight>>;
45
47
  close(): Promise<void>;
46
- [Symbol.dispose](): Promise<void>;
47
- createCheckpoint(): Promise<void>;
48
+ private doClose;
49
+ [Symbol.asyncDispose](): Promise<void>;
50
+ createCheckpoint(): Promise<number>;
48
51
  commitCheckpoint(): Promise<void>;
49
52
  revertCheckpoint(): Promise<void>;
50
- commitAllCheckpoints(): Promise<void>;
51
- revertAllCheckpoints(): Promise<void>;
53
+ commitAllCheckpointsTo(depth: number): Promise<void>;
54
+ revertAllCheckpointsTo(depth: number): Promise<void>;
52
55
  }
53
- export declare function serializeLeaf(leaf: Fr | NullifierLeaf | PublicDataTreeLeaf): SerializedLeafValue;
54
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVya2xlX3RyZWVzX2ZhY2FkZS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL25hdGl2ZS9tZXJrbGVfdHJlZXNfZmFjYWRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUM5RCxPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFJcEQsT0FBTyxFQUFFLEtBQUssdUJBQXVCLEVBQUUsV0FBVyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDcEYsT0FBTyxLQUFLLEVBQ1Ysb0JBQW9CLEVBQ3BCLGFBQWEsRUFDYixrQkFBa0IsRUFDbEIsd0JBQXdCLEVBQ3hCLHlCQUF5QixFQUN6Qix5QkFBeUIsRUFDekIsUUFBUSxFQUNULE1BQU0saUNBQWlDLENBQUM7QUFDekMsT0FBTyxFQUNMLFlBQVksRUFDWixhQUFhLEVBRWIsa0JBQWtCLEVBRW5CLE1BQU0scUJBQXFCLENBQUM7QUFDN0IsT0FBTyxFQUFFLEtBQUssV0FBVyxFQUF5QixjQUFjLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUMzRixPQUFPLEVBQUUsS0FBSyxrQkFBa0IsRUFBRSw0QkFBNEIsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBSWxHLE9BQU8sRUFFTCxLQUFLLG1CQUFtQixFQUl6QixNQUFNLGNBQWMsQ0FBQztBQUN0QixPQUFPLEtBQUssRUFBRSx3QkFBd0IsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBRWpGLHFCQUFhLGlCQUFrQixZQUFXLHdCQUF3QjtJQUU5RCxTQUFTLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSx3QkFBd0I7SUFDckQsU0FBUyxDQUFDLFFBQVEsQ0FBQyxhQUFhLEVBQUUsV0FBVztJQUM3QyxTQUFTLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxrQkFBa0I7SUFIakQsWUFDcUIsUUFBUSxFQUFFLHdCQUF3QixFQUNsQyxhQUFhLEVBQUUsV0FB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
56
+ export declare function serializeLeaf(leaf: Fr | BlockHash | NullifierLeaf | PublicDataTreeLeaf): SerializedLeafValue;
57
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVya2xlX3RyZWVzX2ZhY2FkZS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL25hdGl2ZS9tZXJrbGVfdHJlZXNfZmFjYWRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUM5RCxPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFJcEQsT0FBTyxFQUFFLEtBQUssdUJBQXVCLEVBQUUsV0FBVyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDcEYsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ2hELE9BQU8sS0FBSyxFQUNWLG9CQUFvQixFQUNwQixhQUFhLEVBQ2Isa0JBQWtCLEVBQ2xCLHdCQUF3QixFQUN4Qix5QkFBeUIsRUFDekIseUJBQXlCLEVBQ3pCLFFBQVEsRUFDVCxNQUFNLGlDQUFpQyxDQUFDO0FBQ3pDLE9BQU8sRUFDTCxZQUFZLEVBQ1osYUFBYSxFQUViLGtCQUFrQixFQUVuQixNQUFNLHFCQUFxQixDQUFDO0FBQzdCLE9BQU8sRUFBRSxLQUFLLFdBQVcsRUFBeUIsY0FBYyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDM0YsT0FBTyxFQUFFLEtBQUssa0JBQWtCLEVBQUUsNEJBQTRCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUlsRyxPQUFPLEVBRUwsS0FBSyxtQkFBbUIsRUFJekIsTUFBTSxjQUFjLENBQUM7QUFDdEIsT0FBTyxLQUFLLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUVqRixxQkFBYSxpQkFBa0IsWUFBVyx3QkFBd0I7SUFFOUQsU0FBUyxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsd0JBQXdCO0lBQ3JELFNBQVMsQ0FBQyxRQUFRLENBQUMsYUFBYSxFQUFFLFdBQVc7SUFDN0MsU0FBUyxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsa0JBQWtCO0lBSGpE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