@aztec/world-state 0.69.1-devnet → 0.70.0

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.
@@ -0,0 +1,154 @@
1
+ import { promiseWithResolvers } from '@aztec/foundation/promise';
2
+ import { WorldStateMessageType } from './message.js';
3
+ // These are the set of message types that implement mutating operations
4
+ // Messages of these types require exclusive access to their given forks
5
+ export const MUTATING_MSG_TYPES = new Set([
6
+ WorldStateMessageType.APPEND_LEAVES,
7
+ WorldStateMessageType.BATCH_INSERT,
8
+ WorldStateMessageType.SEQUENTIAL_INSERT,
9
+ WorldStateMessageType.UPDATE_ARCHIVE,
10
+ WorldStateMessageType.COMMIT,
11
+ WorldStateMessageType.ROLLBACK,
12
+ WorldStateMessageType.SYNC_BLOCK,
13
+ WorldStateMessageType.CREATE_FORK,
14
+ WorldStateMessageType.DELETE_FORK,
15
+ WorldStateMessageType.FINALISE_BLOCKS,
16
+ WorldStateMessageType.UNWIND_BLOCKS,
17
+ WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS,
18
+ ]);
19
+ // This class implements the per-fork operation queue
20
+ export class WorldStateOpsQueue {
21
+ constructor() {
22
+ this.requests = [];
23
+ this.inFlightMutatingCount = 0;
24
+ this.inFlightCount = 0;
25
+ this.requestId = 0;
26
+ this.ops = new Map();
27
+ }
28
+ // The primary public api, this is where an operation is queued
29
+ // We return a promise that will ultimately be resolved/rejected with the response/error generated by the 'request' argument
30
+ execute(request, messageType, committedOnly) {
31
+ if (this.stopResolve !== undefined) {
32
+ throw new Error('Unable to send request to world state, queue already stopped');
33
+ }
34
+ const op = {
35
+ requestId: this.requestId++,
36
+ mutating: MUTATING_MSG_TYPES.has(messageType),
37
+ request,
38
+ promise: promiseWithResolvers(),
39
+ };
40
+ this.ops.set(op.requestId, op);
41
+ // Perform the appropriate action based upon the queueing rules
42
+ if (op.mutating) {
43
+ this.executeMutating(op);
44
+ }
45
+ else if (committedOnly === false) {
46
+ this.executeNonMutatingUncommitted(op);
47
+ }
48
+ else {
49
+ this.executeNonMutatingCommitted(op);
50
+ }
51
+ return op.promise.promise;
52
+ }
53
+ // Mutating requests need exclusive access
54
+ executeMutating(op) {
55
+ // If nothing is in flight then we send the request immediately
56
+ // Otherwise add to the queue
57
+ if (this.inFlightCount === 0) {
58
+ this.sendEnqueuedRequest(op);
59
+ }
60
+ else {
61
+ this.requests.push(op);
62
+ }
63
+ }
64
+ // Non mutating requests including uncommitted state
65
+ executeNonMutatingUncommitted(op) {
66
+ // If there are no mutating requests in flight and there is nothing queued
67
+ // then send the request immediately
68
+ // If a mutating request is in flight then we must wait
69
+ // If a mutating request is not in flight but something is queued then it must be a mutating request
70
+ if (this.inFlightMutatingCount == 0 && this.requests.length == 0) {
71
+ this.sendEnqueuedRequest(op);
72
+ }
73
+ else {
74
+ this.requests.push(op);
75
+ }
76
+ }
77
+ executeNonMutatingCommitted(op) {
78
+ // This is a non-mutating request for committed data
79
+ // It can always be sent
80
+ op.request()
81
+ .then(op.promise.resolve, op.promise.reject)
82
+ .finally(() => {
83
+ this.ops.delete(op.requestId);
84
+ });
85
+ }
86
+ checkAndEnqueue(completedOp) {
87
+ // As request has completed
88
+ // First we decrements the relevant in flight counters
89
+ if (completedOp.mutating) {
90
+ --this.inFlightMutatingCount;
91
+ }
92
+ --this.inFlightCount;
93
+ // If there are still requests in flight then do nothing further
94
+ if (this.inFlightCount != 0) {
95
+ return;
96
+ }
97
+ // No requests in flight, send next queued requests
98
+ // We loop and send:
99
+ // 1 mutating request if it is next in the queue
100
+ // As many non-mutating requests as we encounter until
101
+ // we exhaust the queue or we reach a mutating request
102
+ while (this.requests.length > 0) {
103
+ const next = this.requests[0];
104
+ if (next.mutating) {
105
+ if (this.inFlightCount == 0) {
106
+ // send the mutating request
107
+ this.requests.shift();
108
+ this.sendEnqueuedRequest(next);
109
+ }
110
+ // this request is mutating, we need to stop here
111
+ break;
112
+ }
113
+ else {
114
+ // not mutating, send and go round again
115
+ this.requests.shift();
116
+ this.sendEnqueuedRequest(next);
117
+ }
118
+ }
119
+ // If the queue is empty, there is nothing in flight and we have been told to stop, then resolve the stop promise
120
+ if (this.inFlightCount == 0 && this.stopResolve !== undefined) {
121
+ this.stopResolve();
122
+ }
123
+ }
124
+ sendEnqueuedRequest(op) {
125
+ // Here we increment the in flight counts before sending
126
+ ++this.inFlightCount;
127
+ if (op.mutating) {
128
+ ++this.inFlightMutatingCount;
129
+ }
130
+ // Make the request and pass the response/error through to the stored promise
131
+ op.request()
132
+ .then(op.promise.resolve, op.promise.reject)
133
+ .finally(() => {
134
+ this.checkAndEnqueue(op);
135
+ this.ops.delete(op.requestId);
136
+ });
137
+ }
138
+ stop() {
139
+ // If there is already a stop promise then return it
140
+ if (this.stopPromise) {
141
+ return this.stopPromise;
142
+ }
143
+ // Otherwise create a new one and capture the resolve method
144
+ this.stopPromise = new Promise(resolve => {
145
+ this.stopResolve = resolve;
146
+ });
147
+ // If no outstanding requests then immediately resolve the promise
148
+ if (this.requests.length == 0 && this.inFlightCount == 0 && this.stopResolve !== undefined) {
149
+ this.stopResolve();
150
+ }
151
+ return this.stopPromise;
152
+ }
153
+ }
154
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid29ybGRfc3RhdGVfb3BzX3F1ZXVlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL25hdGl2ZS93b3JsZF9zdGF0ZV9vcHNfcXVldWUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFakUsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sY0FBYyxDQUFDO0FBdUJyRCx3RUFBd0U7QUFDeEUsd0VBQXdFO0FBQ3hFLE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixHQUFHLElBQUksR0FBRyxDQUFDO0lBQ3hDLHFCQUFxQixDQUFDLGFBQWE7SUFDbkMscUJBQXFCLENBQUMsWUFBWTtJQUNsQyxxQkFBcUIsQ0FBQyxpQkFBaUI7SUFDdkMscUJBQXFCLENBQUMsY0FBYztJQUNwQyxxQkFBcUIsQ0FBQyxNQUFNO0lBQzVCLHFCQUFxQixDQUFDLFFBQVE7SUFDOUIscUJBQXFCLENBQUMsVUFBVTtJQUNoQyxxQkFBcUIsQ0FBQyxXQUFXO0lBQ2pDLHFCQUFxQixDQUFDLFdBQVc7SUFDakMscUJBQXFCLENBQUMsZUFBZTtJQUNyQyxxQkFBcUIsQ0FBQyxhQUFhO0lBQ25DLHFCQUFxQixDQUFDLHdCQUF3QjtDQUMvQyxDQUFDLENBQUM7QUFFSCxxREFBcUQ7QUFDckQsTUFBTSxPQUFPLGtCQUFrQjtJQUEvQjtRQUNVLGFBQVEsR0FBbUIsRUFBRSxDQUFDO1FBQzlCLDBCQUFxQixHQUFHLENBQUMsQ0FBQztRQUMxQixrQkFBYSxHQUFHLENBQUMsQ0FBQztRQUdsQixjQUFTLEdBQUcsQ0FBQyxDQUFDO1FBQ2QsUUFBRyxHQUE4QixJQUFJLEdBQUcsRUFBRSxDQUFDO0lBd0lyRCxDQUFDO0lBdElDLCtEQUErRDtJQUMvRCw0SEFBNEg7SUFDckgsT0FBTyxDQUFDLE9BQTJCLEVBQUUsV0FBa0MsRUFBRSxhQUFzQjtRQUNwRyxJQUFJLElBQUksQ0FBQyxXQUFXLEtBQUssU0FB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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/world-state",
3
- "version": "0.69.1-devnet",
3
+ "version": "0.70.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -62,13 +62,13 @@
62
62
  ]
