@aztec/world-state 0.0.1-commit.e558bd1c → 0.0.1-commit.e57c76e

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 (41) hide show
  1. package/dest/native/fork_checkpoint.d.ts +7 -1
  2. package/dest/native/fork_checkpoint.d.ts.map +1 -1
  3. package/dest/native/fork_checkpoint.js +15 -3
  4. package/dest/native/merkle_trees_facade.d.ts +9 -6
  5. package/dest/native/merkle_trees_facade.d.ts.map +1 -1
  6. package/dest/native/merkle_trees_facade.js +33 -13
  7. package/dest/native/message.d.ts +13 -6
  8. package/dest/native/message.d.ts.map +1 -1
  9. package/dest/native/native_world_state.d.ts +31 -5
  10. package/dest/native/native_world_state.d.ts.map +1 -1
  11. package/dest/native/native_world_state.js +80 -27
  12. package/dest/native/native_world_state_instance.d.ts +5 -4
  13. package/dest/native/native_world_state_instance.d.ts.map +1 -1
  14. package/dest/native/native_world_state_instance.js +33 -26
  15. package/dest/native/world_state_ops_queue.js +5 -5
  16. package/dest/synchronizer/config.d.ts +3 -3
  17. package/dest/synchronizer/config.d.ts.map +1 -1
  18. package/dest/synchronizer/config.js +15 -13
  19. package/dest/synchronizer/factory.d.ts +5 -5
  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 +4 -4
  23. package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
  24. package/dest/synchronizer/server_world_state_synchronizer.js +90 -18
  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/dest/world-state-db/merkle_tree_db.d.ts +1 -10
  29. package/dest/world-state-db/merkle_tree_db.d.ts.map +1 -1
  30. package/package.json +12 -11
  31. package/src/native/fork_checkpoint.ts +19 -3
  32. package/src/native/merkle_trees_facade.ts +41 -16
  33. package/src/native/message.ts +14 -5
  34. package/src/native/native_world_state.ts +98 -34
  35. package/src/native/native_world_state_instance.ts +40 -30
  36. package/src/native/world_state_ops_queue.ts +5 -5
  37. package/src/synchronizer/config.ts +21 -15
  38. package/src/synchronizer/factory.ts +13 -11
  39. package/src/synchronizer/server_world_state_synchronizer.ts +95 -20
  40. package/src/testing.ts +8 -9
  41. package/src/world-state-db/merkle_tree_db.ts +0 -10
@@ -14,8 +14,8 @@ import type {
14
14
  } from '@aztec/stdlib/interfaces/server';
15
15
  import type { SnapshotDataKeys } from '@aztec/stdlib/snapshots';
16
16
  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';
17
+ import { BlockHeader, GlobalVariables, PartialStateReference, StateReference } from '@aztec/stdlib/tx';
18
+ import { EMPTY_GENESIS_DATA, type GenesisData, WorldStateRevision } from '@aztec/stdlib/world-state';
19
19
  import { getTelemetryClient } from '@aztec/telemetry-client';
20
20
 
21
21
  import assert from 'assert/strict';
@@ -44,6 +44,29 @@ export const WORLD_STATE_DB_VERSION = 2; // The initial version
44
44
 
45
45
  export const WORLD_STATE_DIR = 'world_state';
46
46
 
