@aztec/world-state 0.0.1-commit.b33fc05d0 → 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 (56) 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/fork_checkpoint.d.ts +7 -1
  5. package/dest/native/fork_checkpoint.d.ts.map +1 -1
  6. package/dest/native/fork_checkpoint.js +15 -3
  7. package/dest/native/ipc_world_state_instance.d.ts +87 -0
  8. package/dest/native/ipc_world_state_instance.d.ts.map +1 -0
  9. package/dest/native/ipc_world_state_instance.js +660 -0
  10. package/dest/native/merkle_trees_facade.d.ts +11 -7
  11. package/dest/native/merkle_trees_facade.d.ts.map +1 -1
  12. package/dest/native/merkle_trees_facade.js +62 -114
  13. package/dest/native/message.d.ts +14 -222
  14. package/dest/native/message.d.ts.map +1 -1
  15. package/dest/native/message.js +0 -42
  16. package/dest/native/native_world_state.d.ts +19 -7
  17. package/dest/native/native_world_state.d.ts.map +1 -1
  18. package/dest/native/native_world_state.js +108 -57
  19. package/dest/native/native_world_state_instance.d.ts +58 -41
  20. package/dest/native/native_world_state_instance.d.ts.map +1 -1
  21. package/dest/native/native_world_state_instance.js +5 -202
  22. package/dest/native/world_state_operation.d.ts +7 -0
  23. package/dest/native/world_state_operation.d.ts.map +1 -0
  24. package/dest/native/world_state_operation.js +5 -0
  25. package/dest/synchronizer/config.d.ts +1 -1
  26. package/dest/synchronizer/config.d.ts.map +1 -1
  27. package/dest/synchronizer/config.js +9 -10
  28. package/dest/synchronizer/factory.d.ts +4 -4
  29. package/dest/synchronizer/factory.d.ts.map +1 -1
  30. package/dest/synchronizer/factory.js +7 -6
  31. package/dest/synchronizer/server_world_state_synchronizer.d.ts +4 -4
  32. package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
  33. package/dest/synchronizer/server_world_state_synchronizer.js +67 -15
  34. package/dest/testing.d.ts +4 -3
  35. package/dest/testing.d.ts.map +1 -1
  36. package/dest/testing.js +10 -6
  37. package/dest/world-state-db/merkle_tree_db.d.ts +1 -10
  38. package/dest/world-state-db/merkle_tree_db.d.ts.map +1 -1
  39. package/package.json +14 -11
  40. package/src/instrumentation/instrumentation.ts +6 -18
  41. package/src/native/fork_checkpoint.ts +19 -3
  42. package/src/native/ipc_world_state_instance.ts +830 -0
  43. package/src/native/merkle_trees_facade.ts +88 -108
  44. package/src/native/message.ts +13 -287
  45. package/src/native/native_world_state.ts +124 -96
  46. package/src/native/native_world_state_instance.ts +96 -280
  47. package/src/native/world_state_operation.ts +33 -0
  48. package/src/synchronizer/config.ts +14 -10
  49. package/src/synchronizer/factory.ts +11 -10
  50. package/src/synchronizer/server_world_state_synchronizer.ts +70 -20
  51. package/src/testing.ts +8 -9
  52. package/src/world-state-db/merkle_tree_db.ts +0 -10
  53. package/dest/native/world_state_ops_queue.d.ts +0 -19
  54. package/dest/native/world_state_ops_queue.d.ts.map +0 -1
  55. package/dest/native/world_state_ops_queue.js +0 -146
  56. 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';
@@ -14,21 +13,21 @@ 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
- 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,37 +49,49 @@ 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,
55
+ private readonly genesis: GenesisData = EMPTY_GENESIS_DATA,
56
56
  private readonly cleanup = () => Promise.resolve(),
57
+ /** Factory to recreate a fresh IpcWorldState after clear(). */
58
+ private readonly recreateInstance?: () => Promise<NativeWorldStateInstance>,
57
59
  ) {}
58
60
 
