@aztec/world-state 0.0.1-commit.993d240 → 0.0.1-commit.9a89641

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 (65) 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 +61 -22
  11. package/dest/native/ipc_world_state_instance.d.ts.map +1 -1
  12. package/dest/native/ipc_world_state_instance.js +530 -488
  13. package/dest/native/merkle_trees_facade.d.ts +4 -3
  14. package/dest/native/merkle_trees_facade.d.ts.map +1 -1
  15. package/dest/native/merkle_trees_facade.js +37 -109
  16. package/dest/native/message.d.ts +14 -229
  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 +14 -28
  20. package/dest/native/native_world_state.d.ts.map +1 -1
  21. package/dest/native/native_world_state.js +108 -99
  22. package/dest/native/native_world_state_instance.d.ts +56 -43
  23. package/dest/native/native_world_state_instance.d.ts.map +1 -1
  24. package/dest/native/native_world_state_instance.js +1 -210
  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/errors.d.ts +8 -1
  29. package/dest/synchronizer/errors.d.ts.map +1 -1
  30. package/dest/synchronizer/errors.js +8 -1
  31. package/dest/synchronizer/factory.d.ts +1 -1
  32. package/dest/synchronizer/factory.d.ts.map +1 -1
  33. package/dest/synchronizer/factory.js +1 -1
  34. package/dest/synchronizer/index.d.ts +2 -1
  35. package/dest/synchronizer/index.d.ts.map +1 -1
  36. package/dest/synchronizer/index.js +1 -0
  37. package/dest/synchronizer/server_world_state_synchronizer.d.ts +11 -6
  38. package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
  39. package/dest/synchronizer/server_world_state_synchronizer.js +55 -43
  40. package/dest/test/utils.d.ts +1 -1
  41. package/dest/test/utils.d.ts.map +1 -1
  42. package/dest/test/utils.js +6 -1
  43. package/dest/testing.d.ts +2 -2
  44. package/dest/testing.d.ts.map +1 -1
  45. package/dest/testing.js +15 -4
  46. package/package.json +11 -10
  47. package/src/index.ts +1 -0
  48. package/src/instrumentation/instrumentation.ts +6 -18
  49. package/src/native/index.ts +1 -0
  50. package/src/native/ipc_world_state_instance.ts +625 -495
  51. package/src/native/merkle_trees_facade.ts +59 -104
  52. package/src/native/message.ts +13 -296
  53. package/src/native/native_world_state.ts +136 -153
  54. package/src/native/native_world_state_instance.ts +94 -291
  55. package/src/native/world_state_operation.ts +33 -0
  56. package/src/synchronizer/errors.ts +8 -0
  57. package/src/synchronizer/factory.ts +0 -1
  58. package/src/synchronizer/index.ts +1 -0
  59. package/src/synchronizer/server_world_state_synchronizer.ts +63 -30
  60. package/src/test/utils.ts +10 -1
  61. package/src/testing.ts +14 -4
  62. package/dest/native/world_state_ops_queue.d.ts +0 -19
  63. package/dest/native/world_state_ops_queue.d.ts.map +0 -1
  64. package/dest/native/world_state_ops_queue.js +0 -146
  65. package/src/native/world_state_ops_queue.ts +0 -190
@@ -1,38 +1,148 @@
1
- import { AsyncApi } from '@aztec/bb.js/aztec-wsdb';
2
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';
3
2
  import { createLogger } from '@aztec/foundation/log';
4
3
  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
- }
4
+ import { WsdbService } from '@aztec/wsdb';
5
+ import { cpus } from 'node:os';
19
6
  // ————— Request conversion helpers —————
20
7
  function toWsdbRevision(rev) {
21
8
  return {
22
- forkid: rev.forkId,
23
- blocknumber: Number(rev.blockNumber),
24
- includeuncommitted: rev.includeUncommitted
9
+ forkId: rev.forkId,
10
+ blockNumber: Number(rev.blockNumber),
11
+ includeUncommitted: rev.includeUncommitted
25
12
  };
26
13
  }
27
14
  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
- ]);
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');
34
26
  }
35
- return result;
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;
36
146
  }
37
147
  // ————— Response conversion helpers —————
38
148
  /** Convert Uint8Array fields to Buffer recursively (for opaque blob responses). */ function convertUint8ArraysToBuffers(obj) {
@@ -51,57 +161,49 @@ function blockStateRefToMap(ref) {
51
161
  }
52
162
  return obj;
53
163
  }
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
164
  /** Convert Wsdb state reference (Record<number, [Uint8Array, number]>) to NAPI format. */ function convertStateRef(state) {
63
165
  const result = {};
64
- for (const [key, [root, size]] of Object.entries(state)){
65
- result[Number(key)] = [
166
+ for (const { treeId, root, size } of state){
167
+ result[treeId] = [
66
168
  Buffer.from(root),
67
169
  BigInt(size)
68
170
  ];
69
171
  }
70
172
  return result;
71
173
  }
72
- /** Convert Wsdb WorldStateStatusSummary (lowercase) to NAPI format (camelCase). */ function convertStatusSummary(s) {
174
+ /** Convert Wsdb WorldStateStatusSummary to the native world-state format. */ function convertStatusSummary(s) {
73
175
  return {
74
- unfinalizedBlockNumber: s.unfinalizedblocknumber,
75
- finalizedBlockNumber: s.finalizedblocknumber,
76
- oldestHistoricalBlock: s.oldesthistoricalblock,
77
- treesAreSynched: s.treesaresynched
176
+ unfinalizedBlockNumber: s.unfinalizedBlockNumber,
177
+ finalizedBlockNumber: s.finalizedBlockNumber,
178
+ oldestHistoricalBlock: s.oldestHistoricalBlock,
179
+ treesAreSynched: s.treesAreSynched
78
180
  };
79
181
  }
80
182
  function convertDBStats(s) {
81
183
  return {
82
184
  name: s.name,
83
- numDataItems: s.numdataitems,
84
- totalUsedSize: s.totalusedsize
185
+ numDataItems: s.numDataItems,
186
+ totalUsedSize: s.totalUsedSize
85
187
  };
86
188
  }
87
189
  function convertTreeDBStats(s) {
88
190
  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)
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)
96
198
  };
97
199
  }
