@aztec/world-state 0.0.1-commit.b2a5d0dd1 → 0.0.1-commit.b3d3157a

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 (47) hide show
  1. package/dest/instrumentation/instrumentation.d.ts +4 -3
  2. package/dest/instrumentation/instrumentation.d.ts.map +1 -1
  3. package/dest/instrumentation/instrumentation.js +4 -9
  4. package/dest/native/ipc_world_state_instance.d.ts +87 -0
  5. package/dest/native/ipc_world_state_instance.d.ts.map +1 -0
  6. package/dest/native/ipc_world_state_instance.js +660 -0
  7. package/dest/native/merkle_trees_facade.d.ts +6 -3
  8. package/dest/native/merkle_trees_facade.d.ts.map +1 -1
  9. package/dest/native/merkle_trees_facade.js +53 -113
  10. package/dest/native/message.d.ts +14 -229
  11. package/dest/native/message.d.ts.map +1 -1
  12. package/dest/native/message.js +0 -42
  13. package/dest/native/native_world_state.d.ts +15 -5
  14. package/dest/native/native_world_state.d.ts.map +1 -1
  15. package/dest/native/native_world_state.js +97 -51
  16. package/dest/native/native_world_state_instance.d.ts +58 -41
  17. package/dest/native/native_world_state_instance.d.ts.map +1 -1
  18. package/dest/native/native_world_state_instance.js +5 -203
  19. package/dest/native/world_state_operation.d.ts +7 -0
  20. package/dest/native/world_state_operation.d.ts.map +1 -0
  21. package/dest/native/world_state_operation.js +5 -0
  22. package/dest/synchronizer/config.d.ts +1 -1
  23. package/dest/synchronizer/config.d.ts.map +1 -1
  24. package/dest/synchronizer/config.js +9 -10
  25. package/dest/synchronizer/factory.d.ts +3 -3
  26. package/dest/synchronizer/factory.d.ts.map +1 -1
  27. package/dest/synchronizer/factory.js +5 -5
  28. package/dest/synchronizer/server_world_state_synchronizer.d.ts +1 -1
  29. package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
  30. package/dest/synchronizer/server_world_state_synchronizer.js +16 -13
  31. package/dest/testing.js +1 -1
  32. package/package.json +14 -10
  33. package/src/instrumentation/instrumentation.ts +6 -18
  34. package/src/native/ipc_world_state_instance.ts +830 -0
  35. package/src/native/merkle_trees_facade.ts +76 -108
  36. package/src/native/message.ts +13 -296
  37. package/src/native/native_world_state.ts +113 -79
  38. package/src/native/native_world_state_instance.ts +96 -284
  39. package/src/native/world_state_operation.ts +33 -0
  40. package/src/synchronizer/config.ts +14 -10
  41. package/src/synchronizer/factory.ts +8 -7
  42. package/src/synchronizer/server_world_state_synchronizer.ts +11 -14
  43. package/src/testing.ts +1 -1
  44. package/dest/native/world_state_ops_queue.d.ts +0 -19
  45. package/dest/native/world_state_ops_queue.d.ts.map +0 -1
  46. package/dest/native/world_state_ops_queue.js +0 -146
  47. package/src/native/world_state_ops_queue.ts +0 -190
@@ -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';
@@ -19,16 +18,16 @@ import { EMPTY_GENESIS_DATA, type GenesisData, WorldStateRevision } from '@aztec
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
- WorldStateMessageType,
32
31
  type WorldStateStatusFull,
33
32
  type WorldStateStatusSummary,
34
33
  blockStateReference,
@@ -36,7 +35,7 @@ import {
36
35
  sanitizeSummary,
37
36
  treeStateReferenceToSnapshot,
38
37
  } from './message.js';
39
- import { NativeWorldState } from './native_world_state_instance.js';
38
+ import type { NativeWorldStateInstance } from './native_world_state_instance.js';
40
39
 
41
40
  // The current version of the world state database schema