59
61
  static async new(
60
62
  rollupAddress: EthAddress,
61
63
  dataDir: string,
62
64
  wsTreeMapSizes: WorldStateTreeMapSizes,
63
- prefilledPublicData: PublicDataTreeLeaf[] = [],
65
+ genesis: GenesisData = EMPTY_GENESIS_DATA,
64
66
  instrumentation = new WorldStateInstrumentation(getTelemetryClient()),
65
67
  bindings?: LoggerBindings,
66
68
  cleanup = () => Promise.resolve(),
67
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
+
68
76
  const log = createLogger('world-state:database', bindings);
69
77
  const worldStateDirectory = join(dataDir, WORLD_STATE_DIR);
70
- // Create a version manager to handle versioning
78
+
71
79
  const versionManager = new DatabaseVersionManager({
72
80
  schemaVersion: WORLD_STATE_DB_VERSION,
73
81
  rollupAddress,
74
82
  dataDirectory: worldStateDirectory,
75
- onOpen: (dir: string) => {
76
- return Promise.resolve(
77
- new NativeWorldState(dir, wsTreeMapSizes, prefilledPublicData, instrumentation, bindings),
78
- );
79
- },
83
+ onOpen: dir => IpcWorldState.spawn(dir, wsTreeMapSizes, genesis, instrumentation, bindings),
80
84
  });
81
85
 
82
86
  const [instance] = await versionManager.open();
83
- const worldState = new this(instance, instrumentation, log, 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);
84
95
  try {
85
96
  await worldState.init();
86
97
  } catch (e) {
@@ -92,9 +103,8 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
92
103
  }
93
104
 
94
105
  static async tmp(
95
- rollupAddress = EthAddress.ZERO,
96
106
  cleanupTmpDir = true,
97
- prefilledPublicData: PublicDataTreeLeaf[] = [],
107
+ genesis: GenesisData = EMPTY_GENESIS_DATA,
98
108
  instrumentation = new WorldStateInstrumentation(getTelemetryClient()),
99
109
  bindings?: LoggerBindings,
100
110
  ): Promise<NativeWorldStateService> {
@@ -110,7 +120,8 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
110
120
  };
111
121
  log.debug(`Created temporary world state database at: ${dataDir} with tree map size: ${dbMapSizeKb}`);
112
122
 
113
- // 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
+
114
125
  const cleanup = async () => {
115
126
  if (cleanupTmpDir) {
116
127
  await rm(dataDir, { recursive: true, force: true, maxRetries: 3 });
@@ -120,15 +131,43 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
120
131
  }
121
132
  };
122
133
 
123
- return this.new(
124
- rollupAddress,
125
- dataDir,
126
- worldStateTreeMapSizes,
127
- prefilledPublicData,
128
- instrumentation,
129
- bindings,
130
- cleanup,
131
- );
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;
132
171
  }
133
172
 
134
173
  protected async init() {
@@ -147,16 +186,29 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
147
186
 
148
187
  // the initial header _must_ be the first element in the archive tree
149
188
  // 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()]);
189
+ const indices = await committed.findLeafIndices(MerkleTreeId.ARCHIVE, [await this.initialHeader.hash()]);
151
190
  const initialHeaderIndex = indices[0];
152
191
  assert.strictEqual(initialHeaderIndex, 0n, 'Invalid initial archive state');
153
192
  }
154
193
 
155
- 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');
156
199
  await this.instance.close();
157
200
  this.cachedStatusSummary = undefined;
158
- await tryRmDir(this.instance.getDataDir(), this.log);
159
- 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');
160
212
  }
161
213
 
162
214
  public getCommitted(): MerkleTreeReadOperations {
@@ -175,17 +227,16 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
175
227
  blockNumber?: BlockNumber,
176
228
  opts: { closeDelayMs?: number } = {},
177
229
  ): Promise<MerkleTreeWriteOperations> {
178
- const resp = await this.instance.call(WorldStateMessageType.CREATE_FORK, {
230
+ const forkId = await this.instance.createFork({
179
231
  latest: blockNumber === undefined,
180
232
  blockNumber: blockNumber ?? BlockNumber.ZERO,
181
- canonical: true,
182
233
  });
183
234
  return new MerkleTreesForkFacade(
184
235
  this.instance,
185
236
  this.initialHeader!,
186
237
  new WorldStateRevision(
187
- /*forkId=*/ resp.forkId,
188
- /* blockNumber=*/ BlockNumber.ZERO,
238
+ /*forkId=*/ forkId,
239
+ /* blockNumber=*/ WorldStateRevision.LATEST,
189
240
  /* includeUncommitted=*/ true,
190
241
  ),
191
242
  opts,
@@ -228,35 +279,41 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
228
279
  });
229
280
 
230
281
  try {
231
- return await this.instance.call(
232
- WorldStateMessageType.SYNC_BLOCK,
233
- {
234
- blockNumber: l2Block.number,
235
- blockHeaderHash: await l2Block.hash(),
236
- paddedL1ToL2Messages: paddedL1ToL2Messages.map(serializeLeaf),
237
- paddedNoteHashes: paddedNoteHashes.map(serializeLeaf),
238
- paddedNullifiers: paddedNullifiers.map(serializeLeaf),
239
- publicDataWrites: publicDataWrites.map(serializeLeaf),
240
- blockStateRef: blockStateReference(l2Block.header.state),
241
- canonical: true,
242
- },
243
- this.sanitizeAndCacheSummaryFromFull.bind(this),
244
- this.deleteCachedSummary.bind(this),
245
- );
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);
246
292
  } catch (err) {
293
+ this.deleteCachedSummary();
247
294
  this.worldStateInstrumentation.incCriticalErrors('synch_pending_block');
248
295
  throw err;
249
296
  }
250
297
  }
251
298
 
252
299
  public async close(): Promise<void> {
253
- await this.instance.close();
254
- 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();
255
309
  }
256
310
 
257
311
  private async buildInitialHeader(): Promise<BlockHeader> {
258
312
  const state = await this.getInitialStateReference();
259
- return BlockHeader.empty({ state });
313
+ return BlockHeader.empty({
314
+ state,
315
+ globalVariables: GlobalVariables.empty({ timestamp: this.genesis.genesisTimestamp }),
316
+ });
260
317
  }