98
200
  function convertWorldStateDBStats(s) {
99
201
  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)
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)
105
207
  };
106
208
  }
107
209
  function convertTreeMeta(m) {
@@ -109,28 +211,28 @@ function convertTreeMeta(m) {
109
211
  name: m.name,
110
212
  depth: m.depth,
111
213
  size: m.size,
112
- committedSize: m.committedsize,
214
+ committedSize: m.committedSize,
113
215
  root: m.root,
114
- initialSize: m.initialsize,
115
- initialRoot: m.initialroot,
116
- oldestHistoricBlock: m.oldesthistoricblock,
117
- unfinalizedBlockHeight: m.unfinalizedblockheight,
118
- finalizedBlockHeight: m.finalizedblockheight
216
+ initialSize: m.initialSize,
217
+ initialRoot: m.initialRoot,
218
+ oldestHistoricBlock: m.oldestHistoricBlock,
219
+ unfinalizedBlockHeight: m.unfinalizedBlockHeight,
220
+ finalizedBlockHeight: m.finalizedBlockHeight
119
221
  };
120
222
  }
121
223
  function convertWorldStateMeta(m) {
122
224
  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)
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)
128
230
  };
129
231
  }
130
232
  function convertStatusFull(s) {
131
233
  return {
132
234
  summary: convertStatusSummary(s.summary),
133
- dbStats: convertWorldStateDBStats(s.dbstats),
235
+ dbStats: convertWorldStateDBStats(s.dbStats),
134
236
  meta: convertWorldStateMeta(s.meta)
135
237
  };
136
238
  }
@@ -143,466 +245,406 @@ function convertStatusFull(s) {
143
245
  path: s.path.map((p)=>Buffer.from(p))
144
246
  };
145
247
  }
248
+ // ————— Public API —————
146
249
  /**
147
250
  * IPC-backed world state instance.
148
- * Uses WsdbBackend (spawns aztec-wsdb binary) and the generated AsyncApi
251
+ * Uses WsdbService (spawns aztec-wsdb binary) and the generated AsyncApi
149
252
  * to communicate via the NamedUnion IPC protocol.
150
253
  */ export class IpcWorldState {
151
- wsdbBackend;
254
+ wsdb;
152
255
  instrumentation;
153
256
  log;
154
257
  open;
155
- queues;
156
258
  api;
157
259
  /** 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;
260
+ constructor(wsdb, instrumentation, bindings, log = createLogger('world-state:ipc-database', bindings)){
261
+ this.wsdb = wsdb;
160
262
  this.instrumentation = instrumentation;
161
263
  this.log = log;
162
264
  this.open = true;
163
- this.queues = new Map();
164
265
  this.checkpointDepths = new Map();
165
- this.api = new AsyncApi(wsdbBackend);
166
- this.queues.set(0, new WorldStateOpsQueue());
266
+ this.api = wsdb;
167
267
  this.log.info('Created IPC-backed world state instance');
168
268
  }
169
- /** Returns the socket path of the underlying wsdb server. */ getSocketPath() {
170
- return this.wsdbBackend.getSocketPath();
171
- }
172
269
  /**
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);
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()
219
279
  }
220
- }
280
+ });
281
+ return new IpcWorldState(wsdb, instrumentation, bindings);
282
+ }
283
+ getIpcPath() {
284
+ return this.wsdb.getIpcPath();
221
285
  }
222
286
  async close() {
223
287
  if (!this.open) {
224
288
  return;
225
289
  }
226
290
  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();
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');
243
626
  }
627
+ return await this.send(operation, request);
244
628
  }
245
- async _sendMessage(messageType, body) {
629
+ async send(operation, request) {
246
630
  const start = performance.now();
247
631
  try {
248
- const response = await this.dispatch(messageType, body);
632
+ const response = await request();
249
633
  const durationMs = performance.now() - start;
250
- this.log.trace(`Call ${WorldStateMessageType[messageType]} took (ms)`, {
634
+ this.log.trace(`Call ${operation} took (ms)`, {
251
635
  duration: durationMs
252
636
  });
253
- this.instrumentation.recordRoundTrip(durationMs * 1000, messageType);
637
+ this.instrumentation.recordRoundTrip(durationMs * 1000, operation);
254
638
  return response;
255
639
  } catch (error) {
256
- this.log.error(`Call ${WorldStateMessageType[messageType]} failed: ${error}`, error);
640
+ this.log.error(`Call ${operation} failed: ${error}`, error);
257
641
  throw error;
258
642
  }
259
643
  }
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
644
  }
603
645
  /**
604
646
  * Helper to create WsdbOptions from standard world state config.
605
- * Returns the options needed to construct a WsdbBackend.
647
+ * Returns the options needed to construct a wsdb service command line.
606
648
  */ export function getWsdbOptions(dataDir, wsTreeMapSizes) {
607
649
  return {
608
650
  treeHeights: {