@aztec/world-state 0.0.0-test.1 → 0.0.1-commit.0b941701

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 (63) hide show
  1. package/dest/index.d.ts +1 -1
  2. package/dest/instrumentation/instrumentation.d.ts +6 -4
  3. package/dest/instrumentation/instrumentation.d.ts.map +1 -1
  4. package/dest/instrumentation/instrumentation.js +19 -42
  5. package/dest/native/bench_metrics.d.ts +23 -0
  6. package/dest/native/bench_metrics.d.ts.map +1 -0
  7. package/dest/native/bench_metrics.js +81 -0
  8. package/dest/native/fork_checkpoint.d.ts +1 -1
  9. package/dest/native/fork_checkpoint.d.ts.map +1 -1
  10. package/dest/native/index.d.ts +1 -1
  11. package/dest/native/merkle_trees_facade.d.ts +19 -7
  12. package/dest/native/merkle_trees_facade.d.ts.map +1 -1
  13. package/dest/native/merkle_trees_facade.js +76 -14
  14. package/dest/native/message.d.ts +72 -51
  15. package/dest/native/message.d.ts.map +1 -1
  16. package/dest/native/message.js +61 -61
  17. package/dest/native/native_world_state.d.ts +26 -18
  18. package/dest/native/native_world_state.d.ts.map +1 -1
  19. package/dest/native/native_world_state.js +98 -37
  20. package/dest/native/native_world_state_instance.d.ts +20 -4
  21. package/dest/native/native_world_state_instance.d.ts.map +1 -1
  22. package/dest/native/native_world_state_instance.js +42 -3
  23. package/dest/native/world_state_ops_queue.d.ts +1 -1
  24. package/dest/native/world_state_ops_queue.d.ts.map +1 -1
  25. package/dest/native/world_state_ops_queue.js +1 -1
  26. package/dest/synchronizer/config.d.ts +12 -4
  27. package/dest/synchronizer/config.d.ts.map +1 -1
  28. package/dest/synchronizer/config.js +27 -7
  29. package/dest/synchronizer/errors.d.ts +4 -0
  30. package/dest/synchronizer/errors.d.ts.map +1 -0
  31. package/dest/synchronizer/errors.js +5 -0
  32. package/dest/synchronizer/factory.d.ts +9 -2
  33. package/dest/synchronizer/factory.d.ts.map +1 -1
  34. package/dest/synchronizer/factory.js +9 -4
  35. package/dest/synchronizer/index.d.ts +1 -1
  36. package/dest/synchronizer/server_world_state_synchronizer.d.ts +21 -31
  37. package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
  38. package/dest/synchronizer/server_world_state_synchronizer.js +139 -89
  39. package/dest/test/index.d.ts +1 -1
  40. package/dest/test/utils.d.ts +12 -5
  41. package/dest/test/utils.d.ts.map +1 -1
  42. package/dest/test/utils.js +54 -47
  43. package/dest/testing.d.ts +3 -3
  44. package/dest/testing.d.ts.map +1 -1
  45. package/dest/testing.js +7 -11
  46. package/dest/world-state-db/index.d.ts +1 -1
  47. package/dest/world-state-db/merkle_tree_db.d.ts +12 -9
  48. package/dest/world-state-db/merkle_tree_db.d.ts.map +1 -1
  49. package/package.json +24 -24
  50. package/src/instrumentation/instrumentation.ts +24 -44
  51. package/src/native/bench_metrics.ts +91 -0
  52. package/src/native/merkle_trees_facade.ts +83 -18
  53. package/src/native/message.ts +92 -73
  54. package/src/native/native_world_state.ts +119 -49
  55. package/src/native/native_world_state_instance.ts +59 -8
  56. package/src/native/world_state_ops_queue.ts +1 -1
  57. package/src/synchronizer/config.ts +48 -16
  58. package/src/synchronizer/errors.ts +5 -0
  59. package/src/synchronizer/factory.ts +31 -8
  60. package/src/synchronizer/server_world_state_synchronizer.ts +166 -115
  61. package/src/test/utils.ts +92 -82
  62. package/src/testing.ts +4 -8
  63. package/src/world-state-db/merkle_tree_db.ts +16 -8
