@aztec/world-state 0.57.0 → 0.58.0

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 (57) hide show
  1. package/README.md +1 -1
  2. package/dest/index.d.ts +1 -0
  3. package/dest/index.d.ts.map +1 -1
  4. package/dest/index.js +2 -1
  5. package/dest/native/merkle_trees_facade.d.ts +34 -0
  6. package/dest/native/merkle_trees_facade.d.ts.map +1 -0
  7. package/dest/native/merkle_trees_facade.js +193 -0
  8. package/dest/native/message.d.ts +78 -19
  9. package/dest/native/message.d.ts.map +1 -1
  10. package/dest/native/message.js +27 -26
  11. package/dest/native/native_world_state.d.ts +39 -38
  12. package/dest/native/native_world_state.d.ts.map +1 -1
  13. package/dest/native/native_world_state.js +108 -254
  14. package/dest/native/native_world_state_instance.d.ts +40 -0
  15. package/dest/native/native_world_state_instance.d.ts.map +1 -0
  16. package/dest/native/native_world_state_instance.js +183 -0
  17. package/dest/synchronizer/config.d.ts +2 -2
  18. package/dest/synchronizer/config.d.ts.map +1 -1
  19. package/dest/synchronizer/config.js +6 -7
  20. package/dest/synchronizer/factory.d.ts +3 -0
  21. package/dest/synchronizer/factory.d.ts.map +1 -1
  22. package/dest/synchronizer/factory.js +13 -4
  23. package/dest/synchronizer/server_world_state_synchronizer.d.ts +41 -41
  24. package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
  25. package/dest/synchronizer/server_world_state_synchronizer.js +126 -151
  26. package/dest/test/utils.d.ts +14 -0
  27. package/dest/test/utils.d.ts.map +1 -0
  28. package/dest/test/utils.js +67 -0
  29. package/dest/world-state-db/index.d.ts +1 -1
  30. package/dest/world-state-db/index.d.ts.map +1 -1
  31. package/dest/world-state-db/merkle_tree_db.d.ts +42 -32
  32. package/dest/world-state-db/merkle_tree_db.d.ts.map +1 -1
  33. package/dest/world-state-db/merkle_tree_db.js +1 -1
  34. package/dest/world-state-db/merkle_tree_operations_facade.d.ts +8 -37
  35. package/dest/world-state-db/merkle_tree_operations_facade.d.ts.map +1 -1
  36. package/dest/world-state-db/merkle_tree_operations_facade.js +6 -45
  37. package/dest/world-state-db/merkle_tree_snapshot_operations_facade.d.ts +4 -13
  38. package/dest/world-state-db/merkle_tree_snapshot_operations_facade.d.ts.map +1 -1
  39. package/dest/world-state-db/merkle_tree_snapshot_operations_facade.js +2 -29
  40. package/dest/world-state-db/merkle_trees.d.ts +17 -19
  41. package/dest/world-state-db/merkle_trees.d.ts.map +1 -1
  42. package/dest/world-state-db/merkle_trees.js +39 -36
  43. package/package.json +15 -12
  44. package/src/index.ts +1 -0
  45. package/src/native/merkle_trees_facade.ts +279 -0
  46. package/src/native/message.ts +97 -20
  47. package/src/native/native_world_state.ts +125 -346
  48. package/src/native/native_world_state_instance.ts +262 -0
  49. package/src/synchronizer/config.ts +8 -9
  50. package/src/synchronizer/factory.ts +20 -3
  51. package/src/synchronizer/server_world_state_synchronizer.ts +149 -178
  52. package/src/test/utils.ts +123 -0
  53. package/src/world-state-db/index.ts +1 -1
  54. package/src/world-state-db/merkle_tree_db.ts +55 -49
  55. package/src/world-state-db/merkle_tree_operations_facade.ts +10 -55
  56. package/src/world-state-db/merkle_tree_snapshot_operations_facade.ts +7 -46
  57. package/src/world-state-db/merkle_trees.ts +50 -45
