@aztec/world-state 0.0.1-commit.3100065 → 0.0.1-commit.321f6a9

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 (45) 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 -495
  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 -233
  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 +105 -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 -223
  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/factory.d.ts +1 -1
  29. package/dest/synchronizer/factory.d.ts.map +1 -1
  30. package/dest/synchronizer/factory.js +1 -1
  31. package/package.json +11 -10
  32. package/src/index.ts +1 -0
  33. package/src/instrumentation/instrumentation.ts +6 -18
  34. package/src/native/index.ts +1 -0
  35. package/src/native/ipc_world_state_instance.ts +625 -503
  36. package/src/native/merkle_trees_facade.ts +59 -104
  37. package/src/native/message.ts +13 -300
  38. package/src/native/native_world_state.ts +136 -156
  39. package/src/native/native_world_state_instance.ts +94 -308
  40. package/src/native/world_state_operation.ts +33 -0
  41. package/src/synchronizer/factory.ts +0 -1
  42. package/dest/native/world_state_ops_queue.d.ts +0 -19
  43. package/dest/native/world_state_ops_queue.d.ts.map +0 -1
  44. package/dest/native/world_state_ops_queue.js +0 -146
  45. 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,473 +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
- let shouldDeleteForkQueue = false;
202
- try {
203
- const response = await requestQueue.execute(async ()=>{
204
- assert.notEqual(messageType, WorldStateMessageType.CLOSE, 'Use close() to close the IPC instance');
205
- assert.equal(this.open, true, 'IPC instance is closed');
206
- let response;
207
- try {
208
- response = await this._sendMessage(messageType, body);
209
- } catch (error) {
210
- errorHandler(error.message);
211
- throw error;
212
- }
213
- return responseHandler(response);
214
- }, messageType, committedOnly);
215
- return response;
216
- } catch (err) {
217
- shouldDeleteForkQueue = forkId !== 0 && err?.message === 'Fork not found';
218
- throw err;
219
- } finally{
220
- if (messageType === WorldStateMessageType.DELETE_FORK || shouldDeleteForkQueue) {
221
- await requestQueue.stop();
222
- 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()
223
279
  }
224
- }
280
+ });
281
+ return new IpcWorldState(wsdb, instrumentation, bindings);
282
+ }
283
+ getIpcPath() {
284
+ return this.wsdb.getIpcPath();
225
285
  }
226
286
  async close() {
227
287
  if (!this.open) {
228
288
  return;
229
289
  }
230
290
  this.open = false;
231
- const queue = this.queues.get(0);
232
- // Send shutdown command. Under normal operation, the WSDB process sends its
233
- // response before exiting (via ShutdownRequested in ipc_server.hpp). The
234
- // try/catch is defensive: if the process is killed externally (SIGKILL, OOM)
235
- // before responding, the pending IPC callback would be rejected by the socket
236
- // close handler. We proceed to destroy the backend regardless.
237
- try {
238
- await queue.execute(async ()=>{
239
- await this.api.wsdbShutdown({});
240
- }, WorldStateMessageType.CLOSE, false);
241
- } catch (err) {
242
- this.log.debug(`wsdbShutdown completed with error: ${err.message}`);
243
- }
244
- await queue.stop();
245
- if (this.wsdbBackend.destroy) {
246
- 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');
247
626
  }
627
+ return await this.send(operation, request);
248
628
  }
