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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dest/native/ipc_world_state_instance.d.ts +45 -0
  2. package/dest/native/ipc_world_state_instance.d.ts.map +1 -0
  3. package/dest/native/ipc_world_state_instance.js +750 -0
  4. package/dest/native/merkle_trees_facade.d.ts +8 -4
  5. package/dest/native/merkle_trees_facade.d.ts.map +1 -1
  6. package/dest/native/merkle_trees_facade.js +34 -15
  7. package/dest/native/message.d.ts +7 -8
  8. package/dest/native/message.d.ts.map +1 -1
  9. package/dest/native/native_world_state.d.ts +19 -7
  10. package/dest/native/native_world_state.d.ts.map +1 -1
  11. package/dest/native/native_world_state.js +89 -24
  12. package/dest/native/native_world_state_instance.d.ts +21 -39
  13. package/dest/native/native_world_state_instance.d.ts.map +1 -1
  14. package/dest/native/native_world_state_instance.js +8 -202
  15. package/dest/native/world_state_ops_queue.js +5 -5
  16. package/dest/synchronizer/config.d.ts +1 -1
  17. package/dest/synchronizer/config.d.ts.map +1 -1
  18. package/dest/synchronizer/config.js +9 -10
  19. package/dest/synchronizer/factory.d.ts +4 -4
  20. package/dest/synchronizer/factory.d.ts.map +1 -1
  21. package/dest/synchronizer/factory.js +7 -6
  22. package/dest/synchronizer/server_world_state_synchronizer.d.ts +1 -1
  23. package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
  24. package/dest/synchronizer/server_world_state_synchronizer.js +40 -11
  25. package/dest/testing.d.ts +4 -3
  26. package/dest/testing.d.ts.map +1 -1
  27. package/dest/testing.js +10 -6
  28. package/package.json +13 -10
  29. package/src/native/ipc_world_state_instance.ts +863 -0
  30. package/src/native/merkle_trees_facade.ts +41 -19
  31. package/src/native/message.ts +6 -7
  32. package/src/native/native_world_state.ts +98 -35
  33. package/src/native/native_world_state_instance.ts +28 -276
  34. package/src/native/world_state_ops_queue.ts +5 -5
  35. package/src/synchronizer/config.ts +14 -10
  36. package/src/synchronizer/factory.ts +11 -10
  37. package/src/synchronizer/server_world_state_synchronizer.ts +35 -13
  38. package/src/testing.ts +8 -9
@@ -4,6 +4,7 @@ import { createLogger } from '@aztec/foundation/log';
4
4
  import { serializeToBuffer } from '@aztec/foundation/serialize';
5
5
  import { sleep } from '@aztec/foundation/sleep';
6
6
  import { type IndexedTreeLeafPreimage, SiblingPath } from '@aztec/foundation/trees';