47
+ const DEFAULT_TMP_TREE_MAP_SIZE_KB = 10 * 1024 * 1024;
48
+
49
+ /**
50
+ * Sets up a fresh `mkdtemp` directory + default `WorldStateTreeMapSizes` shared by both
51
+ * the `.tmp` (fsync-on) and `.ephemeral` (fsync-off) factories. Returns the raw tmpdir,
52
+ * the tree map sizes, and the package logger.
53
+ */
54
+ async function createTmpWorldStateDir(
55
+ bindings?: LoggerBindings,
56
+ ): Promise<{ dataDir: string; wsTreeMapSizes: WorldStateTreeMapSizes; log: Logger }> {
57
+ const log = createLogger('world-state:database', bindings);
58
+ const dataDir = await mkdtemp(join(tmpdir(), 'aztec-world-state-'));
59
+ const wsTreeMapSizes: WorldStateTreeMapSizes = {
60
+ archiveTreeMapSizeKb: DEFAULT_TMP_TREE_MAP_SIZE_KB,
61
+ nullifierTreeMapSizeKb: DEFAULT_TMP_TREE_MAP_SIZE_KB,
62
+ noteHashTreeMapSizeKb: DEFAULT_TMP_TREE_MAP_SIZE_KB,
63
+ messageTreeMapSizeKb: DEFAULT_TMP_TREE_MAP_SIZE_KB,
64
+ publicDataTreeMapSizeKb: DEFAULT_TMP_TREE_MAP_SIZE_KB,
65
+ };
66
+ log.debug(`Created temporary world state database at: ${dataDir} (map size ${DEFAULT_TMP_TREE_MAP_SIZE_KB} KB)`);
67
+ return { dataDir, wsTreeMapSizes, log };
68
+ }
69
+
47
70
  export class NativeWorldStateService implements MerkleTreeDatabase {
48
71
  protected initialHeader: BlockHeader | undefined;
49
72
  // This is read heavily and only changes when data is persisted, so we cache it
@@ -53,34 +76,46 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
53
76
  protected instance: NativeWorldState,
54
77
  protected readonly worldStateInstrumentation: WorldStateInstrumentation,
55
78
  protected readonly log: Logger,
79
+ private readonly genesis: GenesisData = EMPTY_GENESIS_DATA,
56
80
  private readonly cleanup = () => Promise.resolve(),
57
81
  ) {}
58
82
 
