@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.
- package/README.md +1 -1
- package/dest/index.d.ts +1 -0
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +2 -1
- package/dest/native/merkle_trees_facade.d.ts +34 -0
- package/dest/native/merkle_trees_facade.d.ts.map +1 -0
- package/dest/native/merkle_trees_facade.js +193 -0
- package/dest/native/message.d.ts +78 -19
- package/dest/native/message.d.ts.map +1 -1
- package/dest/native/message.js +27 -26
- package/dest/native/native_world_state.d.ts +39 -38
- package/dest/native/native_world_state.d.ts.map +1 -1
- package/dest/native/native_world_state.js +108 -254
- package/dest/native/native_world_state_instance.d.ts +40 -0
- package/dest/native/native_world_state_instance.d.ts.map +1 -0
- package/dest/native/native_world_state_instance.js +183 -0
- package/dest/synchronizer/config.d.ts +2 -2
- package/dest/synchronizer/config.d.ts.map +1 -1
- package/dest/synchronizer/config.js +6 -7
- package/dest/synchronizer/factory.d.ts +3 -0
- package/dest/synchronizer/factory.d.ts.map +1 -1
- package/dest/synchronizer/factory.js +13 -4
- package/dest/synchronizer/server_world_state_synchronizer.d.ts +41 -41
- package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
- package/dest/synchronizer/server_world_state_synchronizer.js +126 -151
- package/dest/test/utils.d.ts +14 -0
- package/dest/test/utils.d.ts.map +1 -0
- package/dest/test/utils.js +67 -0
- package/dest/world-state-db/index.d.ts +1 -1
- package/dest/world-state-db/index.d.ts.map +1 -1
- package/dest/world-state-db/merkle_tree_db.d.ts +42 -32
- package/dest/world-state-db/merkle_tree_db.d.ts.map +1 -1
- package/dest/world-state-db/merkle_tree_db.js +1 -1
- package/dest/world-state-db/merkle_tree_operations_facade.d.ts +8 -37
- package/dest/world-state-db/merkle_tree_operations_facade.d.ts.map +1 -1
- package/dest/world-state-db/merkle_tree_operations_facade.js +6 -45
- package/dest/world-state-db/merkle_tree_snapshot_operations_facade.d.ts +4 -13
- package/dest/world-state-db/merkle_tree_snapshot_operations_facade.d.ts.map +1 -1
- package/dest/world-state-db/merkle_tree_snapshot_operations_facade.js +2 -29
- package/dest/world-state-db/merkle_trees.d.ts +17 -19
- package/dest/world-state-db/merkle_trees.d.ts.map +1 -1
- package/dest/world-state-db/merkle_trees.js +39 -36
- package/package.json +15 -12
- package/src/index.ts +1 -0
- package/src/native/merkle_trees_facade.ts +279 -0
- package/src/native/message.ts +97 -20
- package/src/native/native_world_state.ts +125 -346
- package/src/native/native_world_state_instance.ts +262 -0
- package/src/synchronizer/config.ts +8 -9
- package/src/synchronizer/factory.ts +20 -3
- package/src/synchronizer/server_world_state_synchronizer.ts +149 -178
- package/src/test/utils.ts +123 -0
- package/src/world-state-db/index.ts +1 -1
- package/src/world-state-db/merkle_tree_db.ts +55 -49
- package/src/world-state-db/merkle_tree_operations_facade.ts +10 -55
- package/src/world-state-db/merkle_tree_snapshot_operations_facade.ts +7 -46
- 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
|
-
|
|
4
|
+
type L2BlockId,
|
|
6
5
|
type L2BlockSource,
|
|
7
|
-
|
|
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
|
-
|
|
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
|
|
34
|
-
*
|
|
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
|
|
37
|
-
|
|
36
|
+
export class ServerWorldStateSynchronizer
|
|
37
|
+
implements WorldStateSynchronizer, L2BlockStreamLocalDataProvider, L2BlockStreamEventHandler
|
|
38
|
+
{
|
|
39
|
+
private readonly merkleTreeCommitted: MerkleTreeReadOperations;
|
|
38
40
|
|
|
39
|
-
|
|
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
|
-
|
|
43
|
+
|
|
44
|
+
private syncPromise = promiseWithResolvers<void>();
|
|
45
|
+
protected blockStream: L2BlockStream | undefined;
|
|
51
46
|
|
|
52
47
|
constructor(
|
|
53
|
-
|
|
54
|
-
private
|
|
55
|
-
private
|
|
56
|
-
private
|
|
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.
|
|
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():
|
|
72
|
-
return
|
|
56
|
+
public getCommitted(): MerkleTreeReadOperations {
|
|
57
|
+
return this.merkleTreeDb.getCommitted();
|
|
73
58
|
}
|
|
74
59
|
|
|
75
|
-
public getSnapshot(blockNumber: number):
|
|
76
|
-
return
|
|
60
|
+
public getSnapshot(blockNumber: number): MerkleTreeReadOperations {
|
|
61
|
+
return this.merkleTreeDb.getSnapshot(blockNumber);
|
|
77
62
|
}
|
|
78
63
|
|
|
79
|
-
public
|
|
80
|
-
return
|
|
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
|
-
//
|
|
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.
|
|
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.
|
|
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
|
-
//
|
|
88
|
+
// If no blocks to be retrieved, go straight to running
|
|
112
89
|
this.setCurrentState(WorldStateRunningState.RUNNING);
|
|
113
|
-
this.syncPromise
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
136
|
-
this.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
154
|
-
this.log.debug('
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
this.
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
this.
|
|
163
|
-
this.
|
|
164
|
-
|
|
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
|
|
171
|
-
|
|
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
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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 ${
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
6
|
+
export { MerkleTreeReadOperations } from '@aztec/circuit-types/interfaces';
|