7
+ import { BlockHash } from '@aztec/stdlib/block';
7
8
  import type {
8
9
  BatchInsertionResult,
9
10
  IndexedTreeId,
@@ -21,7 +22,7 @@ import {
21
22
  PublicDataTreeLeafPreimage,
22
23
  } from '@aztec/stdlib/trees';
23
24
  import { type BlockHeader, PartialStateReference, StateReference } from '@aztec/stdlib/tx';
24
- import { type WorldStateRevision, WorldStateRevisionWithHandle } from '@aztec/stdlib/world-state';
25
+ import type { WorldStateRevision } from '@aztec/stdlib/world-state';
25
26
 
26
27
  import assert from 'assert';
27
28
 
@@ -45,8 +46,12 @@ export class MerkleTreesFacade implements MerkleTreeReadOperations {
45
46
  return this.initialHeader;
46
47
  }
47
48
 
48
- getRevision(): WorldStateRevisionWithHandle {
49
- return WorldStateRevisionWithHandle.fromWorldStateRevision(this.revision, this.instance.getHandle());
49
+ getRevision(): WorldStateRevision {
50
+ return this.revision;
51
+ }
52
+
53
+ getSocketPath(): string {
54
+ return this.instance.getSocketPath();
50
55
  }
51
56
 
52
57
  findLeafIndices(treeId: MerkleTreeId, values: MerkleTreeLeafType<MerkleTreeId>[]): Promise<(bigint | undefined)[]> {
@@ -118,7 +123,7 @@ export class MerkleTreesFacade implements MerkleTreeReadOperations {
118
123
 
119
124
  const leaf = deserializeLeafValue(resp);
120
125
  if (leaf instanceof Fr) {
121
- return leaf as any;
126
+ return treeId === MerkleTreeId.ARCHIVE ? (new BlockHash(leaf) as any) : (leaf as any);
122
127
  } else {
123
128
  return leaf.toBuffer() as any;
124
129
  }
@@ -207,6 +212,7 @@ export class MerkleTreesFacade implements MerkleTreeReadOperations {
207
212
 
208
213
  export class MerkleTreesForkFacade extends MerkleTreesFacade implements MerkleTreeWriteOperations {
209
214
  private log = createLogger('world-state:merkle-trees-fork-facade');
215
+ private closePromise: Promise<void> | undefined;
210
216
 
211
217
  constructor(
212
218
  instance: NativeWorldStateInstance,
@@ -228,7 +234,7 @@ export class MerkleTreesForkFacade extends MerkleTreesFacade implements MerkleTr
228
234
 
229
235
  async appendLeaves<ID extends MerkleTreeId>(treeId: ID, leaves: MerkleTreeLeafType<ID>[]): Promise<void> {
230
236
  await this.instance.call(WorldStateMessageType.APPEND_LEAVES, {
231
- leaves: leaves.map(leaf => leaf as any),
237
+ leaves: leaves.map(leaf => serializeLeaf(hydrateLeaf(treeId, leaf as any))),
232
238
  forkId: this.revision.forkId,
233
239
  treeId,
234
240
  });
@@ -249,15 +255,15 @@ export class MerkleTreesForkFacade extends MerkleTreesFacade implements MerkleTr
249
255
 
250
256
  return {
251
257
  newSubtreeSiblingPath: new SiblingPath<SubtreeSiblingPathHeight>(
252
- resp.subtree_path.length as any,
253
- resp.subtree_path,
258
+ resp.subtreePath.length as any,
259
+ resp.subtreePath,
254
260
  ),
255
- sortedNewLeaves: resp.sorted_leaves
261
+ sortedNewLeaves: resp.sortedLeaves
256
262
  .map(([leaf]) => leaf)
257
263
  .map(deserializeLeafValue)
258
264
  .map(serializeToBuffer),
259
- sortedNewLeavesIndexes: resp.sorted_leaves.map(([, index]) => index),
260
- lowLeavesWitnessData: resp.low_leaf_witness_data.map(data => ({
265
+ sortedNewLeavesIndexes: resp.sortedLeaves.map(([, index]) => index),
266
+ lowLeavesWitnessData: resp.lowLeafWitnessData.map(data => ({
261
267
  index: BigInt(data.index),
262
268
  leafPreimage: deserializeIndexedLeaf(data.leaf),
263
269
  siblingPath: new SiblingPath<TreeHeight>(data.path.length as any, data.path),
@@ -277,12 +283,12 @@ export class MerkleTreesForkFacade extends MerkleTreesFacade implements MerkleTr
277
283
  });
278
284
 
279
285
  return {
280
- lowLeavesWitnessData: resp.low_leaf_witness_data.map(data => ({
286
+ lowLeavesWitnessData: resp.lowLeafWitnessData.map(data => ({
281
287
  index: BigInt(data.index),
282
288
  leafPreimage: deserializeIndexedLeaf(data.leaf),
283
289
  siblingPath: new SiblingPath<TreeHeight>(data.path.length as any, data.path),
284
290
  })),
285
- insertionWitnessData: resp.insertion_witness_data.map(data => ({
291
+ insertionWitnessData: resp.insertionWitnessData.map(data => ({
286
292
  index: BigInt(data.index),
287
293
  leafPreimage: deserializeIndexedLeaf(data.leaf),
288
294
  siblingPath: new SiblingPath<TreeHeight>(data.path.length as any, data.path),
@@ -290,8 +296,17 @@ export class MerkleTreesForkFacade extends MerkleTreesFacade implements MerkleTr
290
296
  };
291
297
  }
292
298
 
293
- public async close(): Promise<void> {
299
+ public close(): Promise<void> {
294
300
  assert.notEqual(this.revision.forkId, 0, 'Fork ID must be set');
301
+ // Share the in-flight close promise across duplicate dispose calls so DELETE_FORK is sent at most once.
302
+ if (this.closePromise) {
303
+ return this.closePromise;
304
+ }
305
+ this.closePromise = this.doClose();
306
+ return this.closePromise;
307
+ }
308
+
309
+ private async doClose(): Promise<void> {
295
310
  try {
296
311
  await this.instance.call(WorldStateMessageType.DELETE_FORK, { forkId: this.revision.forkId });
297
312
  } catch (err: any) {
@@ -300,6 +315,12 @@ export class MerkleTreesForkFacade extends MerkleTreesFacade implements MerkleTr
300
315
  if (err?.message === 'Native instance is closed') {
301
316
  return;
302
317
  }
318
+ // Ignore "Fork not found": the native fork was already destroyed by a pending-chain unwind or a
319
+ // historical prune (both call C++ remove_forks_for_block). Fork IDs are monotonic and never reused,
320
+ // so swallowing this on close cannot mask a deletion of a different fork.
321
+ if (err?.message === 'Fork not found') {
322
+ return;
323
+ }
303
324
  throw err;
304
325
  }
305
326
  }
@@ -309,9 +330,6 @@ export class MerkleTreesForkFacade extends MerkleTreesFacade implements MerkleTr
309
330
  void sleep(this.opts.closeDelayMs)
310
331
  .then(() => this.close())
311
332
  .catch(err => {
312
- if (err && 'message' in err && err.message === 'Native instance is closed') {
313
- return; // Ignore errors due to native instance being closed
314
- }
315
333
  this.log.warn('Error closing MerkleTreesForkFacade after delay', { err });
316
334
  });
317
335
  } else {
@@ -352,9 +370,11 @@ export class MerkleTreesForkFacade extends MerkleTreesFacade implements MerkleTr
352
370
  }
353
371
  }
354
372
 
355
- function hydrateLeaf<ID extends MerkleTreeId>(treeId: ID, leaf: Fr | Buffer) {
373
+ function hydrateLeaf(treeId: MerkleTreeId, leaf: Fr | BlockHash | Buffer) {
356
374
  if (leaf instanceof Fr) {
357
375
  return leaf;
376
+ } else if (leaf instanceof BlockHash) {
377
+ return leaf.toFr();
358
378
  } else if (treeId === MerkleTreeId.NULLIFIER_TREE) {
359
379
  return NullifierLeaf.fromBuffer(leaf);
360
380
  } else if (treeId === MerkleTreeId.PUBLIC_DATA_TREE) {
@@ -364,8 +384,10 @@ function hydrateLeaf<ID extends MerkleTreeId>(treeId: ID, leaf: Fr | Buffer) {
364
384
  }
365
385
  }
366
386
 
367
- export function serializeLeaf(leaf: Fr | NullifierLeaf | PublicDataTreeLeaf): SerializedLeafValue {
368
- if (leaf instanceof Fr) {
387
+ export function serializeLeaf(leaf: Fr | BlockHash | NullifierLeaf | PublicDataTreeLeaf): SerializedLeafValue {
388
+ if (leaf instanceof BlockHash) {
389
+ return leaf.toBuffer();
390
+ } else if (leaf instanceof Fr) {
369
391
  return leaf.toBuffer();
370
392
  } else if (leaf instanceof NullifierLeaf) {
371
393
  return { nullifier: leaf.nullifier.toBuffer() };
@@ -1,7 +1,6 @@
1
1
  import { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  import { Fr } from '@aztec/foundation/curves/bn254';
3
3
  import type { Tuple } from '@aztec/foundation/serialize';
4
- import type { BlockHash } from '@aztec/stdlib/block';
5
4
  import { AppendOnlyTreeSnapshot, MerkleTreeId } from '@aztec/stdlib/trees';
6
5
  import type { StateReference } from '@aztec/stdlib/tx';
7
6
  import type { UInt32 } from '@aztec/stdlib/types';
@@ -391,24 +390,24 @@ interface BatchInsertRequest extends WithTreeId, WithForkId, WithLeaves {
391
390
  }
392
391
 
393
392
  interface BatchInsertResponse {
394
- low_leaf_witness_data: ReadonlyArray<{
393
+ lowLeafWitnessData: ReadonlyArray<{
395
394
  leaf: SerializedIndexedLeaf;
396
395
  index: bigint | number;
397
396
  path: Tuple<Buffer, number>;
398
397
  }>;
399
- sorted_leaves: ReadonlyArray<[SerializedLeafValue, UInt32]>;
400
- subtree_path: Tuple<Buffer, number>;
398
+ sortedLeaves: ReadonlyArray<[SerializedLeafValue, UInt32]>;
399
+ subtreePath: Tuple<Buffer, number>;
401
400
  }
402
401
 
403
402
  interface SequentialInsertRequest extends WithTreeId, WithForkId, WithLeaves {}
404
403
 
405
404
  interface SequentialInsertResponse {
406
- low_leaf_witness_data: ReadonlyArray<{
405
+ lowLeafWitnessData: ReadonlyArray<{
407
406
  leaf: SerializedIndexedLeaf;
408
407
  index: bigint | number;
409
408
  path: Tuple<Buffer, number>;
410
409
  }>;
411
- insertion_witness_data: ReadonlyArray<{
410
+ insertionWitnessData: ReadonlyArray<{
412
411
  leaf: SerializedIndexedLeaf;
413
412
  index: bigint | number;
414
413
  path: Tuple<Buffer, number>;
@@ -423,7 +422,7 @@ interface UpdateArchiveRequest extends WithForkId {
423
422
  interface SyncBlockRequest extends WithCanonicalForkId {
424
423
  blockNumber: BlockNumber;
425
424
  blockStateRef: BlockStateReference;
426
- blockHeaderHash: BlockHash;
425
+ blockHeaderHash: Buffer;
427
426
  paddedNoteHashes: readonly SerializedLeafValue[];
428
427
  paddedL1ToL2Messages: readonly SerializedLeafValue[];
429
428
  paddedNullifiers: readonly SerializedLeafValue[];
@@ -3,7 +3,6 @@ import { BlockNumber } from '@aztec/foundation/branded-types';
3
3
  import { fromEntries, padArrayEnd } from '@aztec/foundation/collection';
4
4
  import { Fr } from '@aztec/foundation/curves/bn254';
5
5
  import { EthAddress } from '@aztec/foundation/eth-address';
6
- import { tryRmDir } from '@aztec/foundation/fs';
7
6
  import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
8
7
  import type { L2Block } from '@aztec/stdlib/block';
9
8
  import { DatabaseVersionManager } from '@aztec/stdlib/database-version/manager';
@@ -14,18 +13,19 @@ import type {
14
13
  } from '@aztec/stdlib/interfaces/server';
15
14
  import type { SnapshotDataKeys } from '@aztec/stdlib/snapshots';
16
15
  import { MerkleTreeId, NullifierLeaf, type NullifierLeafPreimage, PublicDataTreeLeaf } from '@aztec/stdlib/trees';
17
- import { BlockHeader, PartialStateReference, StateReference } from '@aztec/stdlib/tx';
18
- import { WorldStateRevision } from '@aztec/stdlib/world-state';
16
+ import { BlockHeader, GlobalVariables, PartialStateReference, StateReference } from '@aztec/stdlib/tx';
17
+ import { EMPTY_GENESIS_DATA, type GenesisData, WorldStateRevision } from '@aztec/stdlib/world-state';
19
18
  import { getTelemetryClient } from '@aztec/telemetry-client';
20
19
 
21
20
  import assert from 'assert/strict';
22
- import { mkdtemp, rm } from 'fs/promises';
21
+ import { mkdir, mkdtemp, rm } from 'fs/promises';
23
22
  import { tmpdir } from 'os';
24
23
  import { join } from 'path';
25
24
 
26
25
  import { WorldStateInstrumentation } from '../instrumentation/instrumentation.js';
27
26
  import type { WorldStateTreeMapSizes } from '../synchronizer/factory.js';
28
27
  import type { MerkleTreeAdminDatabase as MerkleTreeDatabase } from '../world-state-db/merkle_tree_db.js';
28
+ import { IpcWorldState } from './ipc_world_state_instance.js';
29
29
  import { MerkleTreesFacade, MerkleTreesForkFacade, serializeLeaf } from './merkle_trees_facade.js';
30
30
  import {
31
31
  WorldStateMessageType,
@@ -36,7 +36,7 @@ import {
36
36
  sanitizeSummary,
37
37
  treeStateReferenceToSnapshot,
38
38
  } from './message.js';
39
- import { NativeWorldState } from './native_world_state_instance.js';
39
+ import type { NativeWorldStateInstance } from './native_world_state_instance.js';
40
40
 
41
41
  // The current version of the world state database schema
42
42
  // Increment this when making incompatible changes to the database schema
@@ -50,37 +50,49 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
50
50
  private cachedStatusSummary: WorldStateStatusSummary | undefined;
51
51
 
52
52
  protected constructor(
53
- protected instance: NativeWorldState,
53
+ protected instance: NativeWorldStateInstance,
54
54
  protected readonly worldStateInstrumentation: WorldStateInstrumentation,
55
55
  protected readonly log: Logger,
56
+ private readonly genesis: GenesisData = EMPTY_GENESIS_DATA,
56
57
  private readonly cleanup = () => Promise.resolve(),
58
+ /** Factory to recreate a fresh IpcWorldState after clear(). */
59
+ private readonly recreateInstance?: () => Promise<NativeWorldStateInstance>,
57
60
  ) {}
58
61
 
59
62
  static async new(
60
63
  rollupAddress: EthAddress,
61
64
  dataDir: string,
62
65
  wsTreeMapSizes: WorldStateTreeMapSizes,
63
- prefilledPublicData: PublicDataTreeLeaf[] = [],
66
+ genesis: GenesisData = EMPTY_GENESIS_DATA,
64
67
  instrumentation = new WorldStateInstrumentation(getTelemetryClient()),
65
68
  bindings?: LoggerBindings,
66
69
  cleanup = () => Promise.resolve(),
67
70
  ): Promise<NativeWorldStateService> {
71
+ for (const [key, value] of Object.entries(wsTreeMapSizes)) {
72
+ if (value <= 0) {
73
+ throw new Error(`Map size must be a positive number, got ${value} for ${key}`);
74
+ }
75
+ }
76
+
68
77
  const log = createLogger('world-state:database', bindings);
69
78
  const worldStateDirectory = join(dataDir, WORLD_STATE_DIR);
70
- // Create a version manager to handle versioning
79
+
71
80
  const versionManager = new DatabaseVersionManager({
72
81
  schemaVersion: WORLD_STATE_DB_VERSION,
73
82
  rollupAddress,
74
83
  dataDirectory: worldStateDirectory,
75
- onOpen: (dir: string) => {
76
- return Promise.resolve(
77
- new NativeWorldState(dir, wsTreeMapSizes, prefilledPublicData, instrumentation, bindings),
78
- );
79
- },
84
+ onOpen: dir => IpcWorldState.spawn(dir, wsTreeMapSizes, genesis, instrumentation, bindings),
80
85
  });
81
86
 
82
87
  const [instance] = await versionManager.open();
83
- const worldState = new this(instance, instrumentation, log, cleanup);
88
+
89
+ const recreateInstance = async () => {
90
+ await rm(worldStateDirectory, { recursive: true, force: true, maxRetries: 3 });
91
+ await mkdir(worldStateDirectory, { recursive: true });
92
+ return IpcWorldState.spawn(worldStateDirectory, wsTreeMapSizes, genesis, instrumentation, bindings);
93
+ };
94
+
95
+ const worldState = new this(instance, instrumentation, log, genesis, cleanup, recreateInstance);
84
96
  try {
85
97
  await worldState.init();
86
98
  } catch (e) {
@@ -92,9 +104,8 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
92
104
  }
93
105
 
94
106
  static async tmp(
95
- rollupAddress = EthAddress.ZERO,
96
107
  cleanupTmpDir = true,
97
- prefilledPublicData: PublicDataTreeLeaf[] = [],
108
+ genesis: GenesisData = EMPTY_GENESIS_DATA,
98
109
  instrumentation = new WorldStateInstrumentation(getTelemetryClient()),
99
110
  bindings?: LoggerBindings,
100
111
  ): Promise<NativeWorldStateService> {
@@ -110,7 +121,8 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
110
121
  };
111
122
  log.debug(`Created temporary world state database at: ${dataDir} with tree map size: ${dbMapSizeKb}`);
112
123
 
113
- // pass a cleanup callback because process.on('beforeExit', cleanup) does not work under Jest
124
+ const instance = await IpcWorldState.spawn(dataDir, worldStateTreeMapSizes, genesis, instrumentation, bindings);
125
+
114
126
  const cleanup = async () => {
115
127
  if (cleanupTmpDir) {
116
128
  await rm(dataDir, { recursive: true, force: true, maxRetries: 3 });
@@ -120,15 +132,43 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
120
132
  }
121
133
  };
122
134
 
123
- return this.new(
124
- rollupAddress,
125
- dataDir,
126
- worldStateTreeMapSizes,
127
- prefilledPublicData,
128
- instrumentation,
129
- bindings,
130
- cleanup,
131
- );
135
+ const recreateInstance = async () => {
136
+ await rm(dataDir, { recursive: true, force: true, maxRetries: 3 });
137
+ await mkdir(dataDir, { recursive: true });
138
+ return IpcWorldState.spawn(dataDir, worldStateTreeMapSizes, genesis, instrumentation, bindings);
139
+ };
140
+
141
+ const worldState = new this(instance, instrumentation, log, genesis, cleanup, recreateInstance);
142
+ try {
143
+ await worldState.init();
144
+ } catch (e) {
145
+ log.error(`Error initializing tmp world state: ${e}`);
146
+ throw e;
147
+ }
148
+ return worldState;
149
+ }
150
+
151
+ static ephemeral(
152
+ genesis: GenesisData = EMPTY_GENESIS_DATA,
153
+ instrumentation = new WorldStateInstrumentation(getTelemetryClient()),
154
+ bindings?: LoggerBindings,
155
+ ): Promise<NativeWorldStateService> {
156
+ return this.tmp(/*cleanupTmpDir=*/ true, genesis, instrumentation, bindings);
157
+ }
158
+
159
+ static async fromIpc(
160
+ wsdbBackend: ConstructorParameters<typeof IpcWorldState>[0],
161
+ instrumentation = new WorldStateInstrumentation(getTelemetryClient()),
162
+ bindings?: LoggerBindings,
163
+ genesis: GenesisData = EMPTY_GENESIS_DATA,
164
+ cleanup = () => Promise.resolve(),
165
+ recreateInstance?: () => Promise<NativeWorldStateInstance>,
166
+ ): Promise<NativeWorldStateService> {
167
+ const log = createLogger('world-state:database', bindings);
168
+ const instance = new IpcWorldState(wsdbBackend, instrumentation, bindings);
169
+ const worldState = new this(instance, instrumentation, log, genesis, cleanup, recreateInstance);
170
+ await worldState.init();
171
+ return worldState;
132
172
  }
133
173
 
134
174
  protected async init() {
@@ -147,16 +187,29 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
147
187
 
148
188
  // the initial header _must_ be the first element in the archive tree
149
189
  // if this assertion fails, check that the hashing done in Header in yarn-project matches the initial header hash done in world_state.cpp
150
- const indices = await committed.findLeafIndices(MerkleTreeId.ARCHIVE, [(await this.initialHeader.hash()).toFr()]);
190
+ const indices = await committed.findLeafIndices(MerkleTreeId.ARCHIVE, [await this.initialHeader.hash()]);
151
191
  const initialHeaderIndex = indices[0];
152
192
  assert.strictEqual(initialHeaderIndex, 0n, 'Invalid initial archive state');
153
193
  }
154
194
 
155
- public async clear() {
195
+ public async clear(): Promise<void> {
196
+ if (!this.recreateInstance) {
197
+ throw new Error('clear() is not available for externally-managed IPC backends');
198
+ }
199
+ this.log.warn('Clearing world state: shutting down WSDB, deleting data, and recreating');
156
200
  await this.instance.close();
157
201
  this.cachedStatusSummary = undefined;
158
- await tryRmDir(this.instance.getDataDir(), this.log);
159
- this.instance = this.instance.clone();
202
+ this.instance = await this.recreateInstance();
203
+ await this.init();
204
+ this.log.info('World state cleared and reinitialized from genesis');
205
+ }
206
+
207
+ /** Returns the socket path of the underlying IPC backend, if available. */
208
+ public getSocketPath(): string {
209
+ if (this.instance instanceof IpcWorldState) {
210
+ return this.instance.getSocketPath();
211
+ }
212
+ throw new Error('getSocketPath() is only available with IPC world state');
160
213
  }
161
214
 
162
215
  public getCommitted(): MerkleTreeReadOperations {
@@ -185,7 +238,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
185
238
  this.initialHeader!,
186
239
  new WorldStateRevision(
187
240
  /*forkId=*/ resp.forkId,
188
- /* blockNumber=*/ BlockNumber.ZERO,
241
+ /* blockNumber=*/ WorldStateRevision.LATEST,
189
242
  /* includeUncommitted=*/ true,
190
243
  ),
191
244
  opts,
@@ -232,7 +285,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
232
285
  WorldStateMessageType.SYNC_BLOCK,
233
286
  {
234
287
  blockNumber: l2Block.number,
235
- blockHeaderHash: await l2Block.hash(),
288
+ blockHeaderHash: (await l2Block.hash()).toBuffer(),
236
289
  paddedL1ToL2Messages: paddedL1ToL2Messages.map(serializeLeaf),
237
290
  paddedNoteHashes: paddedNoteHashes.map(serializeLeaf),
238
291
  paddedNullifiers: paddedNullifiers.map(serializeLeaf),
@@ -250,13 +303,23 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
250
303
  }
251
304
 
252
305
  public async close(): Promise<void> {
253
- await this.instance.close();
254
- await this.cleanup();
306
+ try {
307
+ await this.instance.close();
308
+ } finally {
309
+ await this.cleanup();
310
+ }
311
+ }
312
+
313
+ async [Symbol.asyncDispose](): Promise<void> {
314
+ await this.close();
255
315
  }
256
316
 
257
317
  private async buildInitialHeader(): Promise<BlockHeader> {
258
318
  const state = await this.getInitialStateReference();
259
- return BlockHeader.empty({ state });
319
+ return BlockHeader.empty({
320
+ state,
321
+ globalVariables: GlobalVariables.empty({ timestamp: this.genesis.genesisTimestamp }),
322
+ });
260
323
  }
261
324
 
262
325
  private sanitizeAndCacheSummaryFromFull(response: WorldStateStatusFull) {