42
41
  // Increment this when making incompatible changes to the database schema
@@ -50,11 +49,13 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
50
49
  private cachedStatusSummary: WorldStateStatusSummary | undefined;
51
50
 
52
51
  protected constructor(
53
- protected instance: NativeWorldState,
52
+ protected instance: NativeWorldStateInstance,
54
53
  protected readonly worldStateInstrumentation: WorldStateInstrumentation,
55
54
  protected readonly log: Logger,
56
55
  private readonly genesis: GenesisData = EMPTY_GENESIS_DATA,
57
56
  private readonly cleanup = () => Promise.resolve(),
57
+ /** Factory to recreate a fresh IpcWorldState after clear(). */
58
+ private readonly recreateInstance?: () => Promise<NativeWorldStateInstance>,
58
59
  ) {}
59
60
 
60
61
  static async new(
@@ -66,20 +67,31 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
66
67
  bindings?: LoggerBindings,
67
68
  cleanup = () => Promise.resolve(),
68
69
  ): Promise<NativeWorldStateService> {
70
+ for (const [key, value] of Object.entries(wsTreeMapSizes)) {
71
+ if (value <= 0) {
72
+ throw new Error(`Map size must be a positive number, got ${value} for ${key}`);
73
+ }
74
+ }
75
+
69
76
  const log = createLogger('world-state:database', bindings);
70
77
  const worldStateDirectory = join(dataDir, WORLD_STATE_DIR);
71
- // Create a version manager to handle versioning
78
+
72
79
  const versionManager = new DatabaseVersionManager({
73
80
  schemaVersion: WORLD_STATE_DB_VERSION,
74
81
  rollupAddress,
75
82
  dataDirectory: worldStateDirectory,
76
- onOpen: (dir: string) => {
77
- return Promise.resolve(new NativeWorldState(dir, wsTreeMapSizes, genesis, instrumentation, bindings));
78
- },
83
+ onOpen: dir => IpcWorldState.spawn(dir, wsTreeMapSizes, genesis, instrumentation, bindings),
79
84
  });
80
85
 
81
86
  const [instance] = await versionManager.open();
82
- const worldState = new this(instance, instrumentation, log, genesis, cleanup);
87
+
88
+ const recreateInstance = async () => {
89
+ await rm(worldStateDirectory, { recursive: true, force: true, maxRetries: 3 });
90
+ await mkdir(worldStateDirectory, { recursive: true });
91
+ return IpcWorldState.spawn(worldStateDirectory, wsTreeMapSizes, genesis, instrumentation, bindings);
92
+ };
93
+
94
+ const worldState = new this(instance, instrumentation, log, genesis, cleanup, recreateInstance);
83
95
  try {
84
96
  await worldState.init();
85
97
  } catch (e) {
@@ -91,7 +103,6 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
91
103
  }
92
104
 
93
105
  static async tmp(
94
- rollupAddress = EthAddress.ZERO,
95
106
  cleanupTmpDir = true,
96
107
  genesis: GenesisData = EMPTY_GENESIS_DATA,
97
108
  instrumentation = new WorldStateInstrumentation(getTelemetryClient()),
@@ -109,7 +120,8 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
109
120
  };
110
121
  log.debug(`Created temporary world state database at: ${dataDir} with tree map size: ${dbMapSizeKb}`);
111
122
 
112
- // pass a cleanup callback because process.on('beforeExit', cleanup) does not work under Jest
123
+ const instance = await IpcWorldState.spawn(dataDir, worldStateTreeMapSizes, genesis, instrumentation, bindings);
124
+
113
125
  const cleanup = async () => {
114
126
  if (cleanupTmpDir) {
115
127
  await rm(dataDir, { recursive: true, force: true, maxRetries: 3 });
@@ -119,7 +131,43 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
119
131
  }
120
132
  };
121
133
 
122
- return this.new(rollupAddress, dataDir, worldStateTreeMapSizes, genesis, instrumentation, bindings, cleanup);
134
+ const recreateInstance = async () => {
135
+ await rm(dataDir, { recursive: true, force: true, maxRetries: 3 });
136
+ await mkdir(dataDir, { recursive: true });
137
+ return IpcWorldState.spawn(dataDir, worldStateTreeMapSizes, genesis, instrumentation, bindings);
138
+ };
139
+
140
+ const worldState = new this(instance, instrumentation, log, genesis, cleanup, recreateInstance);
141
+ try {
142
+ await worldState.init();
143
+ } catch (e) {
144
+ log.error(`Error initializing tmp world state: ${e}`);
145
+ throw e;
146
+ }
147
+ return worldState;
148
+ }
149
+
150
+ static ephemeral(
151
+ genesis: GenesisData = EMPTY_GENESIS_DATA,
152
+ instrumentation = new WorldStateInstrumentation(getTelemetryClient()),
153
+ bindings?: LoggerBindings,
154
+ ): Promise<NativeWorldStateService> {
155
+ return this.tmp(/*cleanupTmpDir=*/ true, genesis, instrumentation, bindings);
156
+ }
157
+
158
+ static async fromIpc(
159
+ wsdbBackend: ConstructorParameters<typeof IpcWorldState>[0],
160
+ instrumentation = new WorldStateInstrumentation(getTelemetryClient()),
161
+ bindings?: LoggerBindings,
162
+ genesis: GenesisData = EMPTY_GENESIS_DATA,
163
+ cleanup = () => Promise.resolve(),
164
+ recreateInstance?: () => Promise<NativeWorldStateInstance>,
165
+ ): Promise<NativeWorldStateService> {
166
+ const log = createLogger('world-state:database', bindings);
167
+ const instance = new IpcWorldState(wsdbBackend, instrumentation, bindings);
168
+ const worldState = new this(instance, instrumentation, log, genesis, cleanup, recreateInstance);
169
+ await worldState.init();
170
+ return worldState;
123
171
  }