@@ -1,8 +1,10 @@
1
1
  import { MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
2
- import { padArrayEnd } from '@aztec/foundation/collection';
2
+ import { BlockNumber } from '@aztec/foundation/branded-types';
3
+ import { fromEntries, padArrayEnd } from '@aztec/foundation/collection';
4
+ import { Fr } from '@aztec/foundation/curves/bn254';
3
5
  import { EthAddress } from '@aztec/foundation/eth-address';
4
- import { Fr } from '@aztec/foundation/fields';
5
- import { createLogger } from '@aztec/foundation/log';
6
+ import { tryRmDir } from '@aztec/foundation/fs';
7
+ import { type Logger, createLogger } from '@aztec/foundation/log';
6
8
  import type { L2Block } from '@aztec/stdlib/block';
7
9
  import { DatabaseVersionManager } from '@aztec/stdlib/database-version';
8
10
  import type {
@@ -10,8 +12,10 @@ import type {
10
12
  MerkleTreeReadOperations,
11
13
  MerkleTreeWriteOperations,
12
14
  } from '@aztec/stdlib/interfaces/server';
15
+ import type { SnapshotDataKeys } from '@aztec/stdlib/snapshots';
13
16
  import { MerkleTreeId, NullifierLeaf, type NullifierLeafPreimage, PublicDataTreeLeaf } from '@aztec/stdlib/trees';
14
17
  import { BlockHeader, PartialStateReference, StateReference } from '@aztec/stdlib/tx';
18
+ import { WorldStateRevision } from '@aztec/stdlib/world-state';
15
19
  import { getTelemetryClient } from '@aztec/telemetry-client';
16
20
 
17
21
  import assert from 'assert/strict';
@@ -20,6 +24,7 @@ import { tmpdir } from 'os';
20
24
  import { join } from 'path';
21
25
 
22
26
  import { WorldStateInstrumentation } from '../instrumentation/instrumentation.js';
27
+ import type { WorldStateTreeMapSizes } from '../synchronizer/factory.js';
23
28
  import type { MerkleTreeAdminDatabase as MerkleTreeDatabase } from '../world-state-db/merkle_tree_db.js';
24
29
  import { MerkleTreesFacade, MerkleTreesForkFacade, serializeLeaf } from './merkle_trees_facade.js';
25
30
  import {
@@ -27,16 +32,17 @@ import {
27
32
  type WorldStateStatusFull,
28
33
  type WorldStateStatusSummary,
29
34
  blockStateReference,
30
- sanitiseFullStatus,
31
- sanitiseSummary,
35
+ sanitizeFullStatus,
36
+ sanitizeSummary,
32
37
  treeStateReferenceToSnapshot,
33
- worldStateRevision,
34
38
  } from './message.js';
35
39
  import { NativeWorldState } from './native_world_state_instance.js';
36
40
 
37
41
  // The current version of the world state database schema
38
42
  // Increment this when making incompatible changes to the database schema
39
- export const WORLD_STATE_DB_VERSION = 1; // The initial version
43
+ export const WORLD_STATE_DB_VERSION = 2; // The initial version
44
+
45
+ export const WORLD_STATE_DIR = 'world_state';
40
46
 
41
47
  export class NativeWorldStateService implements MerkleTreeDatabase {
42
48
  protected initialHeader: BlockHeader | undefined;
@@ -44,38 +50,38 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
44
50
  private cachedStatusSummary: WorldStateStatusSummary | undefined;
45
51
 
46
52
  protected constructor(
47
- protected readonly instance: NativeWorldState,
53
+ protected instance: NativeWorldState,
48
54
  protected readonly worldStateInstrumentation: WorldStateInstrumentation,
49
- protected readonly log = createLogger('world-state:database'),
55
+ protected readonly log: Logger = createLogger('world-state:database'),
50
56
  private readonly cleanup = () => Promise.resolve(),
51
57
  ) {}
52
58
 
53
59
  static async new(
54
60
  rollupAddress: EthAddress,
55
61
  dataDir: string,
56
- dbMapSizeKb: number,
62
+ wsTreeMapSizes: WorldStateTreeMapSizes,
57
63
  prefilledPublicData: PublicDataTreeLeaf[] = [],
58
64
  instrumentation = new WorldStateInstrumentation(getTelemetryClient()),
59
65
  log = createLogger('world-state:database'),
60
66
  cleanup = () => Promise.resolve(),
61
67
  ): Promise<NativeWorldStateService> {
62
- const worldStateDirectory = join(dataDir, 'world_state');
68
+ const worldStateDirectory = join(dataDir, WORLD_STATE_DIR);
63
69
  // Create a version manager to handle versioning
64
- const versionManager = new DatabaseVersionManager(
65
- WORLD_STATE_DB_VERSION,
70
+ const versionManager = new DatabaseVersionManager({
71
+ schemaVersion: WORLD_STATE_DB_VERSION,
66
72
  rollupAddress,
67
- worldStateDirectory,
68
- (dir: string) => {
69
- return Promise.resolve(new NativeWorldState(dir, dbMapSizeKb, prefilledPublicData, instrumentation));
73
+ dataDirectory: worldStateDirectory,
74
+ onOpen: (dir: string) => {
75
+ return Promise.resolve(new NativeWorldState(dir, wsTreeMapSizes, prefilledPublicData, instrumentation));
70
76
  },
71
- );
77
+ });
72
78
 
73
79
  const [instance] = await versionManager.open();
74
80
  const worldState = new this(instance, instrumentation, log, cleanup);
75
81
  try {
76
82
  await worldState.init();
77
83
  } catch (e) {
78
- log.error(`Error initialising world state: ${e}`);
84
+ log.error(`Error initializing world state: ${e}`);
79
85
  throw e;
80
86
  }
81
87
 
@@ -91,7 +97,14 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
91
97
  const log = createLogger('world-state:database');
92
98
  const dataDir = await mkdtemp(join(tmpdir(), 'aztec-world-state-'));
93
99
  const dbMapSizeKb = 10 * 1024 * 1024;
94
- log.debug(`Created temporary world state database at: ${dataDir} with size: ${dbMapSizeKb}`);
100
+ const worldStateTreeMapSizes: WorldStateTreeMapSizes = {
101
+ archiveTreeMapSizeKb: dbMapSizeKb,
102
+ nullifierTreeMapSizeKb: dbMapSizeKb,
103
+ noteHashTreeMapSizeKb: dbMapSizeKb,
104
+ messageTreeMapSizeKb: dbMapSizeKb,
105
+ publicDataTreeMapSizeKb: dbMapSizeKb,
106
+ };
107
+ log.debug(`Created temporary world state database at: ${dataDir} with tree map size: ${dbMapSizeKb}`);
95
108
 
96
109
  // pass a cleanup callback because process.on('beforeExit', cleanup) does not work under Jest
97
110
  const cleanup = async () => {
@@ -103,7 +116,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
103
116
  }
104
117
  };
105
118
 
106
- return this.new(rollupAddress, dataDir, dbMapSizeKb, prefilledPublicData, instrumentation, log, cleanup);
119
+ return this.new(rollupAddress, dataDir, worldStateTreeMapSizes, prefilledPublicData, instrumentation, log, cleanup);
107
120
  }
108
121
 
109
122
  protected async init() {
@@ -122,26 +135,51 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
122
135
 
123
136
  // the initial header _must_ be the first element in the archive tree
124
137
  // if this assertion fails, check that the hashing done in Header in yarn-project matches the initial header hash done in world_state.cpp
125
- const indices = await committed.findLeafIndices(MerkleTreeId.ARCHIVE, [await this.initialHeader.hash()]);
138
+ const indices = await committed.findLeafIndices(MerkleTreeId.ARCHIVE, [
139
+ (await this.initialHeader.hash()).toField(),
140
+ ]);
126
141
  const initialHeaderIndex = indices[0];
127
142
  assert.strictEqual(initialHeaderIndex, 0n, 'Invalid initial archive state');
128
143
  }
129
144
 
145
+ public async clear() {
146
+ await this.instance.close();
147
+ this.cachedStatusSummary = undefined;
148
+ await tryRmDir(this.instance.getDataDir(), this.log);
149
+ this.instance = this.instance.clone();
150
+ }
151
+
130
152
  public getCommitted(): MerkleTreeReadOperations {
131
- return new MerkleTreesFacade(this.instance, this.initialHeader!, worldStateRevision(false, 0, 0));
153
+ return new MerkleTreesFacade(this.instance, this.initialHeader!, WorldStateRevision.empty());
132
154
  }
133
155
 
134
- public getSnapshot(blockNumber: number): MerkleTreeReadOperations {
135
- return new MerkleTreesFacade(this.instance, this.initialHeader!, worldStateRevision(false, 0, blockNumber));
156
+ public getSnapshot(blockNumber: BlockNumber): MerkleTreeReadOperations {
157
+ return new MerkleTreesFacade(
158
+ this.instance,
159
+ this.initialHeader!,
160
+ new WorldStateRevision(/*forkId=*/ 0, /* blockNumber=*/ blockNumber, /* includeUncommitted=*/ false),
161
+ );
136
162
  }
137
163
 
138
- public async fork(blockNumber?: number): Promise<MerkleTreeWriteOperations> {
164
+ public async fork(
165
+ blockNumber?: BlockNumber,
166
+ opts: { closeDelayMs?: number } = {},
167
+ ): Promise<MerkleTreeWriteOperations> {
139
168
  const resp = await this.instance.call(WorldStateMessageType.CREATE_FORK, {
140
169
  latest: blockNumber === undefined,
141
- blockNumber: blockNumber ?? 0,
170
+ blockNumber: blockNumber ?? BlockNumber.ZERO,
142
171
  canonical: true,
143
172
  });
144
- return new MerkleTreesForkFacade(this.instance, this.initialHeader!, worldStateRevision(true, resp.forkId, 0));
173
+ return new MerkleTreesForkFacade(
174
+ this.instance,
175
+ this.initialHeader!,
176
+ new WorldStateRevision(
177
+ /*forkId=*/ resp.forkId,
178
+ /* blockNumber=*/ BlockNumber.ZERO,
179
+ /* includeUncommitted=*/ true,
180
+ ),
181
+ opts,
182
+ );
145
183
  }
146
184
 
147
185
  public getInitialHeader(): BlockHeader {
@@ -149,11 +187,22 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
149
187
  }
150
188
 
151
189
  public async handleL2BlockAndMessages(l2Block: L2Block, l1ToL2Messages: Fr[]): Promise<WorldStateStatusFull> {
152
- // We have to pad both the values within tx effects because that's how the trees are built by circuits.
190
+ const isFirstBlock = l2Block.indexWithinCheckpoint === 0;
191
+ if (!isFirstBlock && l1ToL2Messages.length > 0) {
192
+ throw new Error(
193
+ `L1 to L2 messages must be empty for non-first blocks, but got ${l1ToL2Messages.length} messages for block ${l2Block.number}.`,
194
+ );
195
+ }
196
+
197
+ // We have to pad the given l1 to l2 messages, and the note hashes and nullifiers within tx effects, because that's
198
+ // how the trees are built by circuits.
199
+ const paddedL1ToL2Messages = isFirstBlock
200
+ ? padArrayEnd<Fr, number>(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP)
201
+ : [];
202
+
153
203
  const paddedNoteHashes = l2Block.body.txEffects.flatMap(txEffect =>
154
204
  padArrayEnd(txEffect.noteHashes, Fr.ZERO, MAX_NOTE_HASHES_PER_TX),
155
205
  );
156
- const paddedL1ToL2Messages = padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
157
206
 
158
207
  const paddedNullifiers = l2Block.body.txEffects
159
208
  .flatMap(txEffect => padArrayEnd(txEffect.nullifiers, Fr.ZERO, MAX_NULLIFIERS_PER_TX))
@@ -173,7 +222,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
173
222
  WorldStateMessageType.SYNC_BLOCK,
174
223
  {
175
224
  blockNumber: l2Block.number,
176
- blockHeaderHash: await l2Block.header.hash(),
225
+ blockHeaderHash: await l2Block.hash(),
177
226
  paddedL1ToL2Messages: paddedL1ToL2Messages.map(serializeLeaf),
178
227
  paddedNoteHashes: paddedNoteHashes.map(serializeLeaf),
179
228
  paddedNullifiers: paddedNullifiers.map(serializeLeaf),
@@ -181,7 +230,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
181
230
  blockStateRef: blockStateReference(l2Block.header.state),
182
231
  canonical: true,
183
232
  },
184
- this.sanitiseAndCacheSummaryFromFull.bind(this),
233
+ this.sanitizeAndCacheSummaryFromFull.bind(this),
185
234
  this.deleteCachedSummary.bind(this),
186
235
  );
187
236
  } catch (err) {
@@ -200,16 +249,16 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
200
249
  return BlockHeader.empty({ state });
201
250
  }
202
251
 
203
- private sanitiseAndCacheSummaryFromFull(response: WorldStateStatusFull) {
204
- const sanitised = sanitiseFullStatus(response);
205
- this.cachedStatusSummary = { ...sanitised.summary };
206
- return sanitised;
252
+ private sanitizeAndCacheSummaryFromFull(response: WorldStateStatusFull) {
253
+ const sanitized = sanitizeFullStatus(response);
254
+ this.cachedStatusSummary = { ...sanitized.summary };
255
+ return sanitized;
207
256
  }
208
257
 
209
- private sanitiseAndCacheSummary(response: WorldStateStatusSummary) {
210
- const sanitised = sanitiseSummary(response);
211
- this.cachedStatusSummary = { ...sanitised };
212
- return sanitised;
258
+ private sanitizeAndCacheSummary(response: WorldStateStatusSummary) {
259
+ const sanitized = sanitizeSummary(response);
260
+ this.cachedStatusSummary = { ...sanitized };
261
+ return sanitized;
213
262
  }
214
263
 
215
264
  private deleteCachedSummary(_: string) {
@@ -217,19 +266,19 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
217
266
  }
218
267
 
219
268
  /**
220
- * Advances the finalised block number to be the number provided
221
- * @param toBlockNumber The block number that is now the tip of the finalised chain
269
+ * Advances the finalized block number to be the number provided
270
+ * @param toBlockNumber The block number that is now the tip of the finalized chain
222
271
  * @returns The new WorldStateStatus
223
272
  */
224
- public async setFinalised(toBlockNumber: bigint) {
273
+ public async setFinalized(toBlockNumber: BlockNumber) {
225
274
  try {
226
275
  await this.instance.call(
227
- WorldStateMessageType.FINALISE_BLOCKS,
276
+ WorldStateMessageType.FINALIZE_BLOCKS,
228
277
  {
229
278
  toBlockNumber,
230
279
  canonical: true,
231
280
  },
232
- this.sanitiseAndCacheSummary.bind(this),
281
+ this.sanitizeAndCacheSummary.bind(this),
233
282
  this.deleteCachedSummary.bind(this),
234
283
  );
235
284
  } catch (err) {
@@ -244,7 +293,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
244
293
  * @param toBlockNumber The block number of the new oldest historical block
245
294
  * @returns The new WorldStateStatus
246
295
  */
247
- public async removeHistoricalBlocks(toBlockNumber: bigint) {
296
+ public async removeHistoricalBlocks(toBlockNumber: BlockNumber) {
248
297
  try {
249
298
  return await this.instance.call(
250
299
  WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS,
@@ -252,7 +301,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
252
301
  toBlockNumber,
253
302
  canonical: true,
254
303
  },
255
- this.sanitiseAndCacheSummaryFromFull.bind(this),
304
+ this.sanitizeAndCacheSummaryFromFull.bind(this),
256
305
  this.deleteCachedSummary.bind(this),
257
306
  );
258
307
  } catch (err) {
@@ -266,7 +315,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
266
315
  * @param toBlockNumber The block number of the new tip of the pending chain,
267
316
  * @returns The new WorldStateStatus
268
317
  */
269
- public async unwindBlocks(toBlockNumber: bigint) {
318
+ public async unwindBlocks(toBlockNumber: BlockNumber) {
270
319
  try {
271
320
  return await this.instance.call(
272
321
  WorldStateMessageType.UNWIND_BLOCKS,
@@ -274,7 +323,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
274
323
  toBlockNumber,
275
324
  canonical: true,
276
325
  },
277
- this.sanitiseAndCacheSummaryFromFull.bind(this),
326
+ this.sanitizeAndCacheSummaryFromFull.bind(this),
278
327
  this.deleteCachedSummary.bind(this),
279
328
  );
280
329
  } catch (err) {
@@ -290,7 +339,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
290
339
  return await this.instance.call(
291
340
  WorldStateMessageType.GET_STATUS,
292
341
  { canonical: true },
293
- this.sanitiseAndCacheSummary.bind(this),
342
+ this.sanitizeAndCacheSummary.bind(this),
294
343
  );
295
344
  }
296
345
 
@@ -314,4 +363,25 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
314
363
  ),
315
364
  );
316
365
  }
366
+
367
+ public async backupTo(
368
+ dstPath: string,
369
+ compact: boolean = true,
370
+ ): Promise<Record<Exclude<SnapshotDataKeys, 'archiver'>, string>> {
371
+ await this.instance.call(WorldStateMessageType.COPY_STORES, {
372
+ dstPath,
373
+ compact,
374
+ canonical: true,
375
+ });
376
+ return fromEntries(NATIVE_WORLD_STATE_DBS.map(([name, dir]) => [name, join(dstPath, dir, 'data.mdb')] as const));
377
+ }
317
378
  }
379
+
380
+ // The following paths are defined in cpp-land
381
+ export const NATIVE_WORLD_STATE_DBS = [
382
+ ['l1-to-l2-message-tree', 'L1ToL2MessageTree'],
383
+ ['archive-tree', 'ArchiveTree'],
384
+ ['public-data-tree', 'PublicDataTree'],
385
+ ['note-hash-tree', 'NoteHashTree'],
386
+ ['nullifier-tree', 'NullifierTree'],
387
+ ] as const;
@@ -8,7 +8,7 @@ import {
8
8
  NULLIFIER_TREE_HEIGHT,
9
9
  PUBLIC_DATA_TREE_HEIGHT,
10
10
  } from '@aztec/constants';
11
- import { createLogger } from '@aztec/foundation/log';
11
+ import { type Logger, createLogger } from '@aztec/foundation/log';
12
12
  import { NativeWorldState as BaseNativeWorldState, MsgpackChannel } from '@aztec/native';
13
13
  import { MerkleTreeId } from '@aztec/stdlib/trees';
14
14
  import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees';
@@ -17,6 +17,7 @@ import assert from 'assert';
17
17
  import { cpus } from 'os';
18
18
 
19
19
  import type { WorldStateInstrumentation } from '../instrumentation/instrumentation.js';
20
+ import type { WorldStateTreeMapSizes } from '../synchronizer/factory.js';
20
21
  import {
21
22
  WorldStateMessageType,
22
23
  type WorldStateRequest,
@@ -35,6 +36,8 @@ export interface NativeWorldStateInstance {
35
36
  messageType: T,
36
37
  body: WorldStateRequest[T] & WorldStateRequestCategories,
37
38
  ): Promise<WorldStateResponse[T]>;
39
+ // TODO(dbanks12): this returns any type, but we should strongly type it
40
+ getHandle(): any;
38
41
  }
39
42
 
40
43
  /**
@@ -50,15 +53,17 @@ export class NativeWorldState implements NativeWorldStateInstance {
50
53
 
51
54
  /** Creates a new native WorldState instance */
52
55
  constructor(
53
- dataDir: string,
54
- dbMapSizeKb: number,
55
- prefilledPublicData: PublicDataTreeLeaf[] = [],
56
- private instrumentation: WorldStateInstrumentation,
57
- private log = createLogger('world-state:database'),
56
+ private readonly dataDir: string,
57
+ private readonly wsTreeMapSizes: WorldStateTreeMapSizes,
58
+ private readonly prefilledPublicData: PublicDataTreeLeaf[] = [],
59
+ private readonly instrumentation: WorldStateInstrumentation,
60
+ private readonly log: Logger = createLogger('world-state:database'),
58
61
  ) {
59
62
  const threads = Math.min(cpus().length, MAX_WORLD_STATE_THREADS);
60
63
  log.info(
61
- `Creating world state data store at directory ${dataDir} with map size ${dbMapSizeKb} KB and ${threads} threads.`,
64
+ `Creating world state data store at directory ${dataDir} with map sizes ${JSON.stringify(
65
+ wsTreeMapSizes,
66
+ )} and ${threads} threads.`,
62
67
  );
63
68
  const prefilledPublicDataBufferArray = prefilledPublicData.map(d => [d.slot.toBuffer(), d.value.toBuffer()]);
64
69
  const ws = new BaseNativeWorldState(
@@ -76,7 +81,13 @@ export class NativeWorldState implements NativeWorldStateInstance {
76
81
  },
77
82
  prefilledPublicDataBufferArray,
78
83
  GeneratorIndex.BLOCK_HASH,
79
- dbMapSizeKb,
84
+ {
85
+ [MerkleTreeId.NULLIFIER_TREE]: wsTreeMapSizes.nullifierTreeMapSizeKb,
86
+ [MerkleTreeId.NOTE_HASH_TREE]: wsTreeMapSizes.noteHashTreeMapSizeKb,
87
+ [MerkleTreeId.PUBLIC_DATA_TREE]: wsTreeMapSizes.publicDataTreeMapSizeKb,
88
+ [MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: wsTreeMapSizes.messageTreeMapSizeKb,
89
+ [MerkleTreeId.ARCHIVE]: wsTreeMapSizes.archiveTreeMapSizeKb,
90
+ },
80
91
  threads,
81
92
  );
82
93
  this.instance = new MsgpackChannel(ws);
@@ -84,6 +95,46 @@ export class NativeWorldState implements NativeWorldStateInstance {
84
95
  this.queues.set(0, new WorldStateOpsQueue());
85
96
  }
86
97
 
98
+ public getDataDir() {
99
+ return this.dataDir;
100
+ }
101
+
102
+ public clone() {
103
+ return new NativeWorldState(
104
+ this.dataDir,
105
+ this.wsTreeMapSizes,
106
+ this.prefilledPublicData,
107
+ this.instrumentation,
108
+ this.log,
109
+ );
110
+ }
111
+
112
+ /**
113
+ * Gets the native WorldState handle from the underlying native instance.
114
+ * We call the getHandle() method on the native WorldState to get a NAPI External
115
+ * that wraps the underlying C++ WorldState pointer.
116
+ * @returns The NAPI External handle to the native WorldState instance,since
117
+ * the NAPI external type is opaque, we return any (we could also use an opaque symbol type)
118
+ */
119
+ public getHandle(): any {
120
+ const worldStateWrapper = (this.instance as any).dest;
121
+
122
+ if (!worldStateWrapper) {
123
+ throw new Error('No WorldStateWrapper found');
124
+ }
125
+
126
+ if (typeof worldStateWrapper.getHandle !== 'function') {
127
+ throw new Error('WorldStateWrapper does not have getHandle method');
128
+ }
129
+
130
+ // Call getHandle() to get the NAPI External
131
+ try {
132
+ return worldStateWrapper.getHandle();
133
+ } catch (error) {
134
+ this.log.error('Failed to get native WorldState handle', error);
135
+ }
136
+ }
137
+
87
138
  /**
88
139
  * Sends a message to the native instance and returns the response.
89
140
  * @param messageType - The type of message to send
@@ -35,7 +35,7 @@ export const MUTATING_MSG_TYPES = new Set([
35
35
  WorldStateMessageType.SYNC_BLOCK,
36
36
  WorldStateMessageType.CREATE_FORK,
37
37
  WorldStateMessageType.DELETE_FORK,
38
- WorldStateMessageType.FINALISE_BLOCKS,
38
+ WorldStateMessageType.FINALIZE_BLOCKS,
39
39
  WorldStateMessageType.UNWIND_BLOCKS,
40
40
  WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS,
41
41
  WorldStateMessageType.CREATE_CHECKPOINT,
@@ -1,24 +1,31 @@
1
- import {
2
- type ConfigMappingsType,
3
- booleanConfigHelper,
4
- getConfigFromMappings,
5
- numberConfigHelper,
6
- } from '@aztec/foundation/config';
1
+ import { type ConfigMappingsType, getConfigFromMappings, numberConfigHelper } from '@aztec/foundation/config';
7
2
 
8
3
  /** World State synchronizer configuration values. */
9
4
  export interface WorldStateConfig {
10
5
  /** The frequency in which to check. */
11
6
  worldStateBlockCheckIntervalMS: number;
12
7
 
13
- /** Whether to follow only the proven chain. */
14
- worldStateProvenBlocksOnly: boolean;
15
-
16
8
  /** Size of the batch for each get-blocks request from the synchronizer to the archiver. */
17
9
  worldStateBlockRequestBatchSize?: number;
18
10
 
19
- /** The map size to be provided to LMDB for each world state tree DB, optional, will inherit from the general dataStoreMapSizeKB if not specified*/
11
+ /** The map size to be provided to LMDB for each world state tree DB, optional, will inherit from the general dataStoreMapSizeKb if not specified*/
20
12
  worldStateDbMapSizeKb?: number;
21
13
 
14
+ /** The map size to be provided to LMDB for each world state archive tree, optional, will inherit from the general worldStateDbMapSizeKb if not specified*/
15
+ archiveTreeMapSizeKb?: number;
16
+
17
+ /** The map size to be provided to LMDB for each world state nullifier tree, optional, will inherit from the general worldStateDbMapSizeKb if not specified*/
18
+ nullifierTreeMapSizeKb?: number;
19
+
20
+ /** The map size to be provided to LMDB for each world state note hash tree, optional, will inherit from the general worldStateDbMapSizeKb if not specified*/
21
+ noteHashTreeMapSizeKb?: number;
22
+
23
+ /** The map size to be provided to LMDB for each world state message tree, optional, will inherit from the general worldStateDbMapSizeKb if not specified*/
24
+ messageTreeMapSizeKb?: number;
25
+
26
+ /** The map size to be provided to LMDB for each world state public data tree, optional, will inherit from the general worldStateDbMapSizeKb if not specified*/
27
+ publicDataTreeMapSizeKb?: number;
28
+
22
29
  /** Optional directory for the world state DB, if unspecified will default to the general data directory */
23
30
  worldStateDataDirectory?: string;
24
31
 
@@ -33,11 +40,6 @@ export const worldStateConfigMappings: ConfigMappingsType<WorldStateConfig> = {
33
40
  defaultValue: 100,
34
41
  description: 'The frequency in which to check.',
35
42
  },
36
- worldStateProvenBlocksOnly: {
37
- env: 'WS_PROVEN_BLOCKS_ONLY',
38
- description: 'Whether to follow only the proven chain.',
39
- ...booleanConfigHelper(),
40
- },
41
43
  worldStateBlockRequestBatchSize: {
42
44
  env: 'WS_BLOCK_REQUEST_BATCH_SIZE',
43
45
  parseEnv: (val: string | undefined) => (val ? +val : undefined),
@@ -46,7 +48,37 @@ export const worldStateConfigMappings: ConfigMappingsType<WorldStateConfig> = {
46
48
  worldStateDbMapSizeKb: {
47
49
  env: 'WS_DB_MAP_SIZE_KB',
48
50
  parseEnv: (val: string | undefined) => (val ? +val : undefined),
49
- description: 'The maximum possible size of the world state DB',
51
+ description: 'The maximum possible size of the world state DB in KB. Overwrites the general dataStoreMapSizeKb.',
52
+ },
53
+ archiveTreeMapSizeKb: {
54
+ env: 'ARCHIVE_TREE_MAP_SIZE_KB',
55
+ parseEnv: (val: string | undefined) => (val ? +val : undefined),
56
+ description:
57
+ 'The maximum possible size of the world state archive tree in KB. Overwrites the general worldStateDbMapSizeKb.',
58
+ },
59
+ nullifierTreeMapSizeKb: {
60
+ env: 'NULLIFIER_TREE_MAP_SIZE_KB',
61
+ parseEnv: (val: string | undefined) => (val ? +val : undefined),
62
+ description:
63
+ 'The maximum possible size of the world state nullifier tree in KB. Overwrites the general worldStateDbMapSizeKb.',
64
+ },
65
+ noteHashTreeMapSizeKb: {
66
+ env: 'NOTE_HASH_TREE_MAP_SIZE_KB',
67
+ parseEnv: (val: string | undefined) => (val ? +val : undefined),
68
+ description:
69
+ 'The maximum possible size of the world state note hash tree in KB. Overwrites the general worldStateDbMapSizeKb.',
70
+ },
71
+ messageTreeMapSizeKb: {
72
+ env: 'MESSAGE_TREE_MAP_SIZE_KB',
73
+ parseEnv: (val: string | undefined) => (val ? +val : undefined),
74
+ description:
75
+ 'The maximum possible size of the world state message tree in KB. Overwrites the general worldStateDbMapSizeKb.',
76
+ },
77
+ publicDataTreeMapSizeKb: {
78
+ env: 'PUBLIC_DATA_TREE_MAP_SIZE_KB',
79
+ parseEnv: (val: string | undefined) => (val ? +val : undefined),
80
+ description:
81
+ 'The maximum possible size of the world state public data tree in KB. Overwrites the general worldStateDbMapSizeKb.',
50
82
  },
51
83
  worldStateDataDirectory: {
52
84
  env: 'WS_DATA_DIRECTORY',
@@ -0,0 +1,5 @@
1
+ export class WorldStateSynchronizerError extends Error {
2
+ constructor(message: string, options?: ErrorOptions) {
3
+ super(message, options);
4
+ }
5
+ }
@@ -9,6 +9,14 @@ import { NativeWorldStateService } from '../native/native_world_state.js';
9
9
  import type { WorldStateConfig } from './config.js';
10
10
  import { ServerWorldStateSynchronizer } from './server_world_state_synchronizer.js';
11
11
 
12
+ export interface WorldStateTreeMapSizes {
13
+ archiveTreeMapSizeKb: number;
14
+ nullifierTreeMapSizeKb: number;
15
+ noteHashTreeMapSizeKb: number;
16
+ messageTreeMapSizeKb: number;
17
+ publicDataTreeMapSizeKb: number;
18
+ }
19
+
12
20
  export async function createWorldStateSynchronizer(
13
21
  config: WorldStateConfig & DataStoreConfig,
14
22
  l2BlockSource: L2BlockSource & L1ToL2MessageSource,
@@ -21,25 +29,40 @@ export async function createWorldStateSynchronizer(
21
29
  }
22
30
 
23
31
  export async function createWorldState(
24
- config: WorldStateConfig & DataStoreConfig,
32
+ config: Pick<
33
+ WorldStateConfig,
34
+ | 'worldStateDataDirectory'
35
+ | 'worldStateDbMapSizeKb'
36
+ | 'archiveTreeMapSizeKb'
37
+ | 'nullifierTreeMapSizeKb'
38
+ | 'noteHashTreeMapSizeKb'
39
+ | 'messageTreeMapSizeKb'
40
+ | 'publicDataTreeMapSizeKb'
41
+ > &
42
+ Pick<DataStoreConfig, 'dataDirectory' | 'dataStoreMapSizeKb' | 'l1Contracts'>,
25
43
  prefilledPublicData: PublicDataTreeLeaf[] = [],
26
44
  instrumentation: WorldStateInstrumentation = new WorldStateInstrumentation(getTelemetryClient()),
27
45
  ) {
28
- const newConfig = {
29
- dataDirectory: config.worldStateDataDirectory ?? config.dataDirectory,
30
- dataStoreMapSizeKB: config.worldStateDbMapSizeKb ?? config.dataStoreMapSizeKB,
31
- } as DataStoreConfig;
46
+ const dataDirectory = config.worldStateDataDirectory ?? config.dataDirectory;
47
+ const dataStoreMapSizeKb = config.worldStateDbMapSizeKb ?? config.dataStoreMapSizeKb;
48
+ const wsTreeMapSizes: WorldStateTreeMapSizes = {
49
+ archiveTreeMapSizeKb: config.archiveTreeMapSizeKb ?? dataStoreMapSizeKb,
50
+ nullifierTreeMapSizeKb: config.nullifierTreeMapSizeKb ?? dataStoreMapSizeKb,
51
+ noteHashTreeMapSizeKb: config.noteHashTreeMapSizeKb ?? dataStoreMapSizeKb,
52
+ messageTreeMapSizeKb: config.messageTreeMapSizeKb ?? dataStoreMapSizeKb,
53
+ publicDataTreeMapSizeKb: config.publicDataTreeMapSizeKb ?? dataStoreMapSizeKb,
54
+ };
32
55
 
33
56
  if (!config.l1Contracts?.rollupAddress) {
34
57
  throw new Error('Rollup address is required to create a world state synchronizer.');
35
58
  }
36
59
 
37
60
  // If a data directory is provided in config, then create a persistent store.
38
- const merkleTrees = newConfig.dataDirectory
61
+ const merkleTrees = dataDirectory
39
62
  ? await NativeWorldStateService.new(
40
63
  config.l1Contracts.rollupAddress,
41
- newConfig.dataDirectory,
42
- newConfig.dataStoreMapSizeKB,
64
+ dataDirectory,
65
+ wsTreeMapSizes,
43
66
  prefilledPublicData,
44
67
  instrumentation,
45
68
  )