63
63
  },
64
64
  "dependencies": {
65
- "@aztec/circuit-types": "0.69.1-devnet",
66
- "@aztec/circuits.js": "0.69.1-devnet",
67
- "@aztec/foundation": "0.69.1-devnet",
68
- "@aztec/kv-store": "0.69.1-devnet",
69
- "@aztec/merkle-tree": "0.69.1-devnet",
70
- "@aztec/telemetry-client": "0.69.1-devnet",
71
- "@aztec/types": "0.69.1-devnet",
65
+ "@aztec/circuit-types": "0.70.0",
66
+ "@aztec/circuits.js": "0.70.0",
67
+ "@aztec/foundation": "0.70.0",
68
+ "@aztec/kv-store": "0.70.0",
69
+ "@aztec/merkle-tree": "0.70.0",
70
+ "@aztec/telemetry-client": "0.70.0",
71
+ "@aztec/types": "0.70.0",
72
72
  "bindings": "^1.5.0",
73
73
  "msgpackr": "^1.10.2",
74
74
  "tslib": "^2.4.0",
@@ -143,7 +143,7 @@ export class MerkleTreesFacade implements MerkleTreeReadOperations {
143
143
  }
144
144
 
145
145
  async getInitialStateReference(): Promise<StateReference> {
146
- const resp = await this.instance.call(WorldStateMessageType.GET_INITIAL_STATE_REFERENCE, void 0);
146
+ const resp = await this.instance.call(WorldStateMessageType.GET_INITIAL_STATE_REFERENCE, { canonical: true });
147
147
 
148
148
  return new StateReference(
149
149
  treeStateReferenceToSnapshot(resp.state[MerkleTreeId.L1_TO_L2_MESSAGE_TREE]),
@@ -314,6 +314,10 @@ interface WithWorldStateRevision {
314
314
  revision: WorldStateRevision;
315
315
  }
316
316
 
317
+ interface WithCanonicalForkId {
318
+ canonical: true;
319
+ }
320
+
317
321
  interface WithLeafIndex {
318
322
  leafIndex: bigint;
319
323
  }
@@ -333,7 +337,7 @@ interface WithLeafValues {
333
337
  leaves: SerializedLeafValue[];
334
338
  }
335
339
 
336
- interface BlockShiftRequest {
340
+ interface BlockShiftRequest extends WithCanonicalForkId {
337
341
  toBlockNumber: bigint;
338
342
  }
339
343
 
@@ -422,7 +426,7 @@ interface UpdateArchiveRequest extends WithForkId {
422
426
  blockHeaderHash: Buffer;
423
427
  }
424
428
 
425
- interface SyncBlockRequest {
429
+ interface SyncBlockRequest extends WithCanonicalForkId {
426
430
  blockNumber: number;
427
431
  blockStateRef: BlockStateReference;
428
432
  blockHeaderHash: Fr;
@@ -432,7 +436,7 @@ interface SyncBlockRequest {
432
436
  publicDataWrites: readonly SerializedLeafValue[];
433
437
  }
434
438
 
435
- interface CreateForkRequest {
439
+ interface CreateForkRequest extends WithCanonicalForkId {
436
440
  latest: boolean;
437
441
  blockNumber: number;
438
442
  }
@@ -441,22 +445,26 @@ interface CreateForkResponse {
441
445
  forkId: number;
442
446
  }
443
447
 
444
- interface DeleteForkRequest {
445
- forkId: number;
448
+ interface DeleteForkRequest extends WithForkId {}
449
+
450
+ export type WorldStateRequestCategories = WithForkId | WithWorldStateRevision | WithCanonicalForkId;
451
+
452
+ export function isWithForkId(body: WorldStateRequestCategories): body is WithForkId {
453
+ return body && 'forkId' in body;
446
454
  }
447
455
 
448
- interface CreateForkResponse {
449
- forkId: number;
456
+ export function isWithRevision(body: WorldStateRequestCategories): body is WithWorldStateRevision {
457
+ return body && 'revision' in body;
450
458
  }
451
459
 
452
- interface DeleteForkRequest {
453
- forkId: number;
460
+ export function isWithCanonical(body: WorldStateRequestCategories): body is WithCanonicalForkId {
461
+ return body && 'canonical' in body;
454
462
  }
455
463
 
456
464
  export type WorldStateRequest = {
457
465
  [WorldStateMessageType.GET_TREE_INFO]: GetTreeInfoRequest;
458
466
  [WorldStateMessageType.GET_STATE_REFERENCE]: GetStateReferenceRequest;
459
- [WorldStateMessageType.GET_INITIAL_STATE_REFERENCE]: void;
467
+ [WorldStateMessageType.GET_INITIAL_STATE_REFERENCE]: WithCanonicalForkId;
460
468
 
461
469
  [WorldStateMessageType.GET_LEAF_VALUE]: GetLeafRequest;
462
470
  [WorldStateMessageType.GET_LEAF_PREIMAGE]: GetLeafPreImageRequest;
@@ -472,8 +480,8 @@ export type WorldStateRequest = {
472
480
 
473
481
  [WorldStateMessageType.UPDATE_ARCHIVE]: UpdateArchiveRequest;
474
482
 
475
- [WorldStateMessageType.COMMIT]: void;
476
- [WorldStateMessageType.ROLLBACK]: void;
483
+ [WorldStateMessageType.COMMIT]: WithCanonicalForkId;
484
+ [WorldStateMessageType.ROLLBACK]: WithCanonicalForkId;
477
485
 
478
486
  [WorldStateMessageType.SYNC_BLOCK]: SyncBlockRequest;
479
487
 
@@ -484,9 +492,9 @@ export type WorldStateRequest = {
484
492
  [WorldStateMessageType.UNWIND_BLOCKS]: BlockShiftRequest;
485
493
  [WorldStateMessageType.FINALISE_BLOCKS]: BlockShiftRequest;
486
494
 
487
- [WorldStateMessageType.GET_STATUS]: void;
495
+ [WorldStateMessageType.GET_STATUS]: WithCanonicalForkId;
488
496
 
489
- [WorldStateMessageType.CLOSE]: void;
497
+ [WorldStateMessageType.CLOSE]: WithCanonicalForkId;
490
498
  };
491
499
 
492
500
  export type WorldStateResponse = {
@@ -160,6 +160,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
160
160
  const resp = await this.instance.call(WorldStateMessageType.CREATE_FORK, {
161
161
  latest: blockNumber === undefined,
162
162
  blockNumber: blockNumber ?? 0,
163
+ canonical: true,
163
164
  });
164
165
  return new MerkleTreesForkFacade(this.instance, this.initialHeader!, worldStateRevision(true, resp.forkId, 0));
165
166
  }
@@ -198,6 +199,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
198
199
  paddedNullifiers: paddedNullifiers.map(serializeLeaf),
199
200
  publicDataWrites: publicDataWrites.map(serializeLeaf),
200
201
  blockStateRef: blockStateReference(l2Block.header.state),
202
+ canonical: true,
201
203
  },
202
204
  this.sanitiseAndCacheSummaryFromFull.bind(this),
203
205
  this.deleteCachedSummary.bind(this),
@@ -240,6 +242,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
240
242
  WorldStateMessageType.FINALISE_BLOCKS,
241
243
  {
242
244
  toBlockNumber,
245
+ canonical: true,
243
246
  },
244
247
  this.sanitiseAndCacheSummary.bind(this),
245
248
  this.deleteCachedSummary.bind(this),
@@ -257,6 +260,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
257
260
  WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS,
258
261
  {
259
262
  toBlockNumber,
263
+ canonical: true,
260
264
  },
261
265
  this.sanitiseAndCacheSummaryFromFull.bind(this),
262
266
  this.deleteCachedSummary.bind(this),
@@ -273,6 +277,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
273
277
  WorldStateMessageType.UNWIND_BLOCKS,
274
278
  {
275
279
  toBlockNumber,
280
+ canonical: true,
276
281
  },
277
282
  this.sanitiseAndCacheSummaryFromFull.bind(this),
278
283
  this.deleteCachedSummary.bind(this),
@@ -283,7 +288,11 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
283
288
  if (this.cachedStatusSummary !== undefined) {
284
289
  return { ...this.cachedStatusSummary };
285
290
  }
286
- return await this.instance.call(WorldStateMessageType.GET_STATUS, void 0, this.sanitiseAndCacheSummary.bind(this));
291
+ return await this.instance.call(
292
+ WorldStateMessageType.GET_STATUS,
293
+ { canonical: true },
294
+ this.sanitiseAndCacheSummary.bind(this),
295
+ );
287
296
  }
288
297
 
289
298
  updateLeaf<ID extends IndexedTreeId>(
@@ -295,7 +304,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
295
304
  }
296
305
 
297
306
  private async getInitialStateReference(): Promise<StateReference> {
298
- const resp = await this.instance.call(WorldStateMessageType.GET_INITIAL_STATE_REFERENCE, void 0);
307
+ const resp = await this.instance.call(WorldStateMessageType.GET_INITIAL_STATE_REFERENCE, { canonical: true });
299
308
 
300
309
  return new StateReference(
301
310
  treeStateReferenceToSnapshot(resp.state[MerkleTreeId.L1_TO_L2_MESSAGE_TREE]),
@@ -11,7 +11,6 @@ import {
11
11
  PUBLIC_DATA_TREE_HEIGHT,
12
12
  } from '@aztec/circuits.js';
13
13
  import { createLogger } from '@aztec/foundation/log';
14
- import { SerialQueue } from '@aztec/foundation/queue';
15
14
 
16
15
  import assert from 'assert';
17
16
  import bindings from 'bindings';
@@ -25,8 +24,13 @@ import {
25
24
  TypedMessage,
26
25
  WorldStateMessageType,
27
26
  type WorldStateRequest,
27
+ type WorldStateRequestCategories,
28
28
  type WorldStateResponse,
29
+ isWithCanonical,
30
+ isWithForkId,
31
+ isWithRevision,
29
32
  } from './message.js';
33
+ import { WorldStateOpsQueue } from './world_state_ops_queue.js';
30
34
 
31
35
  // small extension to pack an NodeJS Fr instance to a representation that the C++ code can understand
32
36
  // this only works for writes. Unpacking from C++ can't create Fr instances because the data is passed
@@ -49,7 +53,10 @@ const NATIVE_MODULE = bindings(NATIVE_LIBRARY_NAME);
49
53
  const MAX_WORLD_STATE_THREADS = +(process.env.HARDWARE_CONCURRENCY || '16');
50
54
 
51
55
  export interface NativeWorldStateInstance {
52
- call<T extends WorldStateMessageType>(messageType: T, body: WorldStateRequest[T]): Promise<WorldStateResponse[T]>;
56
+ call<T extends WorldStateMessageType>(
57
+ messageType: T,
58
+ body: WorldStateRequest[T] & WorldStateRequestCategories,
59
+ ): Promise<WorldStateResponse[T]>;
53
60
  }
54
61
 
55
62
  /**
@@ -78,8 +85,8 @@ export class NativeWorldState implements NativeWorldStateInstance {
78
85
  /** The actual native instance */
79
86
  private instance: any;
80
87
 
81
- /** Calls to the same instance are serialized */
82
- private queue = new SerialQueue();
88
+ // We maintain a map of queue to fork
89
+ private queues = new Map<number, WorldStateOpsQueue>();
83
90
 
84
91
  /** Creates a new native WorldState instance */
85
92
  constructor(
@@ -109,7 +116,8 @@ export class NativeWorldState implements NativeWorldStateInstance {
109
116
  dbMapSizeKb,
110
117
  threads,
111
118
  );
112
- this.queue.start();
119
+ // Manually create the queue for the canonical fork
120
+ this.queues.set(0, new WorldStateOpsQueue());
113
121
  }
114
122
 
115
123
  /**
@@ -120,25 +128,65 @@ export class NativeWorldState implements NativeWorldStateInstance {
120
128
  * @param errorHandler - A callback called on request error, executed on the job queue
121
129
  * @returns The response to the message
122
130
  */
123
- public call<T extends WorldStateMessageType>(
131
+ public async call<T extends WorldStateMessageType>(
124
132
  messageType: T,
125
- body: WorldStateRequest[T],
133
+ body: WorldStateRequest[T] & WorldStateRequestCategories,
126
134
  // allows for the pre-processing of responses on the job queue before being passed back
127
135
  responseHandler = (response: WorldStateResponse[T]): WorldStateResponse[T] => response,
128
136
  errorHandler = (_: string) => {},
129
137
  ): Promise<WorldStateResponse[T]> {
130
- return this.queue.put(async () => {
131
- assert.notEqual(messageType, WorldStateMessageType.CLOSE, 'Use close() to close the native instance');
132
- assert.equal(this.open, true, 'Native instance is closed');
133
- let response: WorldStateResponse[T];
134
- try {
135
- response = await this._sendMessage(messageType, body);
136
- } catch (error: any) {
137
- errorHandler(error.message);
138
- throw error;
139
- }
140
- return responseHandler(response);
141
- });
138
+ // Here we determine which fork the request is being executed against and whether it requires uncommitted data
139
+ // We use the fork Id to select the appropriate request queue and the uncommitted data flag to pass to the queue
140
+ let forkId = -1;
141
+ // We assume it includes uncommitted unless explicitly told otherwise
142
+ let committedOnly = false;
143
+
144
+ // Canonical requests ALWAYS go against the canonical fork
145
+ // These include things like block syncs/unwinds etc
146
+ // These requests don't contain a fork ID
147
+ if (isWithCanonical(body)) {
148
+ forkId = 0;
149
+ } else if (isWithForkId(body)) {
150
+ forkId = body.forkId;
151
+ } else if (isWithRevision(body)) {
152
+ forkId = body.revision.forkId;
153
+ committedOnly = body.revision.includeUncommitted === false;
154
+ } else {
155
+ const _: never = body;
156
+ throw new Error(`Unable to determine forkId for message=${WorldStateMessageType[messageType]}`);
157
+ }
158
+
159
+ // Get the queue or create a new one
160
+ let requestQueue = this.queues.get(forkId);
161
+ if (requestQueue === undefined) {
162
+ requestQueue = new WorldStateOpsQueue();
163
+ this.queues.set(forkId, requestQueue);
164
+ }
165
+
166
+ // Enqueue the request and wait for the response
167
+ const response = await requestQueue.execute(
168
+ async () => {
169
+ assert.notEqual(messageType, WorldStateMessageType.CLOSE, 'Use close() to close the native instance');
170
+ assert.equal(this.open, true, 'Native instance is closed');
171
+ let response: WorldStateResponse[T];
172
+ try {
173
+ response = await this._sendMessage(messageType, body);
174
+ } catch (error: any) {
175
+ errorHandler(error.message);
176
+ throw error;
177
+ }
178
+ return responseHandler(response);
179
+ },
180
+ messageType,
181
+ committedOnly,
182
+ );
183
+
184
+ // If the request was to delete the fork then we clean it up here
185
+ if (messageType === WorldStateMessageType.DELETE_FORK) {
186
+ await requestQueue.stop();
187
+ this.queues.delete(forkId);
188
+ }
189
+ return response;
142
190
  }
143
191
 
144
192
  /**
@@ -149,13 +197,21 @@ export class NativeWorldState implements NativeWorldStateInstance {
149
197
  return;
150
198
  }
151
199
  this.open = false;
152
- await this._sendMessage(WorldStateMessageType.CLOSE, undefined);
153
- await this.queue.end();
200
+ const queue = this.queues.get(0)!;
201
+
202
+ await queue.execute(
203
+ async () => {
204
+ await this._sendMessage(WorldStateMessageType.CLOSE, { canonical: true });
205
+ },
206
+ WorldStateMessageType.CLOSE,
207
+ false,
208
+ );
209
+ await queue.stop();
154
210
  }
155
211
 
156
212
  private async _sendMessage<T extends WorldStateMessageType>(
157
213
  messageType: T,
158
- body: WorldStateRequest[T],
214
+ body: WorldStateRequest[T] & WorldStateRequestCategories,
159
215
  ): Promise<WorldStateResponse[T]> {
160
216
  const messageId = this.nextMessageId++;
161
217
  if (body) {