@@ -1,13 +1,19 @@
1
1
  import {
2
- type HandleL2BlockAndMessagesResult,
3
2
  type L1ToL2MessageSource,
4
3
  type L2Block,
5
- L2BlockDownloader,
4
+ type L2BlockId,
6
5
  type L2BlockSource,
7
- type MerkleTreeAdminOperations,
6
+ L2BlockStream,
7
+ type L2BlockStreamEvent,
8
+ type L2BlockStreamEventHandler,
9
+ type L2BlockStreamLocalDataProvider,
10
+ type L2Tips,
11
+ MerkleTreeId,
12
+ type MerkleTreeReadOperations,
13
+ type MerkleTreeWriteOperations,
8
14
  WorldStateRunningState,
9
- type WorldStateStatus,
10
15
  type WorldStateSynchronizer,
16
+ type WorldStateSynchronizerStatus,
11
17
  } from '@aztec/circuit-types';
12
18
  import { type L2BlockHandledStats } from '@aztec/circuit-types/stats';
13
19
  import { MerkleTreeCalculator } from '@aztec/circuits.js';
@@ -15,74 +21,48 @@ import { L1_TO_L2_MSG_SUBTREE_HEIGHT } from '@aztec/circuits.js/constants';
15
21
  import { type Fr } from '@aztec/foundation/fields';
16
22
  import { createDebugLogger } from '@aztec/foundation/log';
17
23
  import { promiseWithResolvers } from '@aztec/foundation/promise';
18
- import { SerialQueue } from '@aztec/foundation/queue';
19
24
  import { elapsed } from '@aztec/foundation/timer';
20
- import { type AztecKVStore, type AztecSingleton } from '@aztec/kv-store';
21
25
  import { SHA256Trunc } from '@aztec/merkle-tree';
22
26
 
23
- import {
24
- MerkleTreeAdminOperationsFacade,
25
- MerkleTreeOperationsFacade,
26
- } from '../world-state-db/merkle_tree_operations_facade.js';
27
- import { MerkleTreeSnapshotOperationsFacade } from '../world-state-db/merkle_tree_snapshot_operations_facade.js';
28
- import { type MerkleTrees } from '../world-state-db/merkle_trees.js';
27
+ import { type WorldStateStatus } from '../native/message.js';
28
+ import { type MerkleTreeAdminDatabase } from '../world-state-db/merkle_tree_db.js';
29
29
  import { type WorldStateConfig } from './config.js';
30
30
 
31
31
  /**
32
- * Synchronizes the world state with the L2 blocks from a L2BlockSource.
33
- * The synchronizer will download the L2 blocks from the L2BlockSource and insert the new note hashes into the merkle
34
- * tree.
32
+ * Synchronizes the world state with the L2 blocks from a L2BlockSource via a block stream.
33
+ * The synchronizer will download the L2 blocks from the L2BlockSource and update the merkle trees.
34
+ * Handles chain reorgs via the L2BlockStream.
35
35
  */