261
318
 
262
319
  private sanitizeAndCacheSummaryFromFull(response: WorldStateStatusFull) {
@@ -271,7 +328,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
271
328
  return sanitized;
272
329
  }
273
330
 
274
- private deleteCachedSummary(_: string) {
331
+ private deleteCachedSummary() {
275
332
  this.cachedStatusSummary = undefined;
276
333
  }
277
334
 
@@ -282,16 +339,9 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
282
339
  */
283
340
  public async setFinalized(toBlockNumber: BlockNumber) {
284
341
  try {
285
- await this.instance.call(
286
- WorldStateMessageType.FINALIZE_BLOCKS,
287
- {
288
- toBlockNumber,
289
- canonical: true,
290
- },
291
- this.sanitizeAndCacheSummary.bind(this),
292
- this.deleteCachedSummary.bind(this),
293
- );
342
+ this.sanitizeAndCacheSummary(await this.instance.finalizeBlocks(toBlockNumber));
294
343
  } catch (err) {
344
+ this.deleteCachedSummary();
295
345
  this.worldStateInstrumentation.incCriticalErrors('finalize_block');
296
346
  throw err;
297
347
  }
@@ -305,16 +355,9 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
305
355
  */
306
356
  public async removeHistoricalBlocks(toBlockNumber: BlockNumber) {
307
357
  try {
308
- return await this.instance.call(
309
- WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS,
310
- {
311
- toBlockNumber,
312
- canonical: true,
313
- },
314
- this.sanitizeAndCacheSummaryFromFull.bind(this),
315
- this.deleteCachedSummary.bind(this),
316
- );
358
+ return this.sanitizeAndCacheSummaryFromFull(await this.instance.removeHistoricalBlocks(toBlockNumber));
317
359
  } catch (err) {
360
+ this.deleteCachedSummary();
318
361
  this.worldStateInstrumentation.incCriticalErrors('prune_historical_block');
319
362
  throw err;
320
363
  }
@@ -327,16 +370,9 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
327
370
  */
328
371
  public async unwindBlocks(toBlockNumber: BlockNumber) {
329
372
  try {
330
- return await this.instance.call(
331
- WorldStateMessageType.UNWIND_BLOCKS,
332
- {
333
- toBlockNumber,
334
- canonical: true,
335
- },
336
- this.sanitizeAndCacheSummaryFromFull.bind(this),
337
- this.deleteCachedSummary.bind(this),
338
- );
373
+ return this.sanitizeAndCacheSummaryFromFull(await this.instance.unwindBlocks(toBlockNumber));
339
374
  } catch (err) {
375
+ this.deleteCachedSummary();
340
376
  this.worldStateInstrumentation.incCriticalErrors('prune_pending_block');
341
377
  throw err;
342
378
  }
@@ -346,11 +382,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
346
382
  if (this.cachedStatusSummary !== undefined) {
347
383
  return { ...this.cachedStatusSummary };
348
384
  }
349
- return await this.instance.call(
350
- WorldStateMessageType.GET_STATUS,
351
- { canonical: true },
352
- this.sanitizeAndCacheSummary.bind(this),
353
- );
385
+ return this.sanitizeAndCacheSummary(await this.instance.getStatus());
354
386
  }
355
387
 
356
388
  updateLeaf<ID extends IndexedTreeId>(
@@ -362,14 +394,14 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
362
394
  }
363
395
 
364
396
  private async getInitialStateReference(): Promise<StateReference> {
365
- const resp = await this.instance.call(WorldStateMessageType.GET_INITIAL_STATE_REFERENCE, { canonical: true });
397
+ const state = await this.instance.getInitialStateReference();
366
398
 
367
399
  return new StateReference(
368
- treeStateReferenceToSnapshot(resp.state[MerkleTreeId.L1_TO_L2_MESSAGE_TREE]),
400
+ treeStateReferenceToSnapshot(state[MerkleTreeId.L1_TO_L2_MESSAGE_TREE]),
369
401
  new PartialStateReference(
370
- treeStateReferenceToSnapshot(resp.state[MerkleTreeId.NOTE_HASH_TREE]),
371
- treeStateReferenceToSnapshot(resp.state[MerkleTreeId.NULLIFIER_TREE]),
372
- 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]),
373
405
  ),
374
406
  );
375
407
  }
@@ -378,11 +410,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
378
410
  dstPath: string,
379
411
  compact: boolean = true,
380
412
  ): Promise<Record<Exclude<SnapshotDataKeys, 'archiver'>, string>> {
381
- await this.instance.call(WorldStateMessageType.COPY_STORES, {
382
- dstPath,
383
- compact,
384
- canonical: true,
385
- });
413
+ await this.instance.copyStores(dstPath, compact);
386
414
  return fromEntries(NATIVE_WORLD_STATE_DBS.map(([name, dir]) => [name, join(dstPath, dir, 'data.mdb')] as const));
387
415
  }
388
416
  }