@aztec/world-state 0.0.1-commit.d3ec352c → 0.0.1-commit.d58ff9d0
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/dest/index.d.ts +2 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/instrumentation/instrumentation.d.ts +4 -3
- package/dest/instrumentation/instrumentation.d.ts.map +1 -1
- package/dest/instrumentation/instrumentation.js +21 -50
- package/dest/native/fork_checkpoint.d.ts +7 -1
- package/dest/native/fork_checkpoint.d.ts.map +1 -1
- package/dest/native/fork_checkpoint.js +15 -3
- package/dest/native/index.d.ts +2 -1
- package/dest/native/index.d.ts.map +1 -1
- package/dest/native/index.js +1 -0
- package/dest/native/ipc_world_state_instance.d.ts +89 -0
- package/dest/native/ipc_world_state_instance.d.ts.map +1 -0
- package/dest/native/ipc_world_state_instance.js +661 -0
- package/dest/native/merkle_trees_facade.d.ts +18 -9
- package/dest/native/merkle_trees_facade.d.ts.map +1 -1
- package/dest/native/merkle_trees_facade.js +90 -113
- package/dest/native/message.d.ts +15 -222
- package/dest/native/message.d.ts.map +1 -1
- package/dest/native/message.js +1 -43
- package/dest/native/native_world_state.d.ts +26 -12
- package/dest/native/native_world_state.d.ts.map +1 -1
- package/dest/native/native_world_state.js +126 -62
- package/dest/native/native_world_state_instance.d.ts +56 -42
- package/dest/native/native_world_state_instance.d.ts.map +1 -1
- package/dest/native/native_world_state_instance.js +1 -203
- package/dest/native/world_state_operation.d.ts +7 -0
- package/dest/native/world_state_operation.d.ts.map +1 -0
- package/dest/native/world_state_operation.js +5 -0
- package/dest/synchronizer/config.d.ts +3 -5
- package/dest/synchronizer/config.d.ts.map +1 -1
- package/dest/synchronizer/config.js +15 -18
- package/dest/synchronizer/errors.d.ts +8 -1
- package/dest/synchronizer/errors.d.ts.map +1 -1
- package/dest/synchronizer/errors.js +8 -1
- package/dest/synchronizer/factory.d.ts +6 -5
- package/dest/synchronizer/factory.d.ts.map +1 -1
- package/dest/synchronizer/factory.js +7 -6
- package/dest/synchronizer/index.d.ts +2 -1
- package/dest/synchronizer/index.d.ts.map +1 -1
- package/dest/synchronizer/index.js +1 -0
- package/dest/synchronizer/server_world_state_synchronizer.d.ts +16 -10
- package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
- package/dest/synchronizer/server_world_state_synchronizer.js +170 -57
- package/dest/test/utils.d.ts +11 -17
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +49 -81
- package/dest/testing.d.ts +5 -4
- package/dest/testing.d.ts.map +1 -1
- package/dest/testing.js +15 -8
- package/dest/world-state-db/merkle_tree_db.d.ts +7 -18
- package/dest/world-state-db/merkle_tree_db.d.ts.map +1 -1
- package/package.json +14 -12
- package/src/index.ts +1 -0
- package/src/instrumentation/instrumentation.ts +23 -59
- package/src/native/fork_checkpoint.ts +19 -3
- package/src/native/index.ts +1 -0
- package/src/native/ipc_world_state_instance.ts +834 -0
- package/src/native/merkle_trees_facade.ts +119 -106
- package/src/native/message.ts +14 -287
- package/src/native/native_world_state.ts +159 -99
- package/src/native/native_world_state_instance.ts +94 -279
- package/src/native/world_state_operation.ts +33 -0
- package/src/synchronizer/config.ts +16 -23
- package/src/synchronizer/errors.ts +8 -0
- package/src/synchronizer/factory.ts +18 -11
- package/src/synchronizer/index.ts +1 -0
- package/src/synchronizer/server_world_state_synchronizer.ts +203 -78
- package/src/test/utils.ts +79 -128
- package/src/testing.ts +13 -11
- package/src/world-state-db/merkle_tree_db.ts +10 -22
- package/dest/native/world_state_ops_queue.d.ts +0 -19
- package/dest/native/world_state_ops_queue.d.ts.map +0 -1
- package/dest/native/world_state_ops_queue.js +0 -146
- package/dest/synchronizer/utils.d.ts +0 -11
- package/dest/synchronizer/utils.d.ts.map +0 -1
- package/dest/synchronizer/utils.js +0 -59
- package/src/native/world_state_ops_queue.ts +0 -190
- package/src/synchronizer/utils.ts +0 -83
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
|
-
import type { Fr } from '@aztec/foundation/
|
|
1
|
+
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
2
|
+
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
3
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
4
4
|
import { promiseWithResolvers } from '@aztec/foundation/promise';
|
|
5
5
|
import { elapsed } from '@aztec/foundation/timer';
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
L2BlockStreamEvent,
|
|
12
|
-
L2BlockStreamEventHandler,
|
|
13
|
-
L2BlockStreamLocalDataProvider,
|
|
14
|
-
|
|
6
|
+
import {
|
|
7
|
+
type BlockHash,
|
|
8
|
+
EventDrivenL2BlockStream,
|
|
9
|
+
type L2Block,
|
|
10
|
+
type L2BlockSource,
|
|
11
|
+
type L2BlockStreamEvent,
|
|
12
|
+
type L2BlockStreamEventHandler,
|
|
13
|
+
type L2BlockStreamLocalDataProvider,
|
|
14
|
+
type LocalChainTips,
|
|
15
15
|
} from '@aztec/stdlib/block';
|
|
16
16
|
import {
|
|
17
17
|
WorldStateRunningState,
|
|
@@ -23,14 +23,13 @@ import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
|
23
23
|
import type { SnapshotDataKeys } from '@aztec/stdlib/snapshots';
|
|
24
24
|
import type { L2BlockHandledStats } from '@aztec/stdlib/stats';
|
|
25
25
|
import { MerkleTreeId, type MerkleTreeReadOperations, type MerkleTreeWriteOperations } from '@aztec/stdlib/trees';
|
|
26
|
-
import {
|
|
26
|
+
import { getTelemetryClient } from '@aztec/telemetry-client';
|
|
27
27
|
|
|
28
28
|
import { WorldStateInstrumentation } from '../instrumentation/instrumentation.js';
|
|
29
29
|
import type { WorldStateStatusFull } from '../native/message.js';
|
|
30
30
|
import type { MerkleTreeAdminDatabase } from '../world-state-db/merkle_tree_db.js';
|
|
31
31
|
import type { WorldStateConfig } from './config.js';
|
|
32
32
|
import { WorldStateSynchronizerError } from './errors.js';
|
|
33
|
-
import { findFirstBlocksInCheckpoints } from './utils.js';
|
|
34
33
|
|
|
35
34
|
export type { SnapshotDataKeys };
|
|
36
35
|
|
|
@@ -47,10 +46,9 @@ export class ServerWorldStateSynchronizer
|
|
|
47
46
|
private latestBlockNumberAtStart = BlockNumber.ZERO;
|
|
48
47
|
private historyToKeep: number | undefined;
|
|
49
48
|
private currentState: WorldStateRunningState = WorldStateRunningState.IDLE;
|
|
50
|
-
private latestBlockHashQuery: { blockNumber: BlockNumber; hash: string | undefined } | undefined = undefined;
|
|
51
49
|
|
|
52
50
|
private syncPromise = promiseWithResolvers<void>();
|
|
53
|
-
protected blockStream:
|
|
51
|
+
protected blockStream: EventDrivenL2BlockStream | undefined;
|
|
54
52
|
|
|
55
53
|
// WorldState doesn't track the proven block number, it only tracks the latest tips of the pending chain and the finalized chain
|
|
56
54
|
// store the proven block number here, in the synchronizer, so that we don't end up spamming the logs with 'chain-proved' events
|
|
@@ -64,7 +62,7 @@ export class ServerWorldStateSynchronizer
|
|
|
64
62
|
private readonly log: Logger = createLogger('world_state'),
|
|
65
63
|
) {
|
|
66
64
|
this.merkleTreeCommitted = this.merkleTreeDb.getCommitted();
|
|
67
|
-
this.historyToKeep = config.
|
|
65
|
+
this.historyToKeep = config.worldStateCheckpointHistory < 1 ? undefined : config.worldStateCheckpointHistory;
|
|
68
66
|
this.log.info(
|
|
69
67
|
`Created world state synchroniser with block history of ${
|
|
70
68
|
this.historyToKeep === undefined ? 'infinity' : this.historyToKeep
|
|
@@ -80,8 +78,48 @@ export class ServerWorldStateSynchronizer
|
|
|
80
78
|
return this.merkleTreeDb.getSnapshot(blockNumber);
|
|
81
79
|
}
|
|
82
80
|
|
|
83
|
-
public
|
|
84
|
-
|
|
81
|
+
public async getVerifiedSnapshot(blockNumber: BlockNumber, blockHash: BlockHash): Promise<MerkleTreeReadOperations> {
|
|
82
|
+
const snapshot = this.merkleTreeDb.getSnapshot(blockNumber);
|
|
83
|
+
// Block 0's snapshot is the pre-genesis archive view (size 0), so archive leaf 0 is not visible from it;
|
|
84
|
+
// verify against the initial header hash instead. For later blocks, read archive leaf `blockNumber` from the
|
|
85
|
+
// snapshot's own view so the exact handle we return is validated against the requested fork.
|
|
86
|
+
const actualHash =
|
|
87
|
+
blockNumber === BlockNumber.ZERO
|
|
88
|
+
? (await this.merkleTreeCommitted.getInitialHeader().hash()).toString()
|
|
89
|
+
: (await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber)))?.toString();
|
|
90
|
+
|
|
91
|
+
if (actualHash === undefined) {
|
|
92
|
+
// A missing archive leaf means either the block's history has been pruned away (permanent: the block predates
|
|
93
|
+
// the oldest historical block kept by world state) or a reorg flipped the fork between the sync and this read
|
|
94
|
+
// (transient). Only the latter is worth retrying, so it alone surfaces as WorldStateSynchronizerError.
|
|
95
|
+
const { oldestHistoricalBlock } = await this.merkleTreeDb.getStatusSummary();
|
|
96
|
+
if (blockNumber < oldestHistoricalBlock) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`Unable to find leaf for block ${blockNumber} in the archive tree: world state history has been pruned to block ${oldestHistoricalBlock}`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
throw new WorldStateSynchronizerError(`Unable to read block hash at block ${blockNumber} to verify snapshot`, {
|
|
102
|
+
cause: { reason: 'block_not_available', targetBlockNumber: blockNumber },
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (actualHash !== blockHash.toString()) {
|
|
106
|
+
throw new WorldStateSynchronizerError(
|
|
107
|
+
`Block hash mismatch at block ${blockNumber} (expected ${blockHash} but got ${actualHash})`,
|
|
108
|
+
{
|
|
109
|
+
cause: {
|
|
110
|
+
reason: 'block_hash_mismatch',
|
|
111
|
+
targetBlockNumber: blockNumber,
|
|
112
|
+
expectedHash: blockHash.toString(),
|
|
113
|
+
actualHash,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return snapshot;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public fork(blockNumber?: BlockNumber, opts?: { closeDelayMs?: number }): Promise<MerkleTreeWriteOperations> {
|
|
122
|
+
return this.merkleTreeDb.fork(blockNumber, opts);
|
|
85
123
|
}
|
|
86
124
|
|
|
87
125
|
public backupTo(dstPath: string, compact?: boolean): Promise<Record<Exclude<SnapshotDataKeys, 'archiver'>, string>> {
|
|
@@ -101,11 +139,7 @@ export class ServerWorldStateSynchronizer
|
|
|
101
139
|
}
|
|
102
140
|
|
|
103
141
|
// Get the current latest block number
|
|
104
|
-
this.latestBlockNumberAtStart = BlockNumber(
|
|
105
|
-
await (this.config.worldStateProvenBlocksOnly
|
|
106
|
-
? this.l2BlockSource.getProvenBlockNumber()
|
|
107
|
-
: this.l2BlockSource.getBlockNumber()),
|
|
108
|
-
);
|
|
142
|
+
this.latestBlockNumberAtStart = BlockNumber(await this.l2BlockSource.getBlockNumber());
|
|
109
143
|
|
|
110
144
|
const blockToDownloadFrom = (await this.getLatestBlockNumber()) + 1;
|
|
111
145
|
|
|
@@ -126,13 +160,12 @@ export class ServerWorldStateSynchronizer
|
|
|
126
160
|
return this.syncPromise.promise;
|
|
127
161
|
}
|
|
128
162
|
|
|
129
|
-
protected createBlockStream():
|
|
130
|
-
const tracer = this.instrumentation.telemetry.getTracer('WorldStateL2BlockStream');
|
|
163
|
+
protected createBlockStream(): EventDrivenL2BlockStream {
|
|
131
164
|
const logger = createLogger('world-state:block_stream');
|
|
132
|
-
return new
|
|
133
|
-
proven: this.config.worldStateProvenBlocksOnly,
|
|
165
|
+
return new EventDrivenL2BlockStream(this.l2BlockSource, this, this, logger, {
|
|
134
166
|
pollIntervalMS: this.config.worldStateBlockCheckIntervalMS,
|
|
135
167
|
batchSize: this.config.worldStateBlockRequestBatchSize,
|
|
168
|
+
ignoreCheckpoints: true,
|
|
136
169
|
});
|
|
137
170
|
}
|
|
138
171
|
|
|
@@ -161,7 +194,7 @@ export class ServerWorldStateSynchronizer
|
|
|
161
194
|
}
|
|
162
195
|
|
|
163
196
|
public async getLatestBlockNumber() {
|
|
164
|
-
return (await this.getL2Tips()).
|
|
197
|
+
return (await this.getL2Tips()).proposed.number;
|
|
165
198
|
}
|
|
166
199
|
|
|
167
200
|
public async stopSync() {
|
|
@@ -182,13 +215,10 @@ export class ServerWorldStateSynchronizer
|
|
|
182
215
|
/**
|
|
183
216
|
* Forces an immediate sync.
|
|
184
217
|
* @param targetBlockNumber - The target block number that we must sync to. Will download unproven blocks if needed to reach it.
|
|
185
|
-
* @param
|
|
218
|
+
* @param blockHash - If provided, verifies the block at targetBlockNumber matches this hash. On mismatch, triggers a resync (reorg detection).
|
|
186
219
|
* @returns A promise that resolves with the block number the world state was synced to
|
|
187
220
|
*/
|
|
188
|
-
public async syncImmediate(
|
|
189
|
-
targetBlockNumber?: BlockNumber,
|
|
190
|
-
skipThrowIfTargetNotReached?: boolean,
|
|
191
|
-
): Promise<BlockNumber> {
|
|
221
|
+
public async syncImmediate(targetBlockNumber?: BlockNumber, blockHash?: BlockHash): Promise<BlockNumber> {
|
|
192
222
|
if (this.currentState !== WorldStateRunningState.RUNNING) {
|
|
193
223
|
throw new Error(`World State is not running. Unable to perform sync.`);
|
|
194
224
|
}
|
|
@@ -200,7 +230,19 @@ export class ServerWorldStateSynchronizer
|
|
|
200
230
|
// If we have been given a block number to sync to and we have reached that number then return
|
|
201
231
|
const currentBlockNumber = await this.getLatestBlockNumber();
|
|
202
232
|
if (targetBlockNumber !== undefined && targetBlockNumber <= currentBlockNumber) {
|
|
203
|
-
|
|
233
|
+
if (blockHash === undefined) {
|
|
234
|
+
return currentBlockNumber;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// If a block hash was provided, verify we're on the expected fork
|
|
238
|
+
const currentHash = await this.getL2BlockHash(targetBlockNumber);
|
|
239
|
+
if (currentHash === blockHash.toString()) {
|
|
240
|
+
return currentBlockNumber;
|
|
241
|
+
}
|
|
242
|
+
// Hash mismatch: a reorg may have occurred, fall through to trigger sync
|
|
243
|
+
this.log.debug(
|
|
244
|
+
`World state block hash mismatch at ${targetBlockNumber} (expected ${blockHash}, got ${currentHash}). Triggering resync.`,
|
|
245
|
+
);
|
|
204
246
|
}
|
|
205
247
|
this.log.debug(`World State at ${currentBlockNumber} told to sync to ${targetBlockNumber ?? 'latest'}`);
|
|
206
248
|
|
|
@@ -218,7 +260,7 @@ export class ServerWorldStateSynchronizer
|
|
|
218
260
|
|
|
219
261
|
// If we have been given a block number to sync to and we have not reached that number then fail
|
|
220
262
|
const updatedBlockNumber = await this.getLatestBlockNumber();
|
|
221
|
-
if (
|
|
263
|
+
if (targetBlockNumber !== undefined && targetBlockNumber > updatedBlockNumber) {
|
|
222
264
|
throw new WorldStateSynchronizerError(
|
|
223
265
|
`Unable to sync to block number ${targetBlockNumber} (last synced is ${updatedBlockNumber})`,
|
|
224
266
|
{
|
|
@@ -232,6 +274,24 @@ export class ServerWorldStateSynchronizer
|
|
|
232
274
|
);
|
|
233
275
|
}
|
|
234
276
|
|
|
277
|
+
// If a block hash was provided, verify we're on the expected fork after syncing, throw otherwise
|
|
278
|
+
if (blockHash !== undefined && targetBlockNumber !== undefined) {
|
|
279
|
+
const updatedHash = await this.getL2BlockHash(targetBlockNumber);
|
|
280
|
+
if (updatedHash !== blockHash.toString()) {
|
|
281
|
+
throw new WorldStateSynchronizerError(
|
|
282
|
+
`Block hash mismatch at block ${targetBlockNumber} (expected ${blockHash} but got ${updatedHash})`,
|
|
283
|
+
{
|
|
284
|
+
cause: {
|
|
285
|
+
reason: 'block_hash_mismatch',
|
|
286
|
+
targetBlockNumber,
|
|
287
|
+
expectedHash: blockHash.toString(),
|
|
288
|
+
actualHash: updatedHash,
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
235
295
|
return updatedBlockNumber;
|
|
236
296
|
}
|
|
237
297
|
|
|
@@ -240,27 +300,36 @@ export class ServerWorldStateSynchronizer
|
|
|
240
300
|
if (number === BlockNumber.ZERO) {
|
|
241
301
|
return (await this.merkleTreeCommitted.getInitialHeader().hash()).toString();
|
|
242
302
|
}
|
|
243
|
-
|
|
244
|
-
this.latestBlockHashQuery = {
|
|
245
|
-
hash: await this.merkleTreeCommitted
|
|
246
|
-
.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(number))
|
|
247
|
-
.then(leaf => leaf?.toString()),
|
|
248
|
-
blockNumber: number,
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
return this.latestBlockHashQuery.hash;
|
|
303
|
+
return this.merkleTreeCommitted.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(number)).then(leaf => leaf?.toString());
|
|
252
304
|
}
|
|
253
305
|
|
|
254
|
-
/**
|
|
255
|
-
|
|
306
|
+
/**
|
|
307
|
+
* Returns the proposed, proven, and finalized block tips of the chain. World state drives its block stream with
|
|
308
|
+
* `ignoreCheckpoints`, so it does not track checkpointed blocks or checkpoints and omits `checkpointed` from the tips
|
|
309
|
+
* it reports.
|
|
310
|
+
*/
|
|
311
|
+
public async getL2Tips(): Promise<LocalChainTips> {
|
|
256
312
|
const status = await this.merkleTreeDb.getStatusSummary();
|
|
257
|
-
const
|
|
258
|
-
const
|
|
259
|
-
|
|
313
|
+
const unfinalizedBlockHashPromise = this.getL2BlockHash(status.unfinalizedBlockNumber);
|
|
314
|
+
const finalizedBlockHashPromise = this.getL2BlockHash(status.finalizedBlockNumber);
|
|
315
|
+
|
|
316
|
+
const provenBlockNumber = this.provenBlockNumber ?? status.finalizedBlockNumber;
|
|
317
|
+
const provenBlockHashPromise =
|
|
318
|
+
this.provenBlockNumber === undefined ? finalizedBlockHashPromise : this.getL2BlockHash(this.provenBlockNumber);
|
|
319
|
+
|
|
320
|
+
const [unfinalizedBlockHash, finalizedBlockHash, provenBlockHash] = await Promise.all([
|
|
321
|
+
unfinalizedBlockHashPromise,
|
|
322
|
+
finalizedBlockHashPromise,
|
|
323
|
+
provenBlockHashPromise,
|
|
324
|
+
]);
|
|
260
325
|
return {
|
|
261
|
-
|
|
262
|
-
finalized: {
|
|
263
|
-
|
|
326
|
+
proposed: { number: status.unfinalizedBlockNumber, hash: unfinalizedBlockHash },
|
|
327
|
+
finalized: {
|
|
328
|
+
block: { number: status.finalizedBlockNumber, hash: finalizedBlockHash },
|
|
329
|
+
},
|
|
330
|
+
proven: {
|
|
331
|
+
block: { number: provenBlockNumber, hash: provenBlockHash },
|
|
332
|
+
},
|
|
264
333
|
};
|
|
265
334
|
}
|
|
266
335
|
|
|
@@ -268,17 +337,26 @@ export class ServerWorldStateSynchronizer
|
|
|
268
337
|
public async handleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void> {
|
|
269
338
|
switch (event.type) {
|
|
270
339
|
case 'blocks-added':
|
|
271
|
-
await this.handleL2Blocks(event.blocks
|
|
340
|
+
await this.handleL2Blocks(event.blocks);
|
|
272
341
|
break;
|
|
273
342
|
case 'chain-pruned':
|
|
274
|
-
await this.handleChainPruned(
|
|
343
|
+
await this.handleChainPruned(event.block.number);
|
|
275
344
|
break;
|
|
276
345
|
case 'chain-proven':
|
|
277
|
-
await this.handleChainProven(
|
|
346
|
+
await this.handleChainProven(event.block.number);
|
|
278
347
|
break;
|
|
279
348
|
case 'chain-finalized':
|
|
280
|
-
await this.handleChainFinalized(
|
|
349
|
+
await this.handleChainFinalized(event.block.number);
|
|
350
|
+
break;
|
|
351
|
+
// World state runs in block mode with ignoreCheckpoints: it tracks tips via blocks-added/pruned/proven/finalized
|
|
352
|
+
// and ignores the thin tip events (it never anchors on them).
|
|
353
|
+
case 'chain-proposed':
|
|
354
|
+
case 'chain-checkpointed':
|
|
281
355
|
break;
|
|
356
|
+
default: {
|
|
357
|
+
const _: never = event;
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
282
360
|
}
|
|
283
361
|
}
|
|
284
362
|
|
|
@@ -287,16 +365,25 @@ export class ServerWorldStateSynchronizer
|
|
|
287
365
|
* @param l2Blocks - The L2 blocks to handle.
|
|
288
366
|
* @returns Whether the block handled was produced by this same node.
|
|
289
367
|
*/
|
|
290
|
-
private async handleL2Blocks(l2Blocks:
|
|
291
|
-
this.log.
|
|
292
|
-
|
|
293
|
-
|
|
368
|
+
private async handleL2Blocks(l2Blocks: L2Block[]) {
|
|
369
|
+
this.log.debug(`Handling L2 blocks ${l2Blocks[0].number} to ${l2Blocks.at(-1)!.number}`);
|
|
370
|
+
|
|
371
|
+
// Fetch the L1->L2 messages for the first block in a checkpoint.
|
|
372
|
+
const messagesForBlocks = new Map<BlockNumber, Fr[]>();
|
|
373
|
+
await Promise.all(
|
|
374
|
+
l2Blocks
|
|
375
|
+
.filter(b => b.indexWithinCheckpoint === 0)
|
|
376
|
+
.map(async block => {
|
|
377
|
+
const l1ToL2Messages = await this.l2BlockSource.getL1ToL2Messages(block.checkpointNumber);
|
|
378
|
+
messagesForBlocks.set(block.number, l1ToL2Messages);
|
|
379
|
+
}),
|
|
380
|
+
);
|
|
294
381
|
|
|
295
382
|
let updateStatus: WorldStateStatusFull | undefined = undefined;
|
|
296
383
|
for (const block of l2Blocks) {
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
384
|
+
const [duration, result] = await elapsed(() =>
|
|
385
|
+
this.handleL2Block(block, messagesForBlocks.get(block.number) ?? []),
|
|
386
|
+
);
|
|
300
387
|
this.log.info(`World state updated with L2 block ${block.number}`, {
|
|
301
388
|
eventName: 'l2-block-handled',
|
|
302
389
|
duration,
|
|
@@ -319,18 +406,15 @@ export class ServerWorldStateSynchronizer
|
|
|
319
406
|
* @param l1ToL2Messages - The L1 to L2 messages for the block.
|
|
320
407
|
* @returns Whether the block handled was produced by this same node.
|
|
321
408
|
*/
|
|
322
|
-
private async handleL2Block(
|
|
323
|
-
l2Block
|
|
324
|
-
l1ToL2Messages: Fr[],
|
|
325
|
-
isFirstBlock: boolean,
|
|
326
|
-
): Promise<WorldStateStatusFull> {
|
|
327
|
-
// If the above check succeeds, we can proceed to handle the block.
|
|
328
|
-
this.log.trace(`Pushing L2 block ${l2Block.number} to merkle tree db `, {
|
|
409
|
+
private async handleL2Block(l2Block: L2Block, l1ToL2Messages: Fr[]): Promise<WorldStateStatusFull> {
|
|
410
|
+
this.log.debug(`Pushing L2 block ${l2Block.number} to merkle tree db `, {
|
|
329
411
|
blockNumber: l2Block.number,
|
|
330
412
|
blockHash: await l2Block.hash().then(h => h.toString()),
|
|
331
413
|
l1ToL2Messages: l1ToL2Messages.map(msg => msg.toString()),
|
|
414
|
+
blockHeader: l2Block.header.toInspect(),
|
|
415
|
+
blockStats: l2Block.getStats(),
|
|
332
416
|
});
|
|
333
|
-
const result = await this.merkleTreeDb.handleL2BlockAndMessages(l2Block, l1ToL2Messages
|
|
417
|
+
const result = await this.merkleTreeDb.handleL2BlockAndMessages(l2Block, l1ToL2Messages);
|
|
334
418
|
|
|
335
419
|
if (this.currentState === WorldStateRunningState.SYNCHING && l2Block.number >= this.latestBlockNumberAtStart) {
|
|
336
420
|
this.setCurrentState(WorldStateRunningState.RUNNING);
|
|
@@ -342,16 +426,56 @@ export class ServerWorldStateSynchronizer
|
|
|
342
426
|
|
|
343
427
|
private async handleChainFinalized(blockNumber: BlockNumber) {
|
|
344
428
|
this.log.verbose(`Finalized chain is now at block ${blockNumber}`);
|
|
429
|
+
// If the finalized block number is older than the oldest available block in world state,
|
|
430
|
+
// skip entirely. The finalized block number can jump backwards (e.g. when the finalization
|
|
431
|
+
// heuristic changes) and try to read block data that has already been pruned. When this
|
|
432
|
+
// happens, there is nothing useful to do — the native world state is already finalized
|
|
433
|
+
// past this point and pruning has already happened.
|
|
434
|
+
const currentSummary = await this.merkleTreeDb.getStatusSummary();
|
|
435
|
+
if (blockNumber < currentSummary.oldestHistoricalBlock || blockNumber < 1) {
|
|
436
|
+
this.log.trace(
|
|
437
|
+
`Finalized block ${blockNumber} is older than the oldest available block ${currentSummary.oldestHistoricalBlock}. Skipping.`,
|
|
438
|
+
);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
345
441
|
const summary = await this.merkleTreeDb.setFinalized(blockNumber);
|
|
346
442
|
if (this.historyToKeep === undefined) {
|
|
347
443
|
return;
|
|
348
444
|
}
|
|
349
|
-
const
|
|
350
|
-
if (
|
|
445
|
+
const finalisedBlockData = await this.l2BlockSource.getBlockData({ number: summary.finalizedBlockNumber });
|
|
446
|
+
if (finalisedBlockData === undefined) {
|
|
447
|
+
this.log.warn(
|
|
448
|
+
`Failed to retrieve checkpointed block for finalized block number: ${summary.finalizedBlockNumber}`,
|
|
449
|
+
);
|
|
351
450
|
return;
|
|
352
451
|
}
|
|
353
|
-
|
|
354
|
-
const
|
|
452
|
+
// Compute the required historic checkpoint number
|
|
453
|
+
const newHistoricCheckpointNumber = finalisedBlockData.checkpointNumber - this.historyToKeep + 1;
|
|
454
|
+
if (newHistoricCheckpointNumber <= 1) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
// Retrieve the historic checkpoint
|
|
458
|
+
const historicCheckpoint = await this.l2BlockSource.getCheckpoint({
|
|
459
|
+
number: CheckpointNumber(newHistoricCheckpointNumber),
|
|
460
|
+
});
|
|
461
|
+
if (!historicCheckpoint) {
|
|
462
|
+
this.log.warn(`Failed to retrieve checkpoint number ${newHistoricCheckpointNumber} from Archiver`);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
if (historicCheckpoint.checkpoint.blocks.length === 0 || historicCheckpoint.checkpoint.blocks[0] === undefined) {
|
|
466
|
+
this.log.warn(`Retrieved checkpoint number ${newHistoricCheckpointNumber} has no blocks!`);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
// Find the block at the start of the checkpoint and remove blocks up to this one
|
|
470
|
+
const newHistoricBlock = historicCheckpoint.checkpoint.blocks[0];
|
|
471
|
+
if (newHistoricBlock.number <= currentSummary.oldestHistoricalBlock) {
|
|
472
|
+
this.log.debug(
|
|
473
|
+
`Historic block ${newHistoricBlock.number} is not newer than oldest available ${currentSummary.oldestHistoricalBlock}. Skipping prune.`,
|
|
474
|
+
);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
this.log.verbose(`Pruning historic blocks to ${newHistoricBlock.number}`);
|
|
478
|
+
const status = await this.merkleTreeDb.removeHistoricalBlocks(BlockNumber(newHistoricBlock.number));
|
|
355
479
|
this.log.debug(`World state summary `, status.summary);
|
|
356
480
|
}
|
|
357
481
|
|
|
@@ -362,10 +486,11 @@ export class ServerWorldStateSynchronizer
|
|
|
362
486
|
}
|
|
363
487
|
|
|
364
488
|
private async handleChainPruned(blockNumber: BlockNumber) {
|
|
365
|
-
this.log.
|
|
489
|
+
this.log.info(`Chain pruned to block ${blockNumber}`);
|
|
366
490
|
const status = await this.merkleTreeDb.unwindBlocks(blockNumber);
|
|
367
|
-
this.
|
|
368
|
-
|
|
491
|
+
if (this.provenBlockNumber !== undefined && this.provenBlockNumber > blockNumber) {
|
|
492
|
+
this.provenBlockNumber = undefined;
|
|
493
|
+
}
|
|
369
494
|
this.instrumentation.updateWorldStateMetrics(status);
|
|
370
495
|
}
|
|
371
496
|
|