249
- async _sendMessage(messageType, body) {
629
+ async send(operation, request) {
250
630
  const start = performance.now();
251
631
  try {
252
- const response = await this.dispatch(messageType, body);
632
+ const response = await request();
253
633
  const durationMs = performance.now() - start;
254
- this.log.trace(`Call ${WorldStateMessageType[messageType]} took (ms)`, {
634
+ this.log.trace(`Call ${operation} took (ms)`, {
255
635
  duration: durationMs
256
636
  });
257
- this.instrumentation.recordRoundTrip(durationMs * 1000, messageType);
637
+ this.instrumentation.recordRoundTrip(durationMs * 1000, operation);
258
638
  return response;
259
639
  } catch (error) {
260
- this.log.error(`Call ${WorldStateMessageType[messageType]} failed: ${error}`, error);
640
+ this.log.error(`Call ${operation} failed: ${error}`, error);
261
641
  throw error;
262
642
  }
263
643
  }
264
- async dispatch(messageType, body) {
265
- switch(messageType){
266
- // ——— Tree info & state reference ———
267
- case WorldStateMessageType.GET_TREE_INFO:
268
- {
269
- const b = body;
270
- const resp = await this.api.wsdbGetTreeInfo({
271
- treeid: b.treeId,
272
- revision: toWsdbRevision(b.revision)
273
- });
274
- return {
275
- treeId: resp.treeid,
276
- root: Buffer.from(resp.root),
277
- size: resp.size,
278
- depth: resp.depth
279
- };
280
- }
281
- case WorldStateMessageType.GET_STATE_REFERENCE:
282
- {
283
- const b = body;
284
- const resp = await this.api.wsdbGetStateReference({
285
- revision: toWsdbRevision(b.revision)
286
- });
287
- return {
288
- state: convertStateRef(resp.state)
289
- };
290
- }
291
- case WorldStateMessageType.GET_INITIAL_STATE_REFERENCE:
292
- {
293
- const resp = await this.api.wsdbGetInitialStateReference({});
294
- return {
295
- state: convertStateRef(resp.state)
296
- };
297
- }
298
- // ——— Leaf queries ———
299
- case WorldStateMessageType.GET_LEAF_VALUE:
300
- {
301
- const b = body;
302
- const resp = await this.api.wsdbGetLeafValue({
303
- treeid: b.treeId,
304
- revision: toWsdbRevision(b.revision),
305
- leafindex: Number(b.leafIndex)
306
- });
307
- if (!resp.value) {
308
- return undefined;
309
- }
310
- return decodeLeafValue(resp.value);
311
- }
312
- case WorldStateMessageType.GET_LEAF_PREIMAGE:
313
- {
314
- const b = body;
315
- const resp = await this.api.wsdbGetLeafPreimage({
316
- treeid: b.treeId,
317
- revision: toWsdbRevision(b.revision),
318
- leafindex: Number(b.leafIndex)
319
- });
320
- if (!resp.preimage) {
321
- return undefined;
322
- }
323
- return decodeLeafPreimage(resp.preimage);
324
- }
325
- case WorldStateMessageType.GET_SIBLING_PATH:
326
- {
327
- const b = body;
328
- const resp = await this.api.wsdbGetSiblingPath({
329
- treeid: b.treeId,
330
- revision: toWsdbRevision(b.revision),
331
- leafindex: Number(b.leafIndex)
332
- });
333
- return resp.path.map((p)=>Buffer.from(p));
334
- }
335
- case WorldStateMessageType.GET_BLOCK_NUMBERS_FOR_LEAF_INDICES:
336
- {
337
- const b = body;
338
- const resp = await this.api.wsdbGetBlockNumbersForLeafIndices({
339
- treeid: b.treeId,
340
- revision: toWsdbRevision(b.revision),
341
- leafindices: b.leafIndices.map(Number)
342
- });
343
- return {
344
- blockNumbers: resp.blocknumbers.map((n)=>n != null ? BigInt(n) : undefined)
345
- };
346
- }
347
- // ——— Find operations ———
348
- case WorldStateMessageType.FIND_LEAF_INDICES:
349
- {
350
- const b = body;
351
- const resp = await this.api.wsdbFindLeafIndices({
352
- treeid: b.treeId,
353
- revision: toWsdbRevision(b.revision),
354
- leaves: b.leaves.map(serializeLeafToBytes),
355
- startindex: Number(b.startIndex)
356
- });
357
- return {
358
- indices: resp.indices.map((n)=>n != null ? BigInt(n) : undefined)
359
- };
360
- }
361
- case WorldStateMessageType.FIND_LOW_LEAF:
362
- {
363
- const b = body;
364
- const resp = await this.api.wsdbFindLowLeaf({
365
- treeid: b.treeId,
366
- revision: toWsdbRevision(b.revision),
367
- key: new Uint8Array(b.key.toBuffer())
368
- });
369
- return {
370
- alreadyPresent: resp.alreadypresent,
371
- index: BigInt(resp.index)
372
- };
373
- }
374
- case WorldStateMessageType.FIND_SIBLING_PATHS:
375
- {
376
- const b = body;
377
- const resp = await this.api.wsdbFindSiblingPaths({
378
- treeid: b.treeId,
379
- revision: toWsdbRevision(b.revision),
380
- leaves: b.leaves.map(serializeLeafToBytes)
381
- });
382
- return {
383
- paths: resp.paths.map(convertSiblingPathAndIndex)
384
- };
385
- }
386
- // ——— Mutations ———
387
- case WorldStateMessageType.APPEND_LEAVES:
388
- {
389
- const b = body;
390
- await this.api.wsdbAppendLeaves({
391
- treeid: b.treeId,
392
- leaves: b.leaves.map(serializeLeafToBytes),
393
- forkid: b.forkId
394
- });
395
- return undefined;
396
- }
397
- case WorldStateMessageType.BATCH_INSERT:
398
- {
399
- const b = body;
400
- const resp = await this.api.wsdbBatchInsert({
401
- treeid: b.treeId,
402
- leaves: b.leaves.map(serializeLeafToBytes),
403
- subtreedepth: b.subtreeDepth,
404
- forkid: b.forkId
405
- });
406
- const decoded = msgpackDecoder.unpack(Buffer.from(resp.result));
407
- return convertUint8ArraysToBuffers(decoded);
408
- }
409
- case WorldStateMessageType.SEQUENTIAL_INSERT:
410
- {
411
- const b = body;
412
- const resp = await this.api.wsdbSequentialInsert({
413
- treeid: b.treeId,
414
- leaves: b.leaves.map(serializeLeafToBytes),
415
- forkid: b.forkId
416
- });
417
- const decoded = msgpackDecoder.unpack(Buffer.from(resp.result));
418
- return convertUint8ArraysToBuffers(decoded);
419
- }
420
- case WorldStateMessageType.UPDATE_ARCHIVE:
421
- {
422
- const b = body;
423
- await this.api.wsdbUpdateArchive({
424
- blockstateref: blockStateRefToMap(b.blockStateRef),
425
- blockheaderhash: new Uint8Array(b.blockHeaderHash),
426
- forkid: b.forkId
427
- });
428
- return undefined;
429
- }
430
- // ——— Commit / Rollback ———
431
- case WorldStateMessageType.COMMIT:
432
- {
433
- await this.api.wsdbCommit({});
434
- return undefined;
435
- }
436
- case WorldStateMessageType.ROLLBACK:
437
- {
438
- await this.api.wsdbRollback({});
439
- return undefined;
440
- }
441
- // ——— Block sync ———
442
- case WorldStateMessageType.SYNC_BLOCK:
443
- {
444
- const b = body;
445
- const resp = await this.api.wsdbSyncBlock({
446
- blocknumber: Number(b.blockNumber),
447
- blockstateref: blockStateRefToMap(b.blockStateRef),
448
- blockheaderhash: new Uint8Array(b.blockHeaderHash),
449
- // Forwarded so the wsdb (IPC) sync path enforces the same archive-root divergence check as the napi path.
450
- expectedarchiveroot: new Uint8Array(b.expectedArchiveRoot),
451
- expectedpreviousarchiveroot: new Uint8Array(b.expectedPreviousArchiveRoot),
452
- paddednotehashes: b.paddedNoteHashes.map((l)=>new Uint8Array(l)),
453
- paddedl1tol2messages: b.paddedL1ToL2Messages.map((l)=>new Uint8Array(l)),
454
- paddednullifiers: b.paddedNullifiers.map((l)=>({
455
- nullifier: new Uint8Array(l.nullifier)
456
- })),
457
- publicdatawrites: b.publicDataWrites.map((l)=>({
458
- slot: new Uint8Array(l.slot),
459
- value: new Uint8Array(l.value)
460
- }))
461
- });
462
- return convertStatusFull(resp.status);
463
- }
464
- // ——— Fork management ———
465
- case WorldStateMessageType.CREATE_FORK:
466
- {
467
- const b = body;
468
- const resp = await this.api.wsdbCreateFork({
469
- latest: b.latest,
470
- blocknumber: Number(b.blockNumber)
471
- });
472
- return {
473
- forkId: resp.forkid
474
- };
475
- }
476
- case WorldStateMessageType.DELETE_FORK:
477
- {
478
- const b = body;
479
- await this.api.wsdbDeleteFork({
480
- forkid: b.forkId
481
- });
482
- return undefined;
483
- }
484
- // ——— Block finalization ———
485
- case WorldStateMessageType.FINALIZE_BLOCKS:
486
- {
487
- const b = body;
488
- const resp = await this.api.wsdbFinalizeBlocks({
489
- toblocknumber: Number(b.toBlockNumber)
490
- });
491
- return convertStatusSummary(resp.status);
492
- }
493
- case WorldStateMessageType.UNWIND_BLOCKS:
494
- {
495
- const b = body;
496
- const resp = await this.api.wsdbUnwindBlocks({
497
- toblocknumber: Number(b.toBlockNumber)
498
- });
499
- return convertStatusFull(resp.status);
500
- }
501
- case WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS:
502
- {
503
- const b = body;
504
- const resp = await this.api.wsdbRemoveHistoricalBlocks({
505
- toblocknumber: Number(b.toBlockNumber)
506
- });
507
- return convertStatusFull(resp.status);
508
- }
509
- // ——— Status ———
510
- case WorldStateMessageType.GET_STATUS:
511
- {
512
- const resp = await this.api.wsdbGetStatus({});
513
- return convertStatusSummary(resp.status);
514
- }
515
- // ——— Checkpoints ———
516
- case WorldStateMessageType.CREATE_CHECKPOINT:
517
- {
518
- const b = body;
519
- await this.api.wsdbCreateCheckpoint({
520
- forkid: b.forkId
521
- });
522
- const depth = (this.checkpointDepths.get(b.forkId) ?? 0) + 1;
523
- this.checkpointDepths.set(b.forkId, depth);
524
- return {
525
- depth
526
- };
527
- }
528
- case WorldStateMessageType.COMMIT_CHECKPOINT:
529
- {
530
- const b = body;
531
- await this.api.wsdbCommitCheckpoint({
532
- forkid: b.forkId
533
- });
534
- const depth = Math.max(0, (this.checkpointDepths.get(b.forkId) ?? 0) - 1);
535
- this.checkpointDepths.set(b.forkId, depth);
536
- return undefined;
537
- }
538
- case WorldStateMessageType.REVERT_CHECKPOINT:
539
- {
540
- const b = body;
541
- await this.api.wsdbRevertCheckpoint({
542
- forkid: b.forkId
543
- });
544
- const depth = Math.max(0, (this.checkpointDepths.get(b.forkId) ?? 0) - 1);
545
- this.checkpointDepths.set(b.forkId, depth);
546
- return undefined;
547
- }
548
- case WorldStateMessageType.COMMIT_ALL_CHECKPOINTS:
549
- {
550
- const b = body;
551
- const targetDepth = b.depth ?? 0;
552
- const currentDepth = this.checkpointDepths.get(b.forkId) ?? 0;
553
- if (targetDepth === 0) {
554
- // Commit everything — use the bulk operation
555
- await this.api.wsdbCommitAllCheckpoints({
556
- forkid: b.forkId
557
- });
558
- } else {
559
- // Commit one level at a time down to target depth
560
- for(let d = currentDepth; d > targetDepth; d--){
561
- await this.api.wsdbCommitCheckpoint({
562
- forkid: b.forkId
563
- });
564
- }
565
- }
566
- this.checkpointDepths.set(b.forkId, targetDepth);
567
- return undefined;
568
- }
569
- case WorldStateMessageType.REVERT_ALL_CHECKPOINTS:
570
- {
571
- const b = body;
572
- const targetDepth = b.depth ?? 0;
573
- const currentDepth = this.checkpointDepths.get(b.forkId) ?? 0;
574
- if (targetDepth === 0) {
575
- // Revert everything — use the bulk operation
576
- await this.api.wsdbRevertAllCheckpoints({
577
- forkid: b.forkId
578
- });
579
- } else {
580
- // Revert one level at a time down to target depth
581
- for(let d = currentDepth; d > targetDepth; d--){
582
- await this.api.wsdbRevertCheckpoint({
583
- forkid: b.forkId
584
- });
585
- }
586
- }
587
- this.checkpointDepths.set(b.forkId, targetDepth);
588
- return undefined;
589
- }
590
- // ——— Misc ———
591
- case WorldStateMessageType.COPY_STORES:
592
- {
593
- const b = body;
594
- await this.api.wsdbCopyStores({
595
- dstpath: b.dstPath,
596
- compact: b.compact
597
- });
598
- return undefined;
599
- }
600
- case WorldStateMessageType.CLOSE:
601
- {
602
- await this.api.wsdbShutdown({});
603
- return undefined;
604
- }
605
- default:
606
- throw new Error(`Unknown message type: ${messageType}`);
607
- }
608
- }
609
644
  }
610
645
  /**
611
646
  * Helper to create WsdbOptions from standard world state config.
612
- * Returns the options needed to construct a WsdbBackend.
647
+ * Returns the options needed to construct a wsdb service command line.
613
648
  */ export function getWsdbOptions(dataDir, wsTreeMapSizes) {
614
649
  return {
615
650
  treeHeights: {