36
- export class ServerWorldStateSynchronizer implements WorldStateSynchronizer {
37
- private latestBlockNumberAtStart = 0;
36
+ export class ServerWorldStateSynchronizer
37
+ implements WorldStateSynchronizer, L2BlockStreamLocalDataProvider, L2BlockStreamEventHandler
38
+ {
39
+ private readonly merkleTreeCommitted: MerkleTreeReadOperations;
38
40
 
39
- // TODO(palla/prover-node): JobQueue, stopping, runningPromise, pausedPromise, pausedResolve
40
- // should all be hidden under a single abstraction. Also, check if we actually need the jobqueue.
41
- private l2BlockDownloader: L2BlockDownloader;
42
- private syncPromise: Promise<void> = Promise.resolve();
43
- private syncResolve?: () => void = undefined;
44
- private jobQueue = new SerialQueue();
45
- private stopping = false;
46
- private runningPromise: Promise<void> = Promise.resolve();
47
- private pausedPromise?: Promise<void> = undefined;
48
- private pausedResolve?: () => void = undefined;
41
+ private latestBlockNumberAtStart = 0;
49
42
  private currentState: WorldStateRunningState = WorldStateRunningState.IDLE;
50
- private blockNumber: AztecSingleton<number>;
43
+
44
+ private syncPromise = promiseWithResolvers<void>();
45
+ protected blockStream: L2BlockStream | undefined;
51
46
 
52
47
  constructor(
53
- store: AztecKVStore,
54
- private merkleTreeDb: MerkleTrees,
55
- private l2BlockSource: L2BlockSource & L1ToL2MessageSource,
56
- private config: WorldStateConfig,
57
- private log = createDebugLogger('aztec:world_state'),
48
+ private readonly merkleTreeDb: MerkleTreeAdminDatabase,
49
+ private readonly l2BlockSource: L2BlockSource & L1ToL2MessageSource,
50
+ private readonly config: WorldStateConfig,
51
+ private readonly log = createDebugLogger('aztec:world_state'),
58
52
  ) {
59
- this.blockNumber = store.openSingleton('world_state_synch_last_block_number');
60
- this.l2BlockDownloader = new L2BlockDownloader(l2BlockSource, {
61
- maxQueueSize: config.l2QueueSize,
62
- pollIntervalMS: config.worldStateBlockCheckIntervalMS,
63
- proven: config.worldStateProvenBlocksOnly,
64
- });
65
- }
66
-
67
- public getLatest(): MerkleTreeAdminOperations {
68
- return new MerkleTreeAdminOperationsFacade(this.merkleTreeDb, true);
53
+ this.merkleTreeCommitted = this.merkleTreeDb.getCommitted();
69
54
  }
70
55
 
71
- public getCommitted(): MerkleTreeAdminOperations {
72
- return new MerkleTreeAdminOperationsFacade(this.merkleTreeDb, false);
56
+ public getCommitted(): MerkleTreeReadOperations {
57
+ return this.merkleTreeDb.getCommitted();
73
58
  }
74
59
 
75
- public getSnapshot(blockNumber: number): MerkleTreeAdminOperations {
76
- return new MerkleTreeSnapshotOperationsFacade(this.merkleTreeDb, blockNumber);
60
+ public getSnapshot(blockNumber: number): MerkleTreeReadOperations {
61
+ return this.merkleTreeDb.getSnapshot(blockNumber);
77
62
  }
78
63
 
79
- public async ephemeralFork(): Promise<MerkleTreeOperationsFacade> {
80
- return new MerkleTreeOperationsFacade(await this.merkleTreeDb.ephemeralFork(), true);
81
- }
82
-
83
- private async getFork(includeUncommitted: boolean): Promise<MerkleTreeAdminOperationsFacade> {
84
- this.log.verbose(`Forking world state at ${this.blockNumber.get()}`);
85
- return new MerkleTreeAdminOperationsFacade(await this.merkleTreeDb.fork(), includeUncommitted);
64
+ public fork(blockNumber?: number): Promise<MerkleTreeWriteOperations> {
65
+ return this.merkleTreeDb.fork(blockNumber);
86
66
  }
87
67
 
88
68
  public async start() {
@@ -93,86 +73,56 @@ export class ServerWorldStateSynchronizer implements WorldStateSynchronizer {
93
73
  return this.syncPromise;
94
74
  }
95
75
 
96
- // get the current latest block number
76
+ // Get the current latest block number
97
77
  this.latestBlockNumberAtStart = await (this.config.worldStateProvenBlocksOnly
98
78
  ? this.l2BlockSource.getProvenBlockNumber()
99
79
  : this.l2BlockSource.getBlockNumber());
100
80
 
101
- const blockToDownloadFrom = this.currentL2BlockNum + 1;
81
+ const blockToDownloadFrom = (await this.getLatestBlockNumber()) + 1;
102
82
 
103
- // if there are blocks to be retrieved, go to a synching state
104
83
  if (blockToDownloadFrom <= this.latestBlockNumberAtStart) {
84
+ // If there are blocks to be retrieved, go to a synching state
105
85
  this.setCurrentState(WorldStateRunningState.SYNCHING);
106
- this.syncPromise = new Promise(resolve => {
107
- this.syncResolve = resolve;
108
- });
109
- this.log.info(`Starting sync from ${blockToDownloadFrom}, latest block ${this.latestBlockNumberAtStart}`);
86
+ this.log.verbose(`Starting sync from ${blockToDownloadFrom} to latest block ${this.latestBlockNumberAtStart}`);
110
87
  } else {
111
- // if no blocks to be retrieved, go straight to running
88
+ // If no blocks to be retrieved, go straight to running
112
89
  this.setCurrentState(WorldStateRunningState.RUNNING);
113
- this.syncPromise = Promise.resolve();
114
- this.log.debug(
115
- `Next block ${blockToDownloadFrom} already beyond latest block at ${this.latestBlockNumberAtStart}`,
116
- );
90
+ this.syncPromise.resolve();
91
+ this.log.debug(`Next block ${blockToDownloadFrom} already beyond latest block ${this.latestBlockNumberAtStart}`);
117
92
  }
118
93
 
119
- // start looking for further blocks
120
- const blockProcess = async () => {
121
- while (!this.stopping) {
122
- await this.jobQueue.put(() => this.collectAndProcessBlocks());
123
- if (this.pausedPromise) {
124
- await this.pausedPromise;
125
- }
126
- }
127
- };
128
- this.jobQueue.start();
129
- this.runningPromise = blockProcess();
130
- this.l2BlockDownloader.start(blockToDownloadFrom);
131
- this.log.info(`Started block downloader from block ${blockToDownloadFrom}`);
132
- return this.syncPromise;
94
+ this.blockStream = this.createBlockStream();
95
+ this.blockStream.start();
96
+ this.log.info(`Started world state synchronizer from block ${blockToDownloadFrom}`);
97
+ return this.syncPromise.promise;
133
98
  }
134
99
 
135
- public async stop() {
136
- this.log.debug('Stopping world state...');
137
- this.stopping = true;
138
- await this.l2BlockDownloader.stop();
139
- this.log.debug('Cancelling job queue...');
140
- await this.jobQueue.cancel();
141
- this.log.debug('Stopping Merkle trees');
142
- await this.merkleTreeDb.stop();
143
- this.log.debug('Awaiting promise');
144
- await this.runningPromise;
145
- this.setCurrentState(WorldStateRunningState.STOPPED);
146
- this.log.info(`Stopped`);
147
- }
148
-
149
- private get currentL2BlockNum(): number {
150
- return this.blockNumber.get() ?? 0;
100
+ protected createBlockStream() {
101
+ return new L2BlockStream(this.l2BlockSource, this, this, {
102
+ proven: this.config.worldStateProvenBlocksOnly,
103
+ pollIntervalMS: this.config.worldStateBlockCheckIntervalMS,
104
+ batchSize: this.config.worldStateBlockRequestBatchSize,
105
+ });
151
106
  }
152
107
 
153
- private async pause() {
154
- this.log.debug('Pausing world state synchronizer');
155
- ({ promise: this.pausedPromise, resolve: this.pausedResolve } = promiseWithResolvers());
156
- await this.jobQueue.syncPoint();
157
- this.log.debug('Paused world state synchronizer');
108
+ public async stop() {
109
+ this.log.debug('Stopping block stream...');
110
+ await this.blockStream?.stop();
111
+ this.log.debug('Stopping merkle trees...');
112
+ await this.merkleTreeDb.close();
113
+ this.setCurrentState(WorldStateRunningState.STOPPED);
114
+ this.log.info(`Stopped world state synchronizer`);
158
115
  }
159
116
 
160
- private resume() {
161
- if (this.pausedResolve) {
162
- this.log.debug('Resuming world state synchronizer');
163
- this.pausedResolve();
164
- this.pausedResolve = undefined;
165
- this.pausedPromise = undefined;
166
- this.log.debug('Resumed world state synchronizer');
167
- }
117
+ public async status(): Promise<WorldStateSynchronizerStatus> {
118
+ return {
119
+ syncedToL2Block: (await this.getL2Tips()).latest,
120
+ state: this.currentState,
121
+ };
168
122
  }
169
123
 
170
- public status(): Promise<WorldStateStatus> {
171
- const status = {
172
- syncedToL2Block: this.currentL2BlockNum,
173
- state: this.currentState,
174
- } as WorldStateStatus;
175
- return Promise.resolve(status);
124
+ public async getLatestBlockNumber() {
125
+ return (await this.getL2Tips()).latest.number;
176
126
  }
177
127
 
178
128
  /**
@@ -181,78 +131,89 @@ export class ServerWorldStateSynchronizer implements WorldStateSynchronizer {
181
131
  * @returns A promise that resolves with the block number the world state was synced to
182
132
  */
183
133
  public async syncImmediate(targetBlockNumber?: number): Promise<number> {
184
- if (this.currentState !== WorldStateRunningState.RUNNING) {
185
- throw new Error(`World State is not running, unable to perform sync`);
134
+ if (this.currentState !== WorldStateRunningState.RUNNING || this.blockStream === undefined) {
135
+ throw new Error(`World State is not running. Unable to perform sync.`);
186
136
  }
187
- // If we have been given a block number to sync to and we have reached that number then return.
188
- if (targetBlockNumber !== undefined && targetBlockNumber <= this.currentL2BlockNum) {
189
- return this.currentL2BlockNum;
137
+
138
+ // If we have been given a block number to sync to and we have reached that number then return
139
+ const currentBlockNumber = await this.getLatestBlockNumber();
140
+ if (targetBlockNumber !== undefined && targetBlockNumber <= currentBlockNumber) {
141
+ return currentBlockNumber;
190
142
  }
191
- this.log.debug(`World State at ${this.currentL2BlockNum} told to sync to ${targetBlockNumber ?? 'latest'}`);
192
- // ensure any outstanding block updates are completed first
193
- await this.jobQueue.syncPoint();
194
-
195
- while (true) {
196
- // Check the block number again
197
- if (targetBlockNumber !== undefined && targetBlockNumber <= this.currentL2BlockNum) {
198
- return this.currentL2BlockNum;
199
- }
200
- // Poll for more blocks, requesting even unproven blocks.
201
- const numBlocks = await this.l2BlockDownloader.pollImmediate(targetBlockNumber, false);
202
- this.log.debug(`Block download immediate poll yielded ${numBlocks} blocks`);
203
- if (numBlocks) {
204
- // More blocks were received, process them and go round again
205
- await this.jobQueue.put(() => this.collectAndProcessBlocks());
206
- continue;
207
- }
208
- // No blocks are available, if we have been given a block number then we can't achieve it
209
- if (targetBlockNumber !== undefined) {
210
- throw new Error(
211
- `Unable to sync to block number ${targetBlockNumber}, currently synced to block ${this.currentL2BlockNum}`,
212
- );
213
- }
214
- return this.currentL2BlockNum;
143
+ this.log.debug(`World State at ${currentBlockNumber} told to sync to ${targetBlockNumber ?? 'latest'}`);
144
+
145
+ // Force the block stream to sync against the archiver now
146
+ await this.blockStream.sync();
147
+
148
+ // If we have been given a block number to sync to and we have not reached that number then fail
149
+ const updatedBlockNumber = await this.getLatestBlockNumber();
150
+ if (targetBlockNumber !== undefined && targetBlockNumber > updatedBlockNumber) {
151
+ throw new Error(`Unable to sync to block number ${targetBlockNumber} (last synced is ${updatedBlockNumber})`);
215
152
  }
153
+
154
+ return updatedBlockNumber;
216
155
  }
217
156
 
218
- public async syncImmediateAndFork(
219
- targetBlockNumber: number,
220
- forkIncludeUncommitted: boolean,
221
- ): Promise<MerkleTreeAdminOperationsFacade> {
222
- try {
223
- await this.pause();
224
- await this.syncImmediate(targetBlockNumber);
225
- return await this.getFork(forkIncludeUncommitted);
226
- } finally {
227
- this.resume();
228
- }
157
+ /** Returns the L2 block hash for a given number. Used by the L2BlockStream for detecting reorgs. */
158
+ public getL2BlockHash(number: number): Promise<string | undefined> {
159
+ return number === 0
160
+ ? Promise.resolve(this.merkleTreeCommitted.getInitialHeader().hash().toString())
161
+ : this.merkleTreeCommitted.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(number)).then(leaf => leaf?.toString());
229
162
  }
230
163
 
231
- /**
232
- * Checks for the availability of new blocks and processes them.
233
- */
234
- private async collectAndProcessBlocks() {
235
- // This request for blocks will timeout after 1 second if no blocks are received
236
- const blocks = await this.l2BlockDownloader.getBlocks(1);
237
- const messagePromises = blocks.map(block => this.l2BlockSource.getL1ToL2Messages(BigInt(block.number)));
238
- const l1ToL2Messages: Fr[][] = await Promise.all(messagePromises);
164
+ /** Returns the latest L2 block number for each tip of the chain (latest, proven, finalized). */
165
+ public async getL2Tips(): Promise<L2Tips> {
166
+ const status = await this.merkleTreeDb.getStatus();
167
+ const unfinalisedBlockHash = await this.getL2BlockHash(Number(status.unfinalisedBlockNumber));
168
+ const latestBlockId: L2BlockId = { number: Number(status.unfinalisedBlockNumber), hash: unfinalisedBlockHash! };
239
169
 
240
- await this.handleL2BlocksAndMessages(blocks, l1ToL2Messages);
170
+ return {
171
+ latest: latestBlockId,
172
+ finalized: { number: Number(status.finalisedBlockNumber), hash: '' },
173
+ proven: { number: Number(status.finalisedBlockNumber), hash: '' }, // TODO(palla/reorg): Using finalised as proven for now
174
+ };
175
+ }
176
+
177
+ /** Handles an event emitted by the block stream. */
178
+ public async handleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void> {
179
+ try {
180
+ switch (event.type) {
181
+ case 'blocks-added':
182
+ await this.handleL2Blocks(event.blocks);
183
+ break;
184
+ case 'chain-pruned':
185
+ await this.handleChainPruned(event.blockNumber);
186
+ break;
187
+ case 'chain-proven':
188
+ await this.handleChainProven(event.blockNumber);
189
+ break;
190
+ case 'chain-finalized':
191
+ await this.handleChainFinalized(event.blockNumber);
192
+ break;
193
+ }
194
+ } catch (err) {
195
+ this.log.error('Error processing block stream', err);
196
+ }
241
197
  }
242
198
 
243
199
  /**
244
200
  * Handles a list of L2 blocks (i.e. Inserts the new note hashes into the merkle tree).
245
201
  * @param l2Blocks - The L2 blocks to handle.
246
- * @param l1ToL2Messages - The L1 to L2 messages for each block.
247
202
  * @returns Whether the block handled was produced by this same node.
248
203
  */
249
- private async handleL2BlocksAndMessages(l2Blocks: L2Block[], l1ToL2Messages: Fr[][]) {
204
+ private async handleL2Blocks(l2Blocks: L2Block[]) {
205
+ this.log.verbose(`Handling new L2 blocks from ${l2Blocks[0].number} to ${l2Blocks[l2Blocks.length - 1].number}`);
206
+ const messagePromises = l2Blocks.map(block => this.l2BlockSource.getL1ToL2Messages(BigInt(block.number)));
207
+ const l1ToL2Messages: Fr[][] = await Promise.all(messagePromises);
208
+
250
209
  for (let i = 0; i < l2Blocks.length; i++) {
251
- const [duration, result] = await elapsed(() => this.handleL2BlockAndMessages(l2Blocks[i], l1ToL2Messages[i]));
210
+ const [duration, result] = await elapsed(() => this.handleL2Block(l2Blocks[i], l1ToL2Messages[i]));
252
211
  this.log.verbose(`Handled new L2 block`, {
253
212
  eventName: 'l2-block-handled',
254
213
  duration,
255
- isBlockOurs: result.isBlockOurs,
214
+ unfinalisedBlockNumber: result.unfinalisedBlockNumber,
215
+ finalisedBlockNumber: result.finalisedBlockNumber,
216
+ oldestHistoricBlock: result.oldestHistoricalBlock,
256
217
  ...l2Blocks[i].getStats(),
257
218
  } satisfies L2BlockHandledStats);
258
219
  }
@@ -264,29 +225,39 @@ export class ServerWorldStateSynchronizer implements WorldStateSynchronizer {
264
225
  * @param l1ToL2Messages - The L1 to L2 messages for the block.
265
226
  * @returns Whether the block handled was produced by this same node.
266
227
  */
267
- private async handleL2BlockAndMessages(
268
- l2Block: L2Block,
269
- l1ToL2Messages: Fr[],
270
- ): Promise<HandleL2BlockAndMessagesResult> {
228
+ private async handleL2Block(l2Block: L2Block, l1ToL2Messages: Fr[]): Promise<WorldStateStatus> {
271
229
  // First we check that the L1 to L2 messages hash to the block inHash.
272
230
  // Note that we cannot optimize this check by checking the root of the subtree after inserting the messages
273
231
  // to the real L1_TO_L2_MESSAGE_TREE (like we do in merkleTreeDb.handleL2BlockAndMessages(...)) because that
274
232
  // tree uses pedersen and we don't have access to the converted root.
275
- this.#verifyMessagesHashToInHash(l1ToL2Messages, l2Block.header.contentCommitment.inHash);
233
+ this.verifyMessagesHashToInHash(l1ToL2Messages, l2Block.header.contentCommitment.inHash);
276
234
 
277
235
  // If the above check succeeds, we can proceed to handle the block.
278
236
  const result = await this.merkleTreeDb.handleL2BlockAndMessages(l2Block, l1ToL2Messages);
279
- await this.blockNumber.set(l2Block.number);
280
237
 
281
238
  if (this.currentState === WorldStateRunningState.SYNCHING && l2Block.number >= this.latestBlockNumberAtStart) {
282
239
  this.setCurrentState(WorldStateRunningState.RUNNING);
283
- if (this.syncResolve !== undefined) {
284
- this.syncResolve();
285
- }
240
+ this.syncPromise.resolve();
286
241
  }
242
+
287
243
  return result;
288
244
  }
289
245
 
246
+ private async handleChainFinalized(blockNumber: number) {
247
+ this.log.verbose(`Chain finalized at block ${blockNumber}`);
248
+ await this.merkleTreeDb.setFinalised(BigInt(blockNumber));
249
+ }
250
+
251
+ private handleChainProven(blockNumber: number) {
252
+ this.log.verbose(`Chain proven at block ${blockNumber}`);
253
+ return Promise.resolve();
254
+ }
255
+
256
+ private async handleChainPruned(blockNumber: number) {
257
+ this.log.info(`Chain pruned to block ${blockNumber}`);
258
+ await this.merkleTreeDb.unwindBlocks(BigInt(blockNumber));
259
+ }
260
+
290
261
  /**
291
262
  * Method to set the value of the current state.
292
263
  * @param newState - New state value.
@@ -302,7 +273,7 @@ export class ServerWorldStateSynchronizer implements WorldStateSynchronizer {
302
273
  * @param inHash - The inHash of the block.
303
274
  * @throws If the L1 to L2 messages do not hash to the block inHash.
304
275
  */
305
- #verifyMessagesHashToInHash(l1ToL2Messages: Fr[], inHash: Buffer) {
276
+ protected verifyMessagesHashToInHash(l1ToL2Messages: Fr[], inHash: Buffer) {
306
277
  const treeCalculator = new MerkleTreeCalculator(
307
278
  L1_TO_L2_MSG_SUBTREE_HEIGHT,
308
279
  Buffer.alloc(32),
@@ -0,0 +1,123 @@
1
+ import {
2
+ L2Block,
3
+ MerkleTreeId,
4
+ type MerkleTreeReadOperations,
5
+ type MerkleTreeWriteOperations,
6
+ PublicDataWrite,
7
+ TxEffect,
8
+ } from '@aztec/circuit-types';
9
+ import {
10
+ AppendOnlyTreeSnapshot,
11
+ Fr,
12
+ MAX_NOTE_HASHES_PER_TX,
13
+ MAX_NULLIFIERS_PER_TX,
14
+ MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
15
+ NULLIFIER_SUBTREE_HEIGHT,
16
+ NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
17
+ PUBLIC_DATA_SUBTREE_HEIGHT,
18
+ PublicDataTreeLeaf,
19
+ } from '@aztec/circuits.js';
20
+ import { padArrayEnd } from '@aztec/foundation/collection';
21
+
22
+ import { type NativeWorldStateService } from '../native/native_world_state.js';
23
+
24
+ export async function mockBlock(blockNum: number, size: number, fork: MerkleTreeWriteOperations) {
25
+ const l2Block = L2Block.random(blockNum, size);
26
+ const l1ToL2Messages = Array(16).fill(0).map(Fr.random);
27
+
28
+ const paddedTxEffects = padArrayEnd(
29
+ l2Block.body.txEffects,
30
+ TxEffect.empty(),
31
+ l2Block.body.numberOfTxsIncludingPadded,
32
+ );
33
+
34
+ // Sync the append only trees
35
+ {
36
+ const noteHashesPadded = paddedTxEffects.flatMap(txEffect =>
37
+ padArrayEnd(txEffect.noteHashes, Fr.ZERO, MAX_NOTE_HASHES_PER_TX),
38
+ );
39
+ await fork.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, noteHashesPadded);
40
+
41
+ const l1ToL2MessagesPadded = padArrayEnd(l1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP);
42
+ await fork.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2MessagesPadded);
43
+ }
44
+
45
+ // Sync the indexed trees
46
+ {
47
+ // We insert the public data tree leaves with one batch per tx to avoid updating the same key twice
48
+ for (const txEffect of paddedTxEffects) {
49
+ const publicDataWrites = padArrayEnd(
50
+ txEffect.publicDataWrites,
51
+ PublicDataWrite.empty(),
52
+ MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
53
+ );
54
+
55
+ await fork.batchInsert(
56
+ MerkleTreeId.PUBLIC_DATA_TREE,
57
+ publicDataWrites.map(write => new PublicDataTreeLeaf(write.leafIndex, write.newValue).toBuffer()),
58
+ PUBLIC_DATA_SUBTREE_HEIGHT,
59
+ );
60
+
61
+ const nullifiersPadded = padArrayEnd(txEffect.nullifiers, Fr.ZERO, MAX_NULLIFIERS_PER_TX);
62
+
63
+ await fork.batchInsert(
64
+ MerkleTreeId.NULLIFIER_TREE,
65
+ nullifiersPadded.map(nullifier => nullifier.toBuffer()),
66
+ NULLIFIER_SUBTREE_HEIGHT,
67
+ );
68
+ }
69
+ }
70
+
71
+ const state = await fork.getStateReference();
72
+ l2Block.header.state = state;
73
+ await fork.updateArchive(l2Block.header);
74
+
75
+ const archiveState = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
76
+
77
+ l2Block.archive = new AppendOnlyTreeSnapshot(Fr.fromBuffer(archiveState.root), Number(archiveState.size));
78
+
79
+ return {
80
+ block: l2Block,
81
+ messages: l1ToL2Messages,
82
+ };
83
+ }
84
+
85
+ export async function mockBlocks(from: number, count: number, numTxs: number, worldState: NativeWorldStateService) {
86
+ const tempFork = await worldState.fork(from - 1);
87
+
88
+ const blocks = [];
89
+ const messagesArray = [];
90
+ for (let blockNumber = from; blockNumber < from + count; blockNumber++) {
91
+ const { block, messages } = await mockBlock(blockNumber, numTxs, tempFork);
92
+ blocks.push(block);
93
+ messagesArray.push(messages);
94
+ }
95
+
96
+ await tempFork.close();
97
+
98
+ return { blocks, messages: messagesArray };
99
+ }
100
+
101
+ export async function assertSameState(forkA: MerkleTreeReadOperations, forkB: MerkleTreeReadOperations) {
102
+ const nativeStateRef = await forkA.getStateReference();
103
+ const nativeArchive = await forkA.getTreeInfo(MerkleTreeId.ARCHIVE);
104
+ const legacyStateRef = await forkB.getStateReference();
105
+ const legacyArchive = await forkB.getTreeInfo(MerkleTreeId.ARCHIVE);
106
+
107
+ expect(nativeStateRef).toEqual(legacyStateRef);
108
+ expect(nativeArchive).toEqual(legacyArchive);
109
+ }
110
+
111
+ export async function compareChains(left: MerkleTreeReadOperations, right: MerkleTreeReadOperations) {
112
+ for (const treeId of [
113
+ MerkleTreeId.ARCHIVE,
114
+ MerkleTreeId.L1_TO_L2_MESSAGE_TREE,
115
+ MerkleTreeId.NOTE_HASH_TREE,
116
+ MerkleTreeId.NULLIFIER_TREE,
117
+ MerkleTreeId.PUBLIC_DATA_TREE,
118
+ ]) {
119
+ expect(await left.getTreeInfo(treeId)).toEqual(await right.getTreeInfo(treeId));
120
+
121
+ expect(await left.getSiblingPath(treeId, 0n)).toEqual(await right.getSiblingPath(treeId, 0n));
122
+ }
123
+ }
@@ -3,4 +3,4 @@ export * from './merkle_tree_db.js';
3
3
  export * from './merkle_tree_operations_facade.js';
4
4
  export * from './merkle_tree_snapshot_operations_facade.js';
5
5
 
6
- export { MerkleTreeOperations, MerkleTreeAdminOperations } from '@aztec/circuit-types/interfaces';
6
+ export { MerkleTreeReadOperations } from '@aztec/circuit-types/interfaces';