83
+ /**
84
+ * Opens a persistent world state at `dataDir`. Goes through `DatabaseVersionManager` so the
85
+ * caller's rollup address is bound to the on-disk schema and incompatible versions surface
86
+ * loudly. The LMDB envs commit with full fsync.
87
+ */
59
88
  static async new(
60
89
  rollupAddress: EthAddress,
61
90
  dataDir: string,
62
91
  wsTreeMapSizes: WorldStateTreeMapSizes,
63
- prefilledPublicData: PublicDataTreeLeaf[] = [],
92
+ genesis: GenesisData = EMPTY_GENESIS_DATA,
64
93
  instrumentation = new WorldStateInstrumentation(getTelemetryClient()),
65
94
  bindings?: LoggerBindings,
66
95
  cleanup = () => Promise.resolve(),
67
96
  ): Promise<NativeWorldStateService> {
68
97
  const log = createLogger('world-state:database', bindings);
69
98
  const worldStateDirectory = join(dataDir, WORLD_STATE_DIR);
70
- // Create a version manager to handle versioning
71
99
  const versionManager = new DatabaseVersionManager({
72
100
  schemaVersion: WORLD_STATE_DB_VERSION,
73
101
  rollupAddress,
74
102
  dataDirectory: worldStateDirectory,
75
- onOpen: (dir: string) => {
76
- return Promise.resolve(
77
- new NativeWorldState(dir, wsTreeMapSizes, prefilledPublicData, instrumentation, bindings),
78
- );
79
- },
103
+ onOpen: (dir: string) =>
104
+ Promise.resolve(
105
+ new NativeWorldState(
106
+ dir,
107
+ wsTreeMapSizes,
108
+ genesis,
109
+ instrumentation,
110
+ bindings,
111
+ undefined,
112
+ /*ephemeral=*/ false,
113
+ ),
114
+ ),
80
115
  });
81
116
 
82
117
  const [instance] = await versionManager.open();
83
- const worldState = new this(instance, instrumentation, log, cleanup);
118
+ const worldState = new this(instance, instrumentation, log, genesis, cleanup);
84
119
  try {
85
120
  await worldState.init();
86
121
  } catch (e) {
@@ -91,26 +126,23 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
91
126
  return worldState;
92
127
  }
93
128
 
129
+ /**
130
+ * Opens a world state in a fresh tmpdir with full fsync semantics. Use when you need the
131
+ * on-disk file to remain crash-recoverable (e.g. for snapshot/backup tests) but don't
132
+ * want a persistent dataDir. Pass `cleanupTmpDir=false` to keep the directory after
133
+ * close for inspection.
134
+ *
135
+ * If you don't care about crash-recoverability — i.e. you just want a fast scratch
136
+ * database for tests — use {@link ephemeral} instead.
137
+ */
94
138
  static async tmp(
95
139
  rollupAddress = EthAddress.ZERO,
96
140
  cleanupTmpDir = true,
97
- prefilledPublicData: PublicDataTreeLeaf[] = [],
141
+ genesis: GenesisData = EMPTY_GENESIS_DATA,
98
142
  instrumentation = new WorldStateInstrumentation(getTelemetryClient()),
99
143
  bindings?: LoggerBindings,
100
144
  ): Promise<NativeWorldStateService> {
101
- const log = createLogger('world-state:database', bindings);
102
- const dataDir = await mkdtemp(join(tmpdir(), 'aztec-world-state-'));
103
- const dbMapSizeKb = 10 * 1024 * 1024;
104
- const worldStateTreeMapSizes: WorldStateTreeMapSizes = {
105
- archiveTreeMapSizeKb: dbMapSizeKb,
106
- nullifierTreeMapSizeKb: dbMapSizeKb,
107
- noteHashTreeMapSizeKb: dbMapSizeKb,
108
- messageTreeMapSizeKb: dbMapSizeKb,
109
- publicDataTreeMapSizeKb: dbMapSizeKb,
110
- };
111
- log.debug(`Created temporary world state database at: ${dataDir} with tree map size: ${dbMapSizeKb}`);
112
-
113
- // pass a cleanup callback because process.on('beforeExit', cleanup) does not work under Jest
145
+ const { dataDir, wsTreeMapSizes, log } = await createTmpWorldStateDir(bindings);
114
146
  const cleanup = async () => {
115
147
  if (cleanupTmpDir) {
116
148
  await rm(dataDir, { recursive: true, force: true, maxRetries: 3 });
@@ -119,16 +151,45 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
119
151
  log.debug(`Leaving temporary world state database: ${dataDir}`);
120
152
  }
121
153
  };
154
+ return this.new(rollupAddress, dataDir, wsTreeMapSizes, genesis, instrumentation, bindings, cleanup);
155
+ }
122
156
 
123
- return this.new(
124
- rollupAddress,
125
- dataDir,
126
- worldStateTreeMapSizes,
127
- prefilledPublicData,
157
+ /**
158
+ * Opens a fully-ephemeral world state. The directory is created in `os.tmpdir()`, the LMDB
159
+ * envs open with `MDB_NOSYNC | MDB_NOMETASYNC` so commits never block on fsync, and the
160
+ * directory is removed on dispose. A crash mid-write leaves the env unrecoverable.
161
+ *
162
+ * For unit tests and other isolated runs. Use {@link tmp} when you need fsync semantics in a
163
+ * tmp dir, and {@link new} for a persistent store. Skips {@link DatabaseVersionManager} —
164
+ * there is no on-disk schema to bind to and no rollup address is taken.
165
+ */
166
+ static async ephemeral(
167
+ genesis: GenesisData = EMPTY_GENESIS_DATA,
168
+ instrumentation = new WorldStateInstrumentation(getTelemetryClient()),
169
+ bindings?: LoggerBindings,
170
+ ): Promise<NativeWorldStateService> {
171
+ const { dataDir, wsTreeMapSizes, log } = await createTmpWorldStateDir(bindings);
172
+ const cleanup = async () => {
173
+ await rm(dataDir, { recursive: true, force: true, maxRetries: 3 });
174
+ log.debug(`Deleted ephemeral world state database: ${dataDir}`);
175
+ };
176
+ const instance = new NativeWorldState(
177
+ join(dataDir, WORLD_STATE_DIR),
178
+ wsTreeMapSizes,
179
+ genesis,
128
180
  instrumentation,
129
181
  bindings,
130
- cleanup,
182
+ undefined,
183
+ /*ephemeral=*/ true,
131
184
  );
185
+ const worldState = new this(instance, instrumentation, log, genesis, cleanup);
186
+ try {
187
+ await worldState.init();
188
+ } catch (e) {
189
+ log.error(`Error initializing ephemeral world state: ${e}`);
190
+ throw e;
191
+ }
192
+ return worldState;
132
193
  }
133
194
 
134
195
  protected async init() {
@@ -147,7 +208,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
147
208
 
148
209
  // the initial header _must_ be the first element in the archive tree
149
210
  // 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()]);
211
+ const indices = await committed.findLeafIndices(MerkleTreeId.ARCHIVE, [await this.initialHeader.hash()]);
151
212
  const initialHeaderIndex = indices[0];
152
213
  assert.strictEqual(initialHeaderIndex, 0n, 'Invalid initial archive state');
153
214
  }
@@ -185,7 +246,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
185
246
  this.initialHeader!,
186
247
  new WorldStateRevision(
187
248
  /*forkId=*/ resp.forkId,
188
- /* blockNumber=*/ BlockNumber.ZERO,
249
+ /* blockNumber=*/ WorldStateRevision.LATEST,
189
250
  /* includeUncommitted=*/ true,
190
251
  ),
191
252
  opts,
@@ -232,7 +293,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
232
293
  WorldStateMessageType.SYNC_BLOCK,
233
294
  {
234
295
  blockNumber: l2Block.number,
235
- blockHeaderHash: await l2Block.hash(),
296
+ blockHeaderHash: (await l2Block.hash()).toBuffer(),
236
297
  paddedL1ToL2Messages: paddedL1ToL2Messages.map(serializeLeaf),
237
298
  paddedNoteHashes: paddedNoteHashes.map(serializeLeaf),
238
299
  paddedNullifiers: paddedNullifiers.map(serializeLeaf),
@@ -256,7 +317,10 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
256
317
 
257
318
  private async buildInitialHeader(): Promise<BlockHeader> {
258
319
  const state = await this.getInitialStateReference();
259
- return BlockHeader.empty({ state });
320
+ return BlockHeader.empty({
321
+ state,
322
+ globalVariables: GlobalVariables.empty({ timestamp: this.genesis.genesisTimestamp }),
323
+ });
260
324
  }
261
325
 
262
326
  private sanitizeAndCacheSummaryFromFull(response: WorldStateStatusFull) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ARCHIVE_HEIGHT,
3
- GeneratorIndex,
3
+ DomainSeparator,
4
4
  L1_TO_L2_MSG_TREE_HEIGHT,
5
5
  MAX_NULLIFIERS_PER_TX,
6
6
  MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
@@ -11,7 +11,7 @@ import {
11
11
  import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
12
12
  import { NativeWorldState as BaseNativeWorldState, MsgpackChannel } from '@aztec/native';
13
13
  import { MerkleTreeId } from '@aztec/stdlib/trees';
14
- import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees';
14
+ import { EMPTY_GENESIS_DATA, type GenesisData } from '@aztec/stdlib/world-state';
15
15
 
16
16
  import assert from 'assert';
17
17
  import { cpus } from 'os';
@@ -55,18 +55,22 @@ export class NativeWorldState implements NativeWorldStateInstance {
55
55
  constructor(
56
56
  private readonly dataDir: string,
57
57
  private readonly wsTreeMapSizes: WorldStateTreeMapSizes,
58
- private readonly prefilledPublicData: PublicDataTreeLeaf[] = [],
58
+ private readonly genesis: GenesisData = EMPTY_GENESIS_DATA,
59
59
  private readonly instrumentation: WorldStateInstrumentation,
60
60
  bindings?: LoggerBindings,
61
61
  private readonly log: Logger = createLogger('world-state:database', bindings),
62
+ private readonly ephemeral: boolean = false,
62
63
  ) {
63
64
  const threads = Math.min(cpus().length, MAX_WORLD_STATE_THREADS);
64
65
  log.info(
65
66
  `Creating world state data store at directory ${dataDir} with map sizes ${JSON.stringify(
66
67
  wsTreeMapSizes,
67
- )} and ${threads} threads.`,
68
+ )} and ${threads} threads (ephemeral=${ephemeral}).`,
68
69
  );
69
- const prefilledPublicDataBufferArray = prefilledPublicData.map(d => [d.slot.toBuffer(), d.value.toBuffer()]);
70
+ const prefilledPublicDataBufferArray = genesis.prefilledPublicData.map(d => [
71
+ d.slot.toBuffer(),
72
+ d.value.toBuffer(),
73
+ ]);
70
74
  const ws = new BaseNativeWorldState(
71
75
  dataDir,
72
76
  {
@@ -81,7 +85,8 @@ export class NativeWorldState implements NativeWorldStateInstance {
81
85
  [MerkleTreeId.PUBLIC_DATA_TREE]: 2 * MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
82
86
  },
83
87
  prefilledPublicDataBufferArray,
84
- GeneratorIndex.BLOCK_HEADER_HASH,
88
+ DomainSeparator.BLOCK_HEADER_HASH,
89
+ Number(genesis.genesisTimestamp),
85
90
  {
86
91
  [MerkleTreeId.NULLIFIER_TREE]: wsTreeMapSizes.nullifierTreeMapSizeKb,
87
92
  [MerkleTreeId.NOTE_HASH_TREE]: wsTreeMapSizes.noteHashTreeMapSizeKb,
@@ -90,6 +95,7 @@ export class NativeWorldState implements NativeWorldStateInstance {
90
95
  [MerkleTreeId.ARCHIVE]: wsTreeMapSizes.archiveTreeMapSizeKb,
91
96
  },
92
97
  threads,
98
+ ephemeral,
93
99
  );
94
100
  this.instance = new MsgpackChannel(ws);
95
101
  // Manually create the queue for the canonical fork
@@ -104,10 +110,11 @@ export class NativeWorldState implements NativeWorldStateInstance {
104
110
  return new NativeWorldState(
105
111
  this.dataDir,
106
112
  this.wsTreeMapSizes,
107
- this.prefilledPublicData,
113
+ this.genesis,
108
114
  this.instrumentation,
109
115
  this.log.getBindings(),
110
116
  this.log,
117
+ this.ephemeral,
111
118
  );
112
119
  }
113
120
 
@@ -180,30 +187,33 @@ export class NativeWorldState implements NativeWorldStateInstance {
180
187
  this.queues.set(forkId, requestQueue);
181
188
  }
182
189
 
183
- // Enqueue the request and wait for the response
184
- const response = await requestQueue.execute(
185
- async () => {
186
- assert.notEqual(messageType, WorldStateMessageType.CLOSE, 'Use close() to close the native instance');
187
- assert.equal(this.open, true, 'Native instance is closed');
188
- let response: WorldStateResponse[T];
189
- try {
190
- response = await this._sendMessage(messageType, body);
191
- } catch (error: any) {
192
- errorHandler(error.message);
193
- throw error;
194
- }
195
- return responseHandler(response);
196
- },
197
- messageType,
198
- committedOnly,
199
- );
200
-
201
- // If the request was to delete the fork then we clean it up here
202
- if (messageType === WorldStateMessageType.DELETE_FORK) {
203
- await requestQueue.stop();
204
- this.queues.delete(forkId);
190
+ // Enqueue the request and wait for the response. The per-fork queue is cleaned up in `finally` even on
191
+ // error, so the JS-side queues map cannot outlive the native fork (e.g. when the native fork was already
192
+ // destroyed by an unwind/historical-prune and DELETE_FORK rejects with "Fork not found").
193
+ try {
194
+ const response = await requestQueue.execute(
195
+ async () => {
196
+ assert.notEqual(messageType, WorldStateMessageType.CLOSE, 'Use close() to close the native instance');
197
+ assert.equal(this.open, true, 'Native instance is closed');
198
+ let response: WorldStateResponse[T];
199
+ try {
200
+ response = await this._sendMessage(messageType, body);
201
+ } catch (error: any) {
202
+ errorHandler(error.message);
203
+ throw error;
204
+ }
205
+ return responseHandler(response);
206
+ },
207
+ messageType,
208
+ committedOnly,
209
+ );
210
+ return response;
211
+ } finally {
212
+ if (messageType === WorldStateMessageType.DELETE_FORK) {
213
+ await requestQueue.stop();
214
+ this.queues.delete(forkId);
215
+ }
205
216
  }
206
- return response;
207
217
  }
208
218
 
209
219
  /**
@@ -96,7 +96,7 @@ export class WorldStateOpsQueue {
96
96
  // then send the request immediately
97
97
  // If a mutating request is in flight then we must wait
98
98
  // If a mutating request is not in flight but something is queued then it must be a mutating request
99
- if (this.inFlightMutatingCount == 0 && this.requests.length == 0) {
99
+ if (this.inFlightMutatingCount === 0 && this.requests.length === 0) {
100
100
  this.sendEnqueuedRequest(op);
101
101
  } else {
102
102
  this.requests.push(op);
@@ -122,7 +122,7 @@ export class WorldStateOpsQueue {
122
122
  --this.inFlightCount;
123
123
 
124
124
  // If there are still requests in flight then do nothing further
125
- if (this.inFlightCount != 0) {
125
+ if (this.inFlightCount !== 0) {
126
126
  return;
127
127
  }
128
128
 
@@ -134,7 +134,7 @@ export class WorldStateOpsQueue {
134
134
  while (this.requests.length > 0) {
135
135
  const next = this.requests[0];
136
136
  if (next.mutating) {
137
- if (this.inFlightCount == 0) {
137
+ if (this.inFlightCount === 0) {
138
138
  // send the mutating request
139
139
  this.requests.shift();
140
140
  this.sendEnqueuedRequest(next);
@@ -149,7 +149,7 @@ export class WorldStateOpsQueue {
149
149
  }
150
150
 
151
151
  // If the queue is empty, there is nothing in flight and we have been told to stop, then resolve the stop promise
152
- if (this.inFlightCount == 0 && this.stopResolve !== undefined) {
152
+ if (this.inFlightCount === 0 && this.stopResolve !== undefined) {
153
153
  this.stopResolve();
154
154
  }
155
155
  }
@@ -182,7 +182,7 @@ export class WorldStateOpsQueue {
182
182
  });
183
183
 
184
184
  // If no outstanding requests then immediately resolve the promise
185
- if (this.requests.length == 0 && this.inFlightCount == 0 && this.stopResolve !== undefined) {
185
+ if (this.requests.length === 0 && this.inFlightCount === 0 && this.stopResolve !== undefined) {
186
186
  this.stopResolve();
187
187
  }
188
188
  return this.stopPromise;
@@ -1,4 +1,9 @@
1
- import { type ConfigMappingsType, getConfigFromMappings, numberConfigHelper } from '@aztec/foundation/config';
1
+ import {
2
+ type ConfigMappingsType,
3
+ getConfigFromMappings,
4
+ numberConfigHelper,
5
+ optionalNumberConfigHelper,
6
+ } from '@aztec/foundation/config';
2
7
 
3
8
  /** World State synchronizer configuration values. */
4
9
  export interface WorldStateConfig {
@@ -29,54 +34,53 @@ export interface WorldStateConfig {
29
34
  /** Optional directory for the world state DB, if unspecified will default to the general data directory */
30
35
  worldStateDataDirectory?: string;
31
36
 
32
- /** The number of historic blocks to maintain */
33
- worldStateBlockHistory: number;
37
+ /** The number of historic checkpoints worth of blocks to maintain */
38
+ worldStateCheckpointHistory: number;
34
39
  }
35
40
 
36
41
  export const worldStateConfigMappings: ConfigMappingsType<WorldStateConfig> = {
37
42
  worldStateBlockCheckIntervalMS: {
38
43
  env: 'WS_BLOCK_CHECK_INTERVAL_MS',
39
- parseEnv: (val: string) => +val,
40
- defaultValue: 100,
44
+ ...numberConfigHelper(100),
41
45
  description: 'The frequency in which to check.',
42
46
  },
43
47
  worldStateBlockRequestBatchSize: {
44
48
  env: 'WS_BLOCK_REQUEST_BATCH_SIZE',
45
- parseEnv: (val: string | undefined) => (val ? +val : undefined),
49
+ ...optionalNumberConfigHelper(),
46
50
  description: 'Size of the batch for each get-blocks request from the synchronizer to the archiver.',
47
51
  },
48
52
  worldStateDbMapSizeKb: {
49
53
  env: 'WS_DB_MAP_SIZE_KB',
50
- parseEnv: (val: string | undefined) => (val ? +val : undefined),
54
+ ...optionalNumberConfigHelper(),
51
55
  description: 'The maximum possible size of the world state DB in KB. Overwrites the general dataStoreMapSizeKb.',
52
56
  },
53
57
  archiveTreeMapSizeKb: {
54
58
  env: 'ARCHIVE_TREE_MAP_SIZE_KB',
55
- parseEnv: (val: string | undefined) => (val ? +val : undefined),
59
+ ...optionalNumberConfigHelper(),
56
60
  description:
57
61
  'The maximum possible size of the world state archive tree in KB. Overwrites the general worldStateDbMapSizeKb.',
58
62
  },
59
63
  nullifierTreeMapSizeKb: {
60
64
  env: 'NULLIFIER_TREE_MAP_SIZE_KB',
61
- parseEnv: (val: string | undefined) => (val ? +val : undefined),
65
+ ...optionalNumberConfigHelper(),
62
66
  description:
63
67
  'The maximum possible size of the world state nullifier tree in KB. Overwrites the general worldStateDbMapSizeKb.',
64
68
  },
65
69
  noteHashTreeMapSizeKb: {
66
70
  env: 'NOTE_HASH_TREE_MAP_SIZE_KB',
67
- parseEnv: (val: string | undefined) => (val ? +val : undefined),
71
+ ...optionalNumberConfigHelper(),
68
72
  description:
69
73
  'The maximum possible size of the world state note hash tree in KB. Overwrites the general worldStateDbMapSizeKb.',
70
74
  },
71
75
  messageTreeMapSizeKb: {
72
76
  env: 'MESSAGE_TREE_MAP_SIZE_KB',
73
- parseEnv: (val: string | undefined) => (val ? +val : undefined),
77
+ ...optionalNumberConfigHelper(),
74
78
  description:
75
79
  'The maximum possible size of the world state message tree in KB. Overwrites the general worldStateDbMapSizeKb.',
76
80
  },
77
81
  publicDataTreeMapSizeKb: {
78
82
  env: 'PUBLIC_DATA_TREE_MAP_SIZE_KB',
79
- parseEnv: (val: string | undefined) => (val ? +val : undefined),
83
+ ...optionalNumberConfigHelper(),
80
84
  description:
81
85
  'The maximum possible size of the world state public data tree in KB. Overwrites the general worldStateDbMapSizeKb.',
82
86
  },
@@ -84,9 +88,11 @@ export const worldStateConfigMappings: ConfigMappingsType<WorldStateConfig> = {
84
88
  env: 'WS_DATA_DIRECTORY',
85
89
  description: 'Optional directory for the world state database',
86
90
  },
87
- worldStateBlockHistory: {
88
- env: 'WS_NUM_HISTORIC_BLOCKS',
89
- description: 'The number of historic blocks to maintain. Values less than 1 mean all history is maintained',
91
+ worldStateCheckpointHistory: {
92
+ env: 'WS_NUM_HISTORIC_CHECKPOINTS',
93
+ description:
94
+ 'The number of historic checkpoints worth of blocks to maintain. Values less than 1 mean all history is maintained',
95
+ fallback: ['WS_NUM_HISTORIC_BLOCKS'],
90
96
  ...numberConfigHelper(64),
91
97
  },
92
98
  };
@@ -1,8 +1,8 @@
1
1
  import type { LoggerBindings } from '@aztec/foundation/log';
2
- import type { DataStoreConfig } from '@aztec/kv-store/config';
3
2
  import type { L2BlockSource } from '@aztec/stdlib/block';
3
+ import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
4
4
  import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
5
- import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees';
5
+ import { EMPTY_GENESIS_DATA, type GenesisData, isGenesisData } from '@aztec/stdlib/world-state';
6
6
  import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
7
7
 
8
8
  import { WorldStateInstrumentation } from '../instrumentation/instrumentation.js';
@@ -21,12 +21,14 @@ export interface WorldStateTreeMapSizes {
21
21
  export async function createWorldStateSynchronizer(
22
22
  config: WorldStateConfig & DataStoreConfig,
23
23
  l2BlockSource: L2BlockSource & L1ToL2MessageSource,
24
- prefilledPublicData: PublicDataTreeLeaf[] = [],
24
+ genesisOrNativeWorldState: GenesisData | NativeWorldStateService,
25
25
  client: TelemetryClient = getTelemetryClient(),
26
26
  bindings?: LoggerBindings,
27
27
  ) {
28
28
  const instrumentation = new WorldStateInstrumentation(client);
29
- const merkleTrees = await createWorldState(config, prefilledPublicData, instrumentation, bindings);
29
+ const merkleTrees = isGenesisData(genesisOrNativeWorldState)
30
+ ? await createWorldState(config, genesisOrNativeWorldState, instrumentation, bindings)
31
+ : genesisOrNativeWorldState;
30
32
  return new ServerWorldStateSynchronizer(merkleTrees, l2BlockSource, config, instrumentation);
31
33
  }
32
34
 
@@ -41,8 +43,8 @@ export async function createWorldState(
41
43
  | 'messageTreeMapSizeKb'
42
44
  | 'publicDataTreeMapSizeKb'
43
45
  > &
44
- Pick<DataStoreConfig, 'dataDirectory' | 'dataStoreMapSizeKb' | 'l1Contracts'>,
45
- prefilledPublicData: PublicDataTreeLeaf[] = [],
46
+ Pick<DataStoreConfig, 'dataDirectory' | 'dataStoreMapSizeKb' | 'rollupAddress'>,
47
+ genesis: GenesisData = EMPTY_GENESIS_DATA,
46
48
  instrumentation: WorldStateInstrumentation = new WorldStateInstrumentation(getTelemetryClient()),
47
49
  bindings?: LoggerBindings,
48
50
  ) {
@@ -56,24 +58,24 @@ export async function createWorldState(
56
58
  publicDataTreeMapSizeKb: config.publicDataTreeMapSizeKb ?? dataStoreMapSizeKb,
57
59
  };
58
60
 
59
- if (!config.l1Contracts?.rollupAddress) {
61
+ if (!config.rollupAddress) {
60
62
  throw new Error('Rollup address is required to create a world state synchronizer.');
61
63
  }
62
64
 
63
65
  // If a data directory is provided in config, then create a persistent store.
64
66
  const merkleTrees = dataDirectory
65
67
  ? await NativeWorldStateService.new(
66
- config.l1Contracts.rollupAddress,
68
+ config.rollupAddress,
67
69
  dataDirectory,
68
70
  wsTreeMapSizes,
69
- prefilledPublicData,
71
+ genesis,
70
72
  instrumentation,
71
73
  bindings,
72
74
  )
73
75
  : await NativeWorldStateService.tmp(
74
- config.l1Contracts.rollupAddress,
76
+ config.rollupAddress,
75
77
  !['true', '1'].includes(process.env.DEBUG_WORLD_STATE!),
76
- prefilledPublicData,
78
+ genesis,
77
79
  instrumentation,
78
80
  bindings,
79
81
  );