124
172
 
125
173
  protected async init() {
@@ -143,11 +191,24 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
143
191
  assert.strictEqual(initialHeaderIndex, 0n, 'Invalid initial archive state');
144
192
  }
145
193
 
146
- public async clear() {
194
+ public async clear(): Promise<void> {
195
+ if (!this.recreateInstance) {
196
+ throw new Error('clear() is not available for externally-managed IPC backends');
197
+ }
198
+ this.log.warn('Clearing world state: shutting down WSDB, deleting data, and recreating');
147
199
  await this.instance.close();
148
200
  this.cachedStatusSummary = undefined;
149
- await tryRmDir(this.instance.getDataDir(), this.log);
150
- this.instance = this.instance.clone();
201
+ this.instance = await this.recreateInstance();
202
+ await this.init();
203
+ this.log.info('World state cleared and reinitialized from genesis');
204
+ }
205
+
206
+ /** Returns the IPC path of the underlying IPC backend, if available. */
207
+ public getIpcPath(): string {
208
+ if (this.instance instanceof IpcWorldState) {
209
+ return this.instance.getIpcPath();
210
+ }
211
+ throw new Error('getIpcPath() is only available with IPC world state');
151
212
  }
152
213
 
153
214
  public getCommitted(): MerkleTreeReadOperations {
@@ -166,16 +227,15 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
166
227
  blockNumber?: BlockNumber,
167
228
  opts: { closeDelayMs?: number } = {},
168
229
  ): Promise<MerkleTreeWriteOperations> {
169
- const resp = await this.instance.call(WorldStateMessageType.CREATE_FORK, {
230
+ const forkId = await this.instance.createFork({
170
231
  latest: blockNumber === undefined,
171
232
  blockNumber: blockNumber ?? BlockNumber.ZERO,
172
- canonical: true,
173
233
  });
174
234
  return new MerkleTreesForkFacade(
175
235
  this.instance,
176
236
  this.initialHeader!,
177
237
  new WorldStateRevision(
178
- /*forkId=*/ resp.forkId,
238
+ /*forkId=*/ forkId,
179
239
  /* blockNumber=*/ WorldStateRevision.LATEST,
180
240
  /* includeUncommitted=*/ true,
181
241
  ),
@@ -219,30 +279,33 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
219
279
  });
220
280
 
221
281
  try {
222
- return await this.instance.call(
223
- WorldStateMessageType.SYNC_BLOCK,
224
- {
225
- blockNumber: l2Block.number,
226
- blockHeaderHash: (await l2Block.hash()).toBuffer(),
227
- paddedL1ToL2Messages: paddedL1ToL2Messages.map(serializeLeaf),
228
- paddedNoteHashes: paddedNoteHashes.map(serializeLeaf),
229
- paddedNullifiers: paddedNullifiers.map(serializeLeaf),
230
- publicDataWrites: publicDataWrites.map(serializeLeaf),
231
- blockStateRef: blockStateReference(l2Block.header.state),
232
- canonical: true,
233
- },
234
- this.sanitizeAndCacheSummaryFromFull.bind(this),
235
- this.deleteCachedSummary.bind(this),
236
- );
282
+ const status = await this.instance.syncBlock({
283
+ blockNumber: l2Block.number,
284
+ blockHeaderHash: (await l2Block.hash()).toBuffer(),
285
+ paddedL1ToL2Messages: paddedL1ToL2Messages.map(serializeLeaf),
286
+ paddedNoteHashes: paddedNoteHashes.map(serializeLeaf),
287
+ paddedNullifiers: paddedNullifiers.map(serializeLeaf),
288
+ publicDataWrites: publicDataWrites.map(serializeLeaf),
289
+ blockStateRef: blockStateReference(l2Block.header.state),
290
+ });
291
+ return this.sanitizeAndCacheSummaryFromFull(status);
237
292
  } catch (err) {
293
+ this.deleteCachedSummary();
238
294
  this.worldStateInstrumentation.incCriticalErrors('synch_pending_block');
239
295
  throw err;
240
296
  }
241
297
  }
242
298
 
243
299
  public async close(): Promise<void> {
244
- await this.instance.close();
245
- await this.cleanup();
300
+ try {
301
+ await this.instance.close();
302
+ } finally {
303
+ await this.cleanup();
304
+ }
305
+ }
306
+
307
+ async [Symbol.asyncDispose](): Promise<void> {
308
+ await this.close();
246
309
  }
247
310
 
248
311
  private async buildInitialHeader(): Promise<BlockHeader> {
@@ -265,7 +328,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
265
328
  return sanitized;
266
329
  }
267
330
 
268
- private deleteCachedSummary(_: string) {
331
+ private deleteCachedSummary() {
269
332
  this.cachedStatusSummary = undefined;
270
333
  }
271
334
 
@@ -276,16 +339,9 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
276
339
  */
277
340
  public async setFinalized(toBlockNumber: BlockNumber) {
278
341
  try {
279
- await this.instance.call(
280
- WorldStateMessageType.FINALIZE_BLOCKS,
281
- {
282
- toBlockNumber,
283
- canonical: true,
284
- },
285
- this.sanitizeAndCacheSummary.bind(this),
286
- this.deleteCachedSummary.bind(this),
287
- );
342
+ this.sanitizeAndCacheSummary(await this.instance.finalizeBlocks(toBlockNumber));
288
343
  } catch (err) {
344
+ this.deleteCachedSummary();
289
345
  this.worldStateInstrumentation.incCriticalErrors('finalize_block');
290
346
  throw err;
291
347
  }
@@ -299,16 +355,9 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
299
355
  */
300
356
  public async removeHistoricalBlocks(toBlockNumber: BlockNumber) {
301
357
  try {
302
- return await this.instance.call(
303
- WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS,
304
- {
305
- toBlockNumber,
306
- canonical: true,
307
- },
308
- this.sanitizeAndCacheSummaryFromFull.bind(this),
309
- this.deleteCachedSummary.bind(this),
310
- );
358
+ return this.sanitizeAndCacheSummaryFromFull(await this.instance.removeHistoricalBlocks(toBlockNumber));
311
359
  } catch (err) {
360
+ this.deleteCachedSummary();
312
361
  this.worldStateInstrumentation.incCriticalErrors('prune_historical_block');
313
362
  throw err;
314
363
  }
@@ -321,16 +370,9 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
321
370
  */
322
371
  public async unwindBlocks(toBlockNumber: BlockNumber) {
323
372
  try {
324
- return await this.instance.call(
325
- WorldStateMessageType.UNWIND_BLOCKS,
326
- {
327
- toBlockNumber,
328
- canonical: true,
329
- },
330
- this.sanitizeAndCacheSummaryFromFull.bind(this),
331
- this.deleteCachedSummary.bind(this),
332
- );
373
+ return this.sanitizeAndCacheSummaryFromFull(await this.instance.unwindBlocks(toBlockNumber));
333
374
  } catch (err) {
375
+ this.deleteCachedSummary();
334
376
  this.worldStateInstrumentation.incCriticalErrors('prune_pending_block');
335
377
  throw err;
336
378
  }
@@ -340,11 +382,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
340
382
  if (this.cachedStatusSummary !== undefined) {
341
383
  return { ...this.cachedStatusSummary };
342
384
  }
343
- return await this.instance.call(
344
- WorldStateMessageType.GET_STATUS,
345
- { canonical: true },
346
- this.sanitizeAndCacheSummary.bind(this),
347
- );
385
+ return this.sanitizeAndCacheSummary(await this.instance.getStatus());
348
386
  }
349
387
 
350
388
  updateLeaf<ID extends IndexedTreeId>(
@@ -356,14 +394,14 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
356
394
  }
357
395
 
358
396
  private async getInitialStateReference(): Promise<StateReference> {
359
- const resp = await this.instance.call(WorldStateMessageType.GET_INITIAL_STATE_REFERENCE, { canonical: true });
397
+ const state = await this.instance.getInitialStateReference();
360
398
 
361
399
  return new StateReference(
362
- treeStateReferenceToSnapshot(resp.state[MerkleTreeId.L1_TO_L2_MESSAGE_TREE]),
400
+ treeStateReferenceToSnapshot(state[MerkleTreeId.L1_TO_L2_MESSAGE_TREE]),
363
401
  new PartialStateReference(
364
- treeStateReferenceToSnapshot(resp.state[MerkleTreeId.NOTE_HASH_TREE]),
365
- treeStateReferenceToSnapshot(resp.state[MerkleTreeId.NULLIFIER_TREE]),
366
- treeStateReferenceToSnapshot(resp.state[MerkleTreeId.PUBLIC_DATA_TREE]),
402
+ treeStateReferenceToSnapshot(state[MerkleTreeId.NOTE_HASH_TREE]),
403
+ treeStateReferenceToSnapshot(state[MerkleTreeId.NULLIFIER_TREE]),
404
+ treeStateReferenceToSnapshot(state[MerkleTreeId.PUBLIC_DATA_TREE]),
367
405
  ),
368
406
  );
369
407
  }
@@ -372,11 +410,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
372
410
  dstPath: string,
373
411
  compact: boolean = true,
374
412
  ): Promise<Record<Exclude<SnapshotDataKeys, 'archiver'>, string>> {
375
- await this.instance.call(WorldStateMessageType.COPY_STORES, {
376
- dstPath,
377
- compact,
378
- canonical: true,
379
- });
413
+ await this.instance.copyStores(dstPath, compact);
380
414
  return fromEntries(NATIVE_WORLD_STATE_DBS.map(([name, dir]) => [name, join(dstPath, dir, 'data.mdb')] as const));
381
415
  }
382
416
  }