@aztec/world-state 0.0.0-test.1 → 0.0.1-commit.017a351

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) 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 +25 -41
  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 +7 -1
  9. package/dest/native/fork_checkpoint.d.ts.map +1 -1
  10. package/dest/native/fork_checkpoint.js +15 -3
  11. package/dest/native/index.d.ts +1 -1
  12. package/dest/native/ipc_world_state_instance.d.ts +45 -0
  13. package/dest/native/ipc_world_state_instance.d.ts.map +1 -0
  14. package/dest/native/ipc_world_state_instance.js +750 -0
  15. package/dest/native/merkle_trees_facade.d.ts +25 -9
  16. package/dest/native/merkle_trees_facade.d.ts.map +1 -1
  17. package/dest/native/merkle_trees_facade.js +109 -25
  18. package/dest/native/message.d.ts +87 -58
  19. package/dest/native/message.d.ts.map +1 -1
  20. package/dest/native/message.js +61 -61
  21. package/dest/native/native_world_state.d.ts +41 -21
  22. package/dest/native/native_world_state.d.ts.map +1 -1
  23. package/dest/native/native_world_state.js +177 -50
  24. package/dest/native/native_world_state_instance.d.ts +23 -25
  25. package/dest/native/native_world_state_instance.d.ts.map +1 -1
  26. package/dest/native/native_world_state_instance.js +8 -163
  27. package/dest/native/world_state_ops_queue.d.ts +1 -1
  28. package/dest/native/world_state_ops_queue.d.ts.map +1 -1
  29. package/dest/native/world_state_ops_queue.js +6 -6
  30. package/dest/synchronizer/config.d.ts +14 -6
  31. package/dest/synchronizer/config.d.ts.map +1 -1
  32. package/dest/synchronizer/config.js +36 -14
  33. package/dest/synchronizer/errors.d.ts +4 -0
  34. package/dest/synchronizer/errors.d.ts.map +1 -0
  35. package/dest/synchronizer/errors.js +5 -0
  36. package/dest/synchronizer/factory.d.ts +13 -5
  37. package/dest/synchronizer/factory.d.ts.map +1 -1
  38. package/dest/synchronizer/factory.js +15 -9
  39. package/dest/synchronizer/index.d.ts +1 -1
  40. package/dest/synchronizer/server_world_state_synchronizer.d.ts +21 -31
  41. package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
  42. package/dest/synchronizer/server_world_state_synchronizer.js +220 -96
  43. package/dest/test/index.d.ts +1 -1
  44. package/dest/test/utils.d.ts +12 -5
  45. package/dest/test/utils.d.ts.map +1 -1
  46. package/dest/test/utils.js +54 -47
  47. package/dest/testing.d.ts +6 -5
  48. package/dest/testing.d.ts.map +1 -1
  49. package/dest/testing.js +15 -15
  50. package/dest/world-state-db/index.d.ts +1 -1
  51. package/dest/world-state-db/merkle_tree_db.d.ts +12 -18
  52. package/dest/world-state-db/merkle_tree_db.d.ts.map +1 -1
  53. package/package.json +27 -25
  54. package/src/instrumentation/instrumentation.ts +31 -43
  55. package/src/native/bench_metrics.ts +91 -0
  56. package/src/native/fork_checkpoint.ts +19 -3
  57. package/src/native/ipc_world_state_instance.ts +863 -0
  58. package/src/native/merkle_trees_facade.ts +127 -33
  59. package/src/native/message.ts +109 -80
  60. package/src/native/native_world_state.ts +207 -64
  61. package/src/native/native_world_state_instance.ts +28 -223
  62. package/src/native/world_state_ops_queue.ts +6 -6
  63. package/src/synchronizer/config.ts +58 -20
  64. package/src/synchronizer/errors.ts +5 -0
  65. package/src/synchronizer/factory.ts +48 -18
  66. package/src/synchronizer/server_world_state_synchronizer.ts +249 -121
  67. package/src/test/utils.ts +92 -82
  68. package/src/testing.ts +11 -16
  69. package/src/world-state-db/merkle_tree_db.ts +16 -18
@@ -1,59 +1,93 @@
1
+ var _computedKey;
1
2
  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';
3
- import { EthAddress } from '@aztec/foundation/eth-address';
4
- import { Fr } from '@aztec/foundation/fields';
3
+ import { BlockNumber } from '@aztec/foundation/branded-types';
4
+ import { fromEntries, padArrayEnd } from '@aztec/foundation/collection';
5
+ import { Fr } from '@aztec/foundation/curves/bn254';
5
6
  import { createLogger } from '@aztec/foundation/log';
6
- import { DatabaseVersionManager } from '@aztec/stdlib/database-version';
7
+ import { DatabaseVersionManager } from '@aztec/stdlib/database-version/manager';
7
8
  import { MerkleTreeId, NullifierLeaf, PublicDataTreeLeaf } from '@aztec/stdlib/trees';
8
- import { BlockHeader, PartialStateReference, StateReference } from '@aztec/stdlib/tx';
9
+ import { BlockHeader, GlobalVariables, PartialStateReference, StateReference } from '@aztec/stdlib/tx';
10
+ import { EMPTY_GENESIS_DATA, WorldStateRevision } from '@aztec/stdlib/world-state';
9
11
  import { getTelemetryClient } from '@aztec/telemetry-client';
10
12
  import assert from 'assert/strict';
11
- import { mkdtemp, rm } from 'fs/promises';
13
+ import { mkdir, mkdtemp, rm } from 'fs/promises';
12
14
  import { tmpdir } from 'os';
13
15
  import { join } from 'path';
14
16
  import { WorldStateInstrumentation } from '../instrumentation/instrumentation.js';
17
+ import { IpcWorldState } from './ipc_world_state_instance.js';
15
18
  import { MerkleTreesFacade, MerkleTreesForkFacade, serializeLeaf } from './merkle_trees_facade.js';
16
- import { WorldStateMessageType, blockStateReference, sanitiseFullStatus, sanitiseSummary, treeStateReferenceToSnapshot, worldStateRevision } from './message.js';
17
- import { NativeWorldState } from './native_world_state_instance.js';
19
+ import { WorldStateMessageType, blockStateReference, sanitizeFullStatus, sanitizeSummary, treeStateReferenceToSnapshot } from './message.js';
18
20
  // The current version of the world state database schema
19
21
  // Increment this when making incompatible changes to the database schema
20
- export const WORLD_STATE_DB_VERSION = 1; // The initial version
22
+ export const WORLD_STATE_DB_VERSION = 2; // The initial version
23
+ export const WORLD_STATE_DIR = 'world_state';
24
+ _computedKey = Symbol.asyncDispose;
21
25
  export class NativeWorldStateService {
22
26
  instance;
23
27
  worldStateInstrumentation;
24
28
  log;
29
+ genesis;
25
30
  cleanup;
31
+ recreateInstance;
26
32
  initialHeader;
27
33
  // This is read heavily and only changes when data is persisted, so we cache it
28
34
  cachedStatusSummary;
29
- constructor(instance, worldStateInstrumentation, log = createLogger('world-state:database'), cleanup = ()=>Promise.resolve()){
35
+ constructor(instance, worldStateInstrumentation, log, genesis = EMPTY_GENESIS_DATA, cleanup = ()=>Promise.resolve(), /** Factory to recreate a fresh IpcWorldState after clear(). */ recreateInstance){
30
36
  this.instance = instance;
31
37
  this.worldStateInstrumentation = worldStateInstrumentation;
32
38
  this.log = log;
39
+ this.genesis = genesis;
33
40
  this.cleanup = cleanup;
41
+ this.recreateInstance = recreateInstance;
34
42
  }
35
- static async new(rollupAddress, dataDir, dbMapSizeKb, prefilledPublicData = [], instrumentation = new WorldStateInstrumentation(getTelemetryClient()), log = createLogger('world-state:database'), cleanup = ()=>Promise.resolve()) {
36
- const worldStateDirectory = join(dataDir, 'world_state');
37
- // Create a version manager to handle versioning
38
- const versionManager = new DatabaseVersionManager(WORLD_STATE_DB_VERSION, rollupAddress, worldStateDirectory, (dir)=>{
39
- return Promise.resolve(new NativeWorldState(dir, dbMapSizeKb, prefilledPublicData, instrumentation));
43
+ static async new(rollupAddress, dataDir, wsTreeMapSizes, genesis = EMPTY_GENESIS_DATA, instrumentation = new WorldStateInstrumentation(getTelemetryClient()), bindings, cleanup = ()=>Promise.resolve()) {
44
+ for (const [key, value] of Object.entries(wsTreeMapSizes)){
45
+ if (value <= 0) {
46
+ throw new Error(`Map size must be a positive number, got ${value} for ${key}`);
47
+ }
48
+ }
49
+ const log = createLogger('world-state:database', bindings);
50
+ const worldStateDirectory = join(dataDir, WORLD_STATE_DIR);
51
+ const versionManager = new DatabaseVersionManager({
52
+ schemaVersion: WORLD_STATE_DB_VERSION,
53
+ rollupAddress,
54
+ dataDirectory: worldStateDirectory,
55
+ onOpen: (dir)=>IpcWorldState.spawn(dir, wsTreeMapSizes, genesis, instrumentation, bindings)
40
56
  });
41
57
  const [instance] = await versionManager.open();
42
- const worldState = new this(instance, instrumentation, log, cleanup);
58
+ const recreateInstance = async ()=>{
59
+ await rm(worldStateDirectory, {
60
+ recursive: true,
61
+ force: true,
62
+ maxRetries: 3
63
+ });
64
+ await mkdir(worldStateDirectory, {
65
+ recursive: true
66
+ });
67
+ return IpcWorldState.spawn(worldStateDirectory, wsTreeMapSizes, genesis, instrumentation, bindings);
68
+ };
69
+ const worldState = new this(instance, instrumentation, log, genesis, cleanup, recreateInstance);
43
70
  try {
44
71
  await worldState.init();
45
72
  } catch (e) {
46
- log.error(`Error initialising world state: ${e}`);
73
+ log.error(`Error initializing world state: ${e}`);
47
74
  throw e;
48
75
  }
49
76
  return worldState;
50
77
  }
51
- static async tmp(rollupAddress = EthAddress.ZERO, cleanupTmpDir = true, prefilledPublicData = [], instrumentation = new WorldStateInstrumentation(getTelemetryClient())) {
52
- const log = createLogger('world-state:database');
78
+ static async tmp(cleanupTmpDir = true, genesis = EMPTY_GENESIS_DATA, instrumentation = new WorldStateInstrumentation(getTelemetryClient()), bindings) {
79
+ const log = createLogger('world-state:database', bindings);
53
80
  const dataDir = await mkdtemp(join(tmpdir(), 'aztec-world-state-'));
54
81
  const dbMapSizeKb = 10 * 1024 * 1024;
55
- log.debug(`Created temporary world state database at: ${dataDir} with size: ${dbMapSizeKb}`);
56
- // pass a cleanup callback because process.on('beforeExit', cleanup) does not work under Jest
82
+ const worldStateTreeMapSizes = {
83
+ archiveTreeMapSizeKb: dbMapSizeKb,
84
+ nullifierTreeMapSizeKb: dbMapSizeKb,
85
+ noteHashTreeMapSizeKb: dbMapSizeKb,
86
+ messageTreeMapSizeKb: dbMapSizeKb,
87
+ publicDataTreeMapSizeKb: dbMapSizeKb
88
+ };
89
+ log.debug(`Created temporary world state database at: ${dataDir} with tree map size: ${dbMapSizeKb}`);
90
+ const instance = await IpcWorldState.spawn(dataDir, worldStateTreeMapSizes, genesis, instrumentation, bindings);
57
91
  const cleanup = async ()=>{
58
92
  if (cleanupTmpDir) {
59
93
  await rm(dataDir, {
@@ -66,7 +100,35 @@ export class NativeWorldStateService {
66
100
  log.debug(`Leaving temporary world state database: ${dataDir}`);
67
101
  }
68
102
  };
69
- return this.new(rollupAddress, dataDir, dbMapSizeKb, prefilledPublicData, instrumentation, log, cleanup);
103
+ const recreateInstance = async ()=>{
104
+ await rm(dataDir, {
105
+ recursive: true,
106
+ force: true,
107
+ maxRetries: 3
108
+ });
109
+ await mkdir(dataDir, {
110
+ recursive: true
111
+ });
112
+ return IpcWorldState.spawn(dataDir, worldStateTreeMapSizes, genesis, instrumentation, bindings);
113
+ };
114
+ const worldState = new this(instance, instrumentation, log, genesis, cleanup, recreateInstance);
115
+ try {
116
+ await worldState.init();
117
+ } catch (e) {
118
+ log.error(`Error initializing tmp world state: ${e}`);
119
+ throw e;
120
+ }
121
+ return worldState;
122
+ }
123
+ static ephemeral(genesis = EMPTY_GENESIS_DATA, instrumentation = new WorldStateInstrumentation(getTelemetryClient()), bindings) {
124
+ return this.tmp(/*cleanupTmpDir=*/ true, genesis, instrumentation, bindings);
125
+ }
126
+ static async fromIpc(wsdbBackend, instrumentation = new WorldStateInstrumentation(getTelemetryClient()), bindings, genesis = EMPTY_GENESIS_DATA, cleanup = ()=>Promise.resolve(), recreateInstance) {
127
+ const log = createLogger('world-state:database', bindings);
128
+ const instance = new IpcWorldState(wsdbBackend, instrumentation, bindings);
129
+ const worldState = new this(instance, instrumentation, log, genesis, cleanup, recreateInstance);
130
+ await worldState.init();
131
+ return worldState;
70
132
  }
71
133
  async init() {
72
134
  const status = await this.getStatusSummary();
@@ -88,27 +150,49 @@ export class NativeWorldStateService {
88
150
  const initialHeaderIndex = indices[0];
89
151
  assert.strictEqual(initialHeaderIndex, 0n, 'Invalid initial archive state');
90
152
  }
153
+ async clear() {
154
+ if (!this.recreateInstance) {
155
+ throw new Error('clear() is not available for externally-managed IPC backends');
156
+ }
157
+ this.log.warn('Clearing world state: shutting down WSDB, deleting data, and recreating');
158
+ await this.instance.close();
159
+ this.cachedStatusSummary = undefined;
160
+ this.instance = await this.recreateInstance();
161
+ await this.init();
162
+ this.log.info('World state cleared and reinitialized from genesis');
163
+ }
164
+ /** Returns the socket path of the underlying IPC backend, if available. */ getSocketPath() {
165
+ if (this.instance instanceof IpcWorldState) {
166
+ return this.instance.getSocketPath();
167
+ }
168
+ throw new Error('getSocketPath() is only available with IPC world state');
169
+ }
91
170
  getCommitted() {
92
- return new MerkleTreesFacade(this.instance, this.initialHeader, worldStateRevision(false, 0, 0));
171
+ return new MerkleTreesFacade(this.instance, this.initialHeader, WorldStateRevision.empty());
93
172
  }
94
173
  getSnapshot(blockNumber) {
95
- return new MerkleTreesFacade(this.instance, this.initialHeader, worldStateRevision(false, 0, blockNumber));
174
+ return new MerkleTreesFacade(this.instance, this.initialHeader, new WorldStateRevision(/*forkId=*/ 0, /* blockNumber=*/ blockNumber, /* includeUncommitted=*/ false));
96
175
  }
97
- async fork(blockNumber) {
176
+ async fork(blockNumber, opts = {}) {
98
177
  const resp = await this.instance.call(WorldStateMessageType.CREATE_FORK, {
99
178
  latest: blockNumber === undefined,
100
- blockNumber: blockNumber ?? 0,
179
+ blockNumber: blockNumber ?? BlockNumber.ZERO,
101
180
  canonical: true
102
181
  });
103
- return new MerkleTreesForkFacade(this.instance, this.initialHeader, worldStateRevision(true, resp.forkId, 0));
182
+ return new MerkleTreesForkFacade(this.instance, this.initialHeader, new WorldStateRevision(/*forkId=*/ resp.forkId, /* blockNumber=*/ WorldStateRevision.LATEST, /* includeUncommitted=*/ true), opts);
104
183
  }
105
184
  getInitialHeader() {
106
185
  return this.initialHeader;
107
186
  }
108
187
  async handleL2BlockAndMessages(l2Block, l1ToL2Messages) {
109
- // We have to pad both the values within tx effects because that's how the trees are built by circuits.
188
+ const isFirstBlock = l2Block.indexWithinCheckpoint === 0;
189
+ if (!isFirstBlock && l1ToL2Messages.length > 0) {
190
+ throw new Error(`L1 to L2 messages must be empty for non-first blocks, but got ${l1ToL2Messages.length} messages for block ${l2Block.number}.`);
191
+ }
192
+ // We have to pad the given l1 to l2 messages, and the note hashes and nullifiers within tx effects, because that's
193
+ // how the trees are built by circuits.
194
+ const paddedL1ToL2Messages = isFirstBlock ? padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP) : [];
110
195
  const paddedNoteHashes = l2Block.body.txEffects.flatMap((txEffect)=>padArrayEnd(txEffect.noteHashes, Fr.ZERO, MAX_NOTE_HASHES_PER_TX));
111
- const paddedL1ToL2Messages = padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
112
196
  const paddedNullifiers = l2Block.body.txEffects.flatMap((txEffect)=>padArrayEnd(txEffect.nullifiers, Fr.ZERO, MAX_NULLIFIERS_PER_TX)).map((nullifier)=>new NullifierLeaf(nullifier));
113
197
  const publicDataWrites = l2Block.body.txEffects.flatMap((txEffect)=>{
114
198
  return txEffect.publicDataWrites.map((write)=>{
@@ -121,56 +205,65 @@ export class NativeWorldStateService {
121
205
  try {
122
206
  return await this.instance.call(WorldStateMessageType.SYNC_BLOCK, {
123
207
  blockNumber: l2Block.number,
124
- blockHeaderHash: await l2Block.header.hash(),
208
+ blockHeaderHash: (await l2Block.hash()).toBuffer(),
125
209
  paddedL1ToL2Messages: paddedL1ToL2Messages.map(serializeLeaf),
126
210
  paddedNoteHashes: paddedNoteHashes.map(serializeLeaf),
127
211
  paddedNullifiers: paddedNullifiers.map(serializeLeaf),
128
212
  publicDataWrites: publicDataWrites.map(serializeLeaf),
129
213
  blockStateRef: blockStateReference(l2Block.header.state),
130
214
  canonical: true
131
- }, this.sanitiseAndCacheSummaryFromFull.bind(this), this.deleteCachedSummary.bind(this));
215
+ }, this.sanitizeAndCacheSummaryFromFull.bind(this), this.deleteCachedSummary.bind(this));
132
216
  } catch (err) {
133
217
  this.worldStateInstrumentation.incCriticalErrors('synch_pending_block');
134
218
  throw err;
135
219
  }
136
220
  }
137
221
  async close() {
138
- await this.instance.close();
139
- await this.cleanup();
222
+ try {
223
+ await this.instance.close();
224
+ } finally{
225
+ await this.cleanup();
226
+ }
227
+ }
228
+ async [_computedKey]() {
229
+ await this.close();
140
230
  }
141
231
  async buildInitialHeader() {
142
232
  const state = await this.getInitialStateReference();
143
233
  return BlockHeader.empty({
144
- state
234
+ state,
235
+ globalVariables: GlobalVariables.empty({
236
+ timestamp: this.genesis.genesisTimestamp
237
+ })
145
238
  });
146
239
  }
147
- sanitiseAndCacheSummaryFromFull(response) {
148
- const sanitised = sanitiseFullStatus(response);
240
+ sanitizeAndCacheSummaryFromFull(response) {
241
+ const sanitized = sanitizeFullStatus(response);
149
242
  this.cachedStatusSummary = {
150
- ...sanitised.summary
243
+ ...sanitized.summary
151
244
  };
152
- return sanitised;
245
+ return sanitized;
153
246
  }
154
- sanitiseAndCacheSummary(response) {
155
- const sanitised = sanitiseSummary(response);
247
+ sanitizeAndCacheSummary(response) {
248
+ const sanitized = sanitizeSummary(response);
156
249
  this.cachedStatusSummary = {
157
- ...sanitised
250
+ ...sanitized
158
251
  };
159
- return sanitised;
252
+ return sanitized;
160
253
  }
161
254
  deleteCachedSummary(_) {
162
255
  this.cachedStatusSummary = undefined;
163
256
  }
164
257
  /**
165
- * Advances the finalised block number to be the number provided
166
- * @param toBlockNumber The block number that is now the tip of the finalised chain
258
+ * Advances the finalized block number to be the number provided
259
+ * @param toBlockNumber The block number that is now the tip of the finalized chain
167
260
  * @returns The new WorldStateStatus
168
- */ async setFinalised(toBlockNumber) {
261
+ */ async setFinalized(toBlockNumber) {
169
262
  try {
170
- await this.instance.call(WorldStateMessageType.FINALISE_BLOCKS, {
263
+ await this.instance.call(WorldStateMessageType.FINALIZE_BLOCKS, {
171
264
  toBlockNumber,
172
265
  canonical: true
173
- }, this.sanitiseAndCacheSummary.bind(this), this.deleteCachedSummary.bind(this));
266
+ }, this.sanitizeAndCacheSummary.bind(this), this.deleteCachedSummary.bind(this));
174
267
  } catch (err) {
175
268
  this.worldStateInstrumentation.incCriticalErrors('finalize_block');
176
269
  throw err;
@@ -186,7 +279,7 @@ export class NativeWorldStateService {
186
279
  return await this.instance.call(WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS, {
187
280
  toBlockNumber,
188
281
  canonical: true
189
- }, this.sanitiseAndCacheSummaryFromFull.bind(this), this.deleteCachedSummary.bind(this));
282
+ }, this.sanitizeAndCacheSummaryFromFull.bind(this), this.deleteCachedSummary.bind(this));
190
283
  } catch (err) {
191
284
  this.worldStateInstrumentation.incCriticalErrors('prune_historical_block');
192
285
  throw err;
@@ -201,7 +294,7 @@ export class NativeWorldStateService {
201
294
  return await this.instance.call(WorldStateMessageType.UNWIND_BLOCKS, {
202
295
  toBlockNumber,
203
296
  canonical: true
204
- }, this.sanitiseAndCacheSummaryFromFull.bind(this), this.deleteCachedSummary.bind(this));
297
+ }, this.sanitizeAndCacheSummaryFromFull.bind(this), this.deleteCachedSummary.bind(this));
205
298
  } catch (err) {
206
299
  this.worldStateInstrumentation.incCriticalErrors('prune_pending_block');
207
300
  throw err;
@@ -215,7 +308,7 @@ export class NativeWorldStateService {
215
308
  }
216
309
  return await this.instance.call(WorldStateMessageType.GET_STATUS, {
217
310
  canonical: true
218
- }, this.sanitiseAndCacheSummary.bind(this));
311
+ }, this.sanitizeAndCacheSummary.bind(this));
219
312
  }
220
313
  updateLeaf(_treeId, _leaf, _index) {
221
314
  return Promise.reject(new Error('Method not implemented'));
@@ -226,4 +319,38 @@ export class NativeWorldStateService {
226
319
  });
227
320
  return new StateReference(treeStateReferenceToSnapshot(resp.state[MerkleTreeId.L1_TO_L2_MESSAGE_TREE]), new PartialStateReference(treeStateReferenceToSnapshot(resp.state[MerkleTreeId.NOTE_HASH_TREE]), treeStateReferenceToSnapshot(resp.state[MerkleTreeId.NULLIFIER_TREE]), treeStateReferenceToSnapshot(resp.state[MerkleTreeId.PUBLIC_DATA_TREE])));
228
321
  }
322
+ async backupTo(dstPath, compact = true) {
323
+ await this.instance.call(WorldStateMessageType.COPY_STORES, {
324
+ dstPath,
325
+ compact,
326
+ canonical: true
327
+ });
328
+ return fromEntries(NATIVE_WORLD_STATE_DBS.map(([name, dir])=>[
329
+ name,
330
+ join(dstPath, dir, 'data.mdb')
331
+ ]));
332
+ }
229
333
  }
334
+ // The following paths are defined in cpp-land
335
+ export const NATIVE_WORLD_STATE_DBS = [
336
+ [
337
+ 'l1-to-l2-message-tree',
338
+ 'L1ToL2MessageTree'
339
+ ],
340
+ [
341
+ 'archive-tree',
342
+ 'ArchiveTree'
343
+ ],
344
+ [
345
+ 'public-data-tree',
346
+ 'PublicDataTree'
347
+ ],
348
+ [
349
+ 'note-hash-tree',
350
+ 'NoteHashTree'
351
+ ],
352
+ [
353
+ 'nullifier-tree',
354
+ 'NullifierTree'
355
+ ]
356
+ ];
@@ -1,33 +1,31 @@
1
- import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees';
2
- import type { WorldStateInstrumentation } from '../instrumentation/instrumentation.js';
3
- import { WorldStateMessageType, type WorldStateRequest, type WorldStateRequestCategories, type WorldStateResponse } from './message.js';
4
- export interface NativeWorldStateInstance {
5
- call<T extends WorldStateMessageType>(messageType: T, body: WorldStateRequest[T] & WorldStateRequestCategories): Promise<WorldStateResponse[T]>;
6
- }
1
+ import type { WorldStateMessageType, WorldStateRequest, WorldStateRequestCategories, WorldStateResponse } from './message.js';
7
2
  /**
8
- * Strongly-typed interface to access the WorldState class in the native world_state_napi module.
3
+ * Backend-agnostic handle to a running aztec-wsdb world state, accessed by the TS layer.
4
+ *
5
+ * Two implementations exist:
6
+ * - {@link IpcWorldState} — talks to a standalone aztec-wsdb process over UDS or shared memory.
7
+ *
8
+ * The legacy in-process NAPI implementation has been removed; the C++ AVM (NAPI) now connects to
9
+ * the same aztec-wsdb process via UDS using the socket path returned by {@link getSocketPath}.
9
10
  */
10
- export declare class NativeWorldState implements NativeWorldStateInstance {
11
- private instrumentation;
12
- private log;
13
- private open;
14
- private queues;
15
- private instance;
16
- /** Creates a new native WorldState instance */
17
- constructor(dataDir: string, dbMapSizeKb: number, prefilledPublicData: PublicDataTreeLeaf[] | undefined, instrumentation: WorldStateInstrumentation, log?: import("@aztec/foundation/log").Logger);
11
+ export interface NativeWorldStateInstance {
12
+ /**
13
+ * Send a typed msgpack message to the backing world state and await its response.
14
+ *
15
+ * @param responseHandler — optional pre-resolution hook executed on the per-fork queue, useful
16
+ * for caching responses while the queue still holds the fork lock.
17
+ * @param errorHandler optional pre-rejection hook executed on the per-fork queue.
18
+ */
19
+ call<T extends WorldStateMessageType>(messageType: T, body: WorldStateRequest[T] & WorldStateRequestCategories, responseHandler?: (response: WorldStateResponse[T]) => WorldStateResponse[T], errorHandler?: (error: string) => void): Promise<WorldStateResponse[T]>;
18
20
  /**
19
- * Sends a message to the native instance and returns the response.
20
- * @param messageType - The type of message to send
21
- * @param body - The message body
22
- * @param responseHandler - A callback accepting the response, executed on the job queue
23
- * @param errorHandler - A callback called on request error, executed on the job queue
24
- * @returns The response to the message
21
+ * UDS path the underlying aztec-wsdb process listens on. The C++ AVM uses this to attach to the
22
+ * same world state instance the TS layer is using.
25
23
  */
26
- call<T extends WorldStateMessageType>(messageType: T, body: WorldStateRequest[T] & WorldStateRequestCategories, responseHandler?: (response: WorldStateResponse[T]) => WorldStateResponse[T], errorHandler?: (_: string) => void): Promise<WorldStateResponse[T]>;
24
+ getSocketPath(): string;
27
25
  /**
28
- * Stops the native instance.
26
+ * Shut down the world state instance. Cancels any in-flight queues, closes the IPC channel, and
27
+ * terminates the underlying aztec-wsdb process. Idempotent.
29
28
  */
30
29
  close(): Promise<void>;
31
- private _sendMessage;
32
30
  }
33
- //# sourceMappingURL=native_world_state_instance.d.ts.map
31
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmF0aXZlX3dvcmxkX3N0YXRlX2luc3RhbmNlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbmF0aXZlL25hdGl2ZV93b3JsZF9zdGF0ZV9pbnN0YW5jZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFDVixxQkFBcUIsRUFDckIsaUJBQWlCLEVBQ2pCLDJCQUEyQixFQUMzQixrQkFBa0IsRUFDbkIsTUFBTSxjQUFjLENBQUM7QUFFdEI7Ozs7Ozs7O0dBUUc7QUFDSCxNQUFNLFdBQVcsd0JBQXdCO0lBQ3ZDOzs7Ozs7T0FNRztJQUNILElBQUksQ0FBQyxDQUFDLFNBQVMscUJBQXFCLEVBQ2xDLFdBQVcsRUFBRSxDQUFDLEVBQ2QsSUFBSSxFQUFFLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxHQUFHLDJCQUEyQixFQUN4RCxlQUFlLENBQUMsRUFBRSxDQUFDLFFBQVEsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsS0FBSyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsRUFDNUUsWUFBWSxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsTUFBTSxLQUFLLElBQUksR0FDckMsT0FBTyxDQUFDLGtCQUFrQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFbEM7OztPQUdHO0lBQ0gsYUFBYSxJQUFJLE1BQU0sQ0FBQztJQUV4Qjs7O09BR0c7SUFDSCxLQUFLLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0NBQ3hCIn0=
@@ -1 +1 @@
1
- {"version":3,"file":"native_world_state_instance.d.ts","sourceRoot":"","sources":["../../src/native/native_world_state_instance.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAK9D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAC;AACvF,OAAO,EACL,qBAAqB,EACrB,KAAK,iBAAiB,EACtB,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,EAIxB,MAAM,cAAc,CAAC;AAKtB,MAAM,WAAW,wBAAwB;IACvC,IAAI,CAAC,CAAC,SAAS,qBAAqB,EAClC,WAAW,EAAE,CAAC,EACd,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,2BAA2B,GACvD,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,qBAAa,gBAAiB,YAAW,wBAAwB;IAa7D,OAAO,CAAC,eAAe;IACvB,OAAO,CAAC,GAAG;IAbb,OAAO,CAAC,IAAI,CAAQ;IAGpB,OAAO,CAAC,MAAM,CAAyC;IAEvD,OAAO,CAAC,QAAQ,CAA+E;IAE/F,+CAA+C;gBAE7C,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,mBAAmB,kCAA2B,EACtC,eAAe,EAAE,yBAAyB,EAC1C,GAAG,yCAAuC;IA8BpD;;;;;;;OAOG;IACU,IAAI,CAAC,CAAC,SAAS,qBAAqB,EAC/C,WAAW,EAAE,CAAC,EACd,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,2BAA2B,EAExD,eAAe,cAAc,kBAAkB,CAAC,CAAC,CAAC,KAAG,kBAAkB,CAAC,CAAC,CAAa,EACtF,YAAY,OAAO,MAAM,SAAO,GAC/B,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC;IAuDjC;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAiBrB,YAAY;CA8D3B"}
1
+ {"version":3,"file":"native_world_state_instance.d.ts","sourceRoot":"","sources":["../../src/native/native_world_state_instance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,iBAAiB,EACjB,2BAA2B,EAC3B,kBAAkB,EACnB,MAAM,cAAc,CAAC;AAEtB;;;;;;;;GAQG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;;;OAMG;IACH,IAAI,CAAC,CAAC,SAAS,qBAAqB,EAClC,WAAW,EAAE,CAAC,EACd,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,2BAA2B,EACxD,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,KAAK,kBAAkB,CAAC,CAAC,CAAC,EAC5E,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GACrC,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;IAElC;;;OAGG;IACH,aAAa,IAAI,MAAM,CAAC;IAExB;;;OAGG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB"}
@@ -1,164 +1,9 @@
1
- import { ARCHIVE_HEIGHT, GeneratorIndex, L1_TO_L2_MSG_TREE_HEIGHT, MAX_NULLIFIERS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NOTE_HASH_TREE_HEIGHT, NULLIFIER_TREE_HEIGHT, PUBLIC_DATA_TREE_HEIGHT } from '@aztec/constants';
2
- import { createLogger } from '@aztec/foundation/log';
3
- import { NativeWorldState as BaseNativeWorldState, MsgpackChannel } from '@aztec/native';
4
- import { MerkleTreeId } from '@aztec/stdlib/trees';
5
- import assert from 'assert';
6
- import { cpus } from 'os';
7
- import { WorldStateMessageType, isWithCanonical, isWithForkId, isWithRevision } from './message.js';
8
- import { WorldStateOpsQueue } from './world_state_ops_queue.js';
9
- const MAX_WORLD_STATE_THREADS = +(process.env.HARDWARE_CONCURRENCY || '16');
10
1
  /**
11
- * Strongly-typed interface to access the WorldState class in the native world_state_napi module.
12
- */ export class NativeWorldState {
13
- instrumentation;
14
- log;
15
- open;
16
- // We maintain a map of queue to fork
17
- queues;
18
- instance;
19
- /** Creates a new native WorldState instance */ constructor(dataDir, dbMapSizeKb, prefilledPublicData = [], instrumentation, log = createLogger('world-state:database')){
20
- this.instrumentation = instrumentation;
21
- this.log = log;
22
- this.open = true;
23
- this.queues = new Map();
24
- const threads = Math.min(cpus().length, MAX_WORLD_STATE_THREADS);
25
- log.info(`Creating world state data store at directory ${dataDir} with map size ${dbMapSizeKb} KB and ${threads} threads.`);
26
- const prefilledPublicDataBufferArray = prefilledPublicData.map((d)=>[
27
- d.slot.toBuffer(),
28
- d.value.toBuffer()
29
- ]);
30
- const ws = new BaseNativeWorldState(dataDir, {
31
- [MerkleTreeId.NULLIFIER_TREE]: NULLIFIER_TREE_HEIGHT,
32
- [MerkleTreeId.NOTE_HASH_TREE]: NOTE_HASH_TREE_HEIGHT,
33
- [MerkleTreeId.PUBLIC_DATA_TREE]: PUBLIC_DATA_TREE_HEIGHT,
34
- [MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: L1_TO_L2_MSG_TREE_HEIGHT,
35
- [MerkleTreeId.ARCHIVE]: ARCHIVE_HEIGHT
36
- }, {
37
- [MerkleTreeId.NULLIFIER_TREE]: 2 * MAX_NULLIFIERS_PER_TX,
38
- [MerkleTreeId.PUBLIC_DATA_TREE]: 2 * MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
39
- }, prefilledPublicDataBufferArray, GeneratorIndex.BLOCK_HASH, dbMapSizeKb, threads);
40
- this.instance = new MsgpackChannel(ws);
41
- // Manually create the queue for the canonical fork
42
- this.queues.set(0, new WorldStateOpsQueue());
43
- }
44
- /**
45
- * Sends a message to the native instance and returns the response.
46
- * @param messageType - The type of message to send
47
- * @param body - The message body
48
- * @param responseHandler - A callback accepting the response, executed on the job queue
49
- * @param errorHandler - A callback called on request error, executed on the job queue
50
- * @returns The response to the message
51
- */ async call(messageType, body, // allows for the pre-processing of responses on the job queue before being passed back
52
- responseHandler = (response)=>response, errorHandler = (_)=>{}) {
53
- // Here we determine which fork the request is being executed against and whether it requires uncommitted data
54
- // We use the fork Id to select the appropriate request queue and the uncommitted data flag to pass to the queue
55
- let forkId = -1;
56
- // We assume it includes uncommitted unless explicitly told otherwise
57
- let committedOnly = false;
58
- // Canonical requests ALWAYS go against the canonical fork
59
- // These include things like block syncs/unwinds etc
60
- // These requests don't contain a fork ID
61
- if (isWithCanonical(body)) {
62
- forkId = 0;
63
- } else if (isWithForkId(body)) {
64
- forkId = body.forkId;
65
- } else if (isWithRevision(body)) {
66
- forkId = body.revision.forkId;
67
- committedOnly = body.revision.includeUncommitted === false;
68
- } else {
69
- const _ = body;
70
- throw new Error(`Unable to determine forkId for message=${WorldStateMessageType[messageType]}`);
71
- }
72
- // Get the queue or create a new one
73
- let requestQueue = this.queues.get(forkId);
74
- if (requestQueue === undefined) {
75
- requestQueue = new WorldStateOpsQueue();
76
- this.queues.set(forkId, requestQueue);
77
- }
78
- // Enqueue the request and wait for the response
79
- const response = await requestQueue.execute(async ()=>{
80
- assert.notEqual(messageType, WorldStateMessageType.CLOSE, 'Use close() to close the native instance');
81
- assert.equal(this.open, true, 'Native instance is closed');
82
- let response;
83
- try {
84
- response = await this._sendMessage(messageType, body);
85
- } catch (error) {
86
- errorHandler(error.message);
87
- throw error;
88
- }
89
- return responseHandler(response);
90
- }, messageType, committedOnly);
91
- // If the request was to delete the fork then we clean it up here
92
- if (messageType === WorldStateMessageType.DELETE_FORK) {
93
- await requestQueue.stop();
94
- this.queues.delete(forkId);
95
- }
96
- return response;
97
- }
98
- /**
99
- * Stops the native instance.
100
- */ async close() {
101
- if (!this.open) {
102
- return;
103
- }
104
- this.open = false;
105
- const queue = this.queues.get(0);
106
- await queue.execute(async ()=>{
107
- await this._sendMessage(WorldStateMessageType.CLOSE, {
108
- canonical: true
109
- });
110
- }, WorldStateMessageType.CLOSE, false);
111
- await queue.stop();
112
- }
113
- async _sendMessage(messageType, body) {
114
- let logMetadata = {};
115
- if (body) {
116
- if ('treeId' in body) {
117
- logMetadata['treeId'] = MerkleTreeId[body.treeId];
118
- }
119
- if ('revision' in body) {
120
- logMetadata = {
121
- ...logMetadata,
122
- ...body.revision
123
- };
124
- }
125
- if ('forkId' in body) {
126
- logMetadata['forkId'] = body.forkId;
127
- }
128
- if ('blockNumber' in body) {
129
- logMetadata['blockNumber'] = body.blockNumber;
130
- }
131
- if ('toBlockNumber' in body) {
132
- logMetadata['toBlockNumber'] = body.toBlockNumber;
133
- }
134
- if ('leafIndex' in body) {
135
- logMetadata['leafIndex'] = body.leafIndex;
136
- }
137
- if ('blockHeaderHash' in body) {
138
- logMetadata['blockHeaderHash'] = '0x' + body.blockHeaderHash.toString('hex');
139
- }
140
- if ('leaves' in body) {
141
- logMetadata['leavesCount'] = body.leaves.length;
142
- }
143
- // sync operation
144
- if ('paddedNoteHashes' in body) {
145
- logMetadata['notesCount'] = body.paddedNoteHashes.length;
146
- logMetadata['nullifiersCount'] = body.paddedNullifiers.length;
147
- logMetadata['l1ToL2MessagesCount'] = body.paddedL1ToL2Messages.length;
148
- logMetadata['publicDataWritesCount'] = body.publicDataWrites.length;
149
- }
150
- }
151
- try {
152
- const { duration, response } = await this.instance.sendMessage(messageType, body);
153
- this.log.trace(`Call ${WorldStateMessageType[messageType]} took (ms)`, {
154
- duration,
155
- ...logMetadata
156
- });
157
- this.instrumentation.recordRoundTrip(duration.totalUs, messageType);
158
- return response;
159
- } catch (error) {
160
- this.log.error(`Call ${WorldStateMessageType[messageType]} failed: ${error}`, error, logMetadata);
161
- throw error;
162
- }
163
- }
164
- }
2
+ * Backend-agnostic handle to a running aztec-wsdb world state, accessed by the TS layer.
3
+ *
4
+ * Two implementations exist:
5
+ * - {@link IpcWorldState} — talks to a standalone aztec-wsdb process over UDS or shared memory.
6
+ *
7
+ * The legacy in-process NAPI implementation has been removed; the C++ AVM (NAPI) now connects to
8
+ * the same aztec-wsdb process via UDS using the socket path returned by {@link getSocketPath}.
9
+ */ export { };
@@ -16,4 +16,4 @@ export declare class WorldStateOpsQueue {
16
16
  private sendEnqueuedRequest;
17
17
  stop(): Promise<void>;
18
18
  }
19
- //# sourceMappingURL=world_state_ops_queue.d.ts.map
19
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid29ybGRfc3RhdGVfb3BzX3F1ZXVlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbmF0aXZlL3dvcmxkX3N0YXRlX29wc19xdWV1ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUF5QnJELGVBQU8sTUFBTSxrQkFBa0IsNEJBZ0I3QixDQUFDO0FBR0gscUJBQWEsa0JBQWtCO0lBQzdCLE9BQU8sQ0FBQyxRQUFRLENBQXNCO0lBQ3RDLE9BQU8sQ0FBQyxxQkFBcUIsQ0FBSztJQUNsQyxPQUFPLENBQUMsYUFBYSxDQUFLO0lBQzFCLE9BQU8sQ0FBQyxXQUFXLENBQUMsQ0FBZ0I7SUFDcEMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxDQUFhO0lBQ2pDLE9BQU8sQ0FBQyxTQUFTLENBQUs7SUFDdEIsT0FBTyxDQUFDLEdBQUcsQ0FBd0M7SUFJNUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRSxXQUFXLEVBQUUscUJBQXFCLEVBQUUsYUFBYSxFQUFFLE9BQU8sZ0JBc0JyRztJQUdELE9BQU8sQ0FBQyxlQUFlO0lBV3ZCLE9BQU8sQ0FBQyw2QkFBNkI7SUFZckMsT0FBTyxDQUFDLDJCQUEyQjtJQVVuQyxPQUFPLENBQUMsZUFBZTtJQXlDdkIsT0FBTyxDQUFDLG1CQUFtQjtJQWdCcEIsSUFBSSxrQkFnQlY7Q0FDRiJ9
@@ -1 +1 @@
1
- {"version":3,"file":"world_state_ops_queue.d.ts","sourceRoot":"","sources":["../../src/native/world_state_ops_queue.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAyBrD,eAAO,MAAM,kBAAkB,4BAgB7B,CAAC;AAGH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,qBAAqB,CAAK;IAClC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,WAAW,CAAC,CAAgB;IACpC,OAAO,CAAC,WAAW,CAAC,CAAa;IACjC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,GAAG,CAAwC;IAI5C,OAAO,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,qBAAqB,EAAE,aAAa,EAAE,OAAO;IAyBtG,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,6BAA6B;IAYrC,OAAO,CAAC,2BAA2B;IAUnC,OAAO,CAAC,eAAe;IAyCvB,OAAO,CAAC,mBAAmB;IAgBpB,IAAI;CAiBZ"}
1
+ {"version":3,"file":"world_state_ops_queue.d.ts","sourceRoot":"","sources":["../../src/native/world_state_ops_queue.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAyBrD,eAAO,MAAM,kBAAkB,4BAgB7B,CAAC;AAGH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,qBAAqB,CAAK;IAClC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,WAAW,CAAC,CAAgB;IACpC,OAAO,CAAC,WAAW,CAAC,CAAa;IACjC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,GAAG,CAAwC;IAI5C,OAAO,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,qBAAqB,EAAE,aAAa,EAAE,OAAO,gBAsBrG;IAGD,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,6BAA6B;IAYrC,OAAO,CAAC,2BAA2B;IAUnC,OAAO,CAAC,eAAe;IAyCvB,OAAO,CAAC,mBAAmB;IAgBpB,IAAI,kBAgBV;CACF"}