@aztec/world-state 0.0.1-commit.d3ec352c → 0.0.1-commit.e6bd8901
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/instrumentation/instrumentation.d.ts +1 -1
- package/dest/instrumentation/instrumentation.d.ts.map +1 -1
- package/dest/instrumentation/instrumentation.js +11 -42
- package/dest/native/merkle_trees_facade.d.ts +8 -3
- package/dest/native/merkle_trees_facade.d.ts.map +1 -1
- package/dest/native/merkle_trees_facade.js +35 -6
- package/dest/native/message.d.ts +2 -2
- package/dest/native/message.d.ts.map +1 -1
- package/dest/native/message.js +1 -1
- package/dest/native/native_world_state.d.ts +7 -5
- package/dest/native/native_world_state.d.ts.map +1 -1
- package/dest/native/native_world_state.js +5 -4
- package/dest/synchronizer/config.d.ts +1 -3
- package/dest/synchronizer/config.d.ts.map +1 -1
- package/dest/synchronizer/config.js +1 -6
- package/dest/synchronizer/server_world_state_synchronizer.d.ts +5 -4
- package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
- package/dest/synchronizer/server_world_state_synchronizer.js +67 -40
- package/dest/test/utils.d.ts +11 -17
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +44 -81
- package/dest/testing.d.ts +2 -2
- package/dest/testing.d.ts.map +1 -1
- package/dest/testing.js +1 -1
- package/dest/world-state-db/merkle_tree_db.d.ts +7 -9
- package/dest/world-state-db/merkle_tree_db.d.ts.map +1 -1
- package/package.json +11 -11
- package/src/instrumentation/instrumentation.ts +10 -42
- package/src/native/merkle_trees_facade.ts +36 -3
- package/src/native/message.ts +1 -1
- package/src/native/native_world_state.ts +9 -8
- package/src/synchronizer/config.ts +1 -14
- package/src/synchronizer/server_world_state_synchronizer.ts +71 -56
- package/src/test/utils.ts +70 -128
- package/src/testing.ts +1 -1
- package/src/world-state-db/merkle_tree_db.ts +10 -12
- 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/synchronizer/utils.ts +0 -83
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
Metrics,
|
|
8
8
|
type TelemetryClient,
|
|
9
9
|
type UpDownCounter,
|
|
10
|
-
ValueType,
|
|
11
10
|
} from '@aztec/telemetry-client';
|
|
12
11
|
|
|
13
12
|
import {
|
|
@@ -46,56 +45,25 @@ export class WorldStateInstrumentation {
|
|
|
46
45
|
private log: Logger = createLogger('world-state:instrumentation'),
|
|
47
46
|
) {
|
|
48
47
|
const meter = telemetry.getMeter('World State');
|
|
49
|
-
this.dbMapSize = meter.createGauge(Metrics.WORLD_STATE_DB_MAP_SIZE
|
|
50
|
-
description: `The current configured map size for each merkle tree`,
|
|
51
|
-
valueType: ValueType.INT,
|
|
52
|
-
});
|
|
48
|
+
this.dbMapSize = meter.createGauge(Metrics.WORLD_STATE_DB_MAP_SIZE);
|
|
53
49
|
|
|
54
|
-
this.dbPhysicalSize = meter.createGauge(Metrics.WORLD_STATE_DB_PHYSICAL_SIZE
|
|
55
|
-
description: `The current physical disk space used for each database`,
|
|
56
|
-
valueType: ValueType.INT,
|
|
57
|
-
});
|
|
50
|
+
this.dbPhysicalSize = meter.createGauge(Metrics.WORLD_STATE_DB_PHYSICAL_SIZE);
|
|
58
51
|
|
|
59
|
-
this.treeSize = meter.createGauge(Metrics.WORLD_STATE_TREE_SIZE
|
|
60
|
-
description: `The current number of leaves in each merkle tree`,
|
|
61
|
-
valueType: ValueType.INT,
|
|
62
|
-
});
|
|
52
|
+
this.treeSize = meter.createGauge(Metrics.WORLD_STATE_TREE_SIZE);
|
|
63
53
|
|
|
64
|
-
this.unfinalizedHeight = meter.createGauge(Metrics.WORLD_STATE_UNFINALIZED_HEIGHT
|
|
65
|
-
description: `The unfinalized block height of each merkle tree`,
|
|
66
|
-
valueType: ValueType.INT,
|
|
67
|
-
});
|
|
54
|
+
this.unfinalizedHeight = meter.createGauge(Metrics.WORLD_STATE_UNFINALIZED_HEIGHT);
|
|
68
55
|
|
|
69
|
-
this.finalizedHeight = meter.createGauge(Metrics.WORLD_STATE_FINALIZED_HEIGHT
|
|
70
|
-
description: `The finalized block height of each merkle tree`,
|
|
71
|
-
valueType: ValueType.INT,
|
|
72
|
-
});
|
|
56
|
+
this.finalizedHeight = meter.createGauge(Metrics.WORLD_STATE_FINALIZED_HEIGHT);
|
|
73
57
|
|
|
74
|
-
this.oldestBlock = meter.createGauge(Metrics.WORLD_STATE_OLDEST_BLOCK
|
|
75
|
-
description: `The oldest historical block of each merkle tree`,
|
|
76
|
-
valueType: ValueType.INT,
|
|
77
|
-
});
|
|
58
|
+
this.oldestBlock = meter.createGauge(Metrics.WORLD_STATE_OLDEST_BLOCK);
|
|
78
59
|
|
|
79
|
-
this.dbUsedSize = meter.createGauge(Metrics.WORLD_STATE_DB_USED_SIZE
|
|
80
|
-
description: `The current used database size for each db of each merkle tree`,
|
|
81
|
-
valueType: ValueType.INT,
|
|
82
|
-
});
|
|
60
|
+
this.dbUsedSize = meter.createGauge(Metrics.WORLD_STATE_DB_USED_SIZE);
|
|
83
61
|
|
|
84
|
-
this.dbNumItems = meter.createGauge(Metrics.WORLD_STATE_DB_NUM_ITEMS
|
|
85
|
-
description: `The current number of items in each database of each merkle tree`,
|
|
86
|
-
valueType: ValueType.INT,
|
|
87
|
-
});
|
|
62
|
+
this.dbNumItems = meter.createGauge(Metrics.WORLD_STATE_DB_NUM_ITEMS);
|
|
88
63
|
|
|
89
|
-
this.requestHistogram = meter.createHistogram(Metrics.WORLD_STATE_REQUEST_TIME
|
|
90
|
-
description: 'The round trip time of world state requests',
|
|
91
|
-
unit: 'us',
|
|
92
|
-
valueType: ValueType.INT,
|
|
93
|
-
});
|
|
64
|
+
this.requestHistogram = meter.createHistogram(Metrics.WORLD_STATE_REQUEST_TIME);
|
|
94
65
|
|
|
95
|
-
this.criticalErrors = meter.createUpDownCounter(Metrics.WORLD_STATE_CRITICAL_ERROR_COUNT
|
|
96
|
-
description: 'The number of critical errors in the world state',
|
|
97
|
-
valueType: ValueType.INT,
|
|
98
|
-
});
|
|
66
|
+
this.criticalErrors = meter.createUpDownCounter(Metrics.WORLD_STATE_CRITICAL_ERROR_COUNT);
|
|
99
67
|
}
|
|
100
68
|
|
|
101
69
|
private updateTreeStats(treeDbStats: TreeDBStats, treeMeta: TreeMeta, tree: MerkleTreeId) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
|
-
import { Fr } from '@aztec/foundation/
|
|
2
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
3
4
|
import { serializeToBuffer } from '@aztec/foundation/serialize';
|
|
5
|
+
import { sleep } from '@aztec/foundation/sleep';
|
|
4
6
|
import { type IndexedTreeLeafPreimage, SiblingPath } from '@aztec/foundation/trees';
|
|
5
7
|
import type {
|
|
6
8
|
BatchInsertionResult,
|
|
@@ -204,7 +206,14 @@ export class MerkleTreesFacade implements MerkleTreeReadOperations {
|
|
|
204
206
|
}
|
|
205
207
|
|
|
206
208
|
export class MerkleTreesForkFacade extends MerkleTreesFacade implements MerkleTreeWriteOperations {
|
|
207
|
-
|
|
209
|
+
private log = createLogger('world-state:merkle-trees-fork-facade');
|
|
210
|
+
|
|
211
|
+
constructor(
|
|
212
|
+
instance: NativeWorldStateInstance,
|
|
213
|
+
initialHeader: BlockHeader,
|
|
214
|
+
revision: WorldStateRevision,
|
|
215
|
+
private opts: { closeDelayMs?: number },
|
|
216
|
+
) {
|
|
208
217
|
assert.notEqual(revision.forkId, 0, 'Fork ID must be set');
|
|
209
218
|
assert.equal(revision.includeUncommitted, true, 'Fork must include uncommitted data');
|
|
210
219
|
super(instance, initialHeader, revision);
|
|
@@ -283,7 +292,31 @@ export class MerkleTreesForkFacade extends MerkleTreesFacade implements MerkleTr
|
|
|
283
292
|
|
|
284
293
|
public async close(): Promise<void> {
|
|
285
294
|
assert.notEqual(this.revision.forkId, 0, 'Fork ID must be set');
|
|
286
|
-
|
|
295
|
+
try {
|
|
296
|
+
await this.instance.call(WorldStateMessageType.DELETE_FORK, { forkId: this.revision.forkId });
|
|
297
|
+
} catch (err: any) {
|
|
298
|
+
// Ignore errors due to native instance being closed during shutdown.
|
|
299
|
+
// This can happen when validators are still processing block proposals while the node is stopping.
|
|
300
|
+
if (err?.message === 'Native instance is closed') {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
throw err;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async [Symbol.dispose](): Promise<void> {
|
|
308
|
+
if (this.opts.closeDelayMs) {
|
|
309
|
+
void sleep(this.opts.closeDelayMs)
|
|
310
|
+
.then(() => this.close())
|
|
311
|
+
.catch(err => {
|
|
312
|
+
if (err && 'message' in err && err.message === 'Native instance is closed') {
|
|
313
|
+
return; // Ignore errors due to native instance being closed
|
|
314
|
+
}
|
|
315
|
+
this.log.warn('Error closing MerkleTreesForkFacade after delay', { err });
|
|
316
|
+
});
|
|
317
|
+
} else {
|
|
318
|
+
await this.close();
|
|
319
|
+
}
|
|
287
320
|
}
|
|
288
321
|
|
|
289
322
|
public async createCheckpoint(): Promise<void> {
|
package/src/native/message.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
|
-
import { Fr } from '@aztec/foundation/
|
|
2
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
3
|
import type { Tuple } from '@aztec/foundation/serialize';
|
|
4
4
|
import { AppendOnlyTreeSnapshot, MerkleTreeId } from '@aztec/stdlib/trees';
|
|
5
5
|
import type { StateReference } from '@aztec/stdlib/tx';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
|
|
2
2
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
3
3
|
import { fromEntries, padArrayEnd } from '@aztec/foundation/collection';
|
|
4
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
5
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
5
|
-
import { Fr } from '@aztec/foundation/fields';
|
|
6
6
|
import { tryRmDir } from '@aztec/foundation/fs';
|
|
7
7
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
8
|
-
import type {
|
|
8
|
+
import type { L2Block } from '@aztec/stdlib/block';
|
|
9
9
|
import { DatabaseVersionManager } from '@aztec/stdlib/database-version';
|
|
10
10
|
import type {
|
|
11
11
|
IndexedTreeId,
|
|
@@ -159,7 +159,10 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
|
|
|
159
159
|
);
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
public async fork(
|
|
162
|
+
public async fork(
|
|
163
|
+
blockNumber?: BlockNumber,
|
|
164
|
+
opts: { closeDelayMs?: number } = {},
|
|
165
|
+
): Promise<MerkleTreeWriteOperations> {
|
|
163
166
|
const resp = await this.instance.call(WorldStateMessageType.CREATE_FORK, {
|
|
164
167
|
latest: blockNumber === undefined,
|
|
165
168
|
blockNumber: blockNumber ?? BlockNumber.ZERO,
|
|
@@ -173,6 +176,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
|
|
|
173
176
|
/* blockNumber=*/ BlockNumber.ZERO,
|
|
174
177
|
/* includeUncommitted=*/ true,
|
|
175
178
|
),
|
|
179
|
+
opts,
|
|
176
180
|
);
|
|
177
181
|
}
|
|
178
182
|
|
|
@@ -180,11 +184,8 @@ export class NativeWorldStateService implements MerkleTreeDatabase {
|
|
|
180
184
|
return this.initialHeader!;
|
|
181
185
|
}
|
|
182
186
|
|
|
183
|
-
public async handleL2BlockAndMessages(
|
|
184
|
-
l2Block
|
|
185
|
-
l1ToL2Messages: Fr[],
|
|
186
|
-
isFirstBlock: boolean,
|
|
187
|
-
): Promise<WorldStateStatusFull> {
|
|
187
|
+
public async handleL2BlockAndMessages(l2Block: L2Block, l1ToL2Messages: Fr[]): Promise<WorldStateStatusFull> {
|
|
188
|
+
const isFirstBlock = l2Block.indexWithinCheckpoint === 0;
|
|
188
189
|
if (!isFirstBlock && l1ToL2Messages.length > 0) {
|
|
189
190
|
throw new Error(
|
|
190
191
|
`L1 to L2 messages must be empty for non-first blocks, but got ${l1ToL2Messages.length} messages for block ${l2Block.number}.`,
|
|
@@ -1,18 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type ConfigMappingsType,
|
|
3
|
-
booleanConfigHelper,
|
|
4
|
-
getConfigFromMappings,
|
|
5
|
-
numberConfigHelper,
|
|
6
|
-
} from '@aztec/foundation/config';
|
|
1
|
+
import { type ConfigMappingsType, getConfigFromMappings, numberConfigHelper } from '@aztec/foundation/config';
|
|
7
2
|
|
|
8
3
|
/** World State synchronizer configuration values. */
|
|
9
4
|
export interface WorldStateConfig {
|
|
10
5
|
/** The frequency in which to check. */
|
|
11
6
|
worldStateBlockCheckIntervalMS: number;
|
|
12
7
|
|
|
13
|
-
/** Whether to follow only the proven chain. */
|
|
14
|
-
worldStateProvenBlocksOnly: boolean;
|
|
15
|
-
|
|
16
8
|
/** Size of the batch for each get-blocks request from the synchronizer to the archiver. */
|
|
17
9
|
worldStateBlockRequestBatchSize?: number;
|
|
18
10
|
|
|
@@ -48,11 +40,6 @@ export const worldStateConfigMappings: ConfigMappingsType<WorldStateConfig> = {
|
|
|
48
40
|
defaultValue: 100,
|
|
49
41
|
description: 'The frequency in which to check.',
|
|
50
42
|
},
|
|
51
|
-
worldStateProvenBlocksOnly: {
|
|
52
|
-
env: 'WS_PROVEN_BLOCKS_ONLY',
|
|
53
|
-
description: 'Whether to follow only the proven chain.',
|
|
54
|
-
...booleanConfigHelper(),
|
|
55
|
-
},
|
|
56
43
|
worldStateBlockRequestBatchSize: {
|
|
57
44
|
env: 'WS_BLOCK_REQUEST_BATCH_SIZE',
|
|
58
45
|
parseEnv: (val: string | undefined) => (val ? +val : undefined),
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
+
import { GENESIS_BLOCK_HEADER_HASH, INITIAL_L2_BLOCK_NUM, INITIAL_L2_CHECKPOINT_NUM } from '@aztec/constants';
|
|
1
2
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
|
-
import type { Fr } from '@aztec/foundation/
|
|
3
|
+
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
4
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
4
5
|
import { promiseWithResolvers } from '@aztec/foundation/promise';
|
|
5
6
|
import { elapsed } from '@aztec/foundation/timer';
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import {
|
|
8
|
+
GENESIS_CHECKPOINT_HEADER_HASH,
|
|
9
|
+
type L2Block,
|
|
10
|
+
type L2BlockId,
|
|
11
|
+
type L2BlockSource,
|
|
10
12
|
L2BlockStream,
|
|
11
|
-
L2BlockStreamEvent,
|
|
12
|
-
L2BlockStreamEventHandler,
|
|
13
|
-
L2BlockStreamLocalDataProvider,
|
|
14
|
-
L2Tips,
|
|
13
|
+
type L2BlockStreamEvent,
|
|
14
|
+
type L2BlockStreamEventHandler,
|
|
15
|
+
type L2BlockStreamLocalDataProvider,
|
|
16
|
+
type L2Tips,
|
|
15
17
|
} from '@aztec/stdlib/block';
|
|
16
18
|
import {
|
|
17
19
|
WorldStateRunningState,
|
|
@@ -23,14 +25,13 @@ import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
|
23
25
|
import type { SnapshotDataKeys } from '@aztec/stdlib/snapshots';
|
|
24
26
|
import type { L2BlockHandledStats } from '@aztec/stdlib/stats';
|
|
25
27
|
import { MerkleTreeId, type MerkleTreeReadOperations, type MerkleTreeWriteOperations } from '@aztec/stdlib/trees';
|
|
26
|
-
import {
|
|
28
|
+
import { getTelemetryClient } from '@aztec/telemetry-client';
|
|
27
29
|
|
|
28
30
|
import { WorldStateInstrumentation } from '../instrumentation/instrumentation.js';
|
|
29
31
|
import type { WorldStateStatusFull } from '../native/message.js';
|
|
30
32
|
import type { MerkleTreeAdminDatabase } from '../world-state-db/merkle_tree_db.js';
|
|
31
33
|
import type { WorldStateConfig } from './config.js';
|
|
32
34
|
import { WorldStateSynchronizerError } from './errors.js';
|
|
33
|
-
import { findFirstBlocksInCheckpoints } from './utils.js';
|
|
34
35
|
|
|
35
36
|
export type { SnapshotDataKeys };
|
|
36
37
|
|
|
@@ -47,7 +48,6 @@ export class ServerWorldStateSynchronizer
|
|
|
47
48
|
private latestBlockNumberAtStart = BlockNumber.ZERO;
|
|
48
49
|
private historyToKeep: number | undefined;
|
|
49
50
|
private currentState: WorldStateRunningState = WorldStateRunningState.IDLE;
|
|
50
|
-
private latestBlockHashQuery: { blockNumber: BlockNumber; hash: string | undefined } | undefined = undefined;
|
|
51
51
|
|
|
52
52
|
private syncPromise = promiseWithResolvers<void>();
|
|
53
53
|
protected blockStream: L2BlockStream | undefined;
|
|
@@ -80,8 +80,8 @@ export class ServerWorldStateSynchronizer
|
|
|
80
80
|
return this.merkleTreeDb.getSnapshot(blockNumber);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
public fork(blockNumber?: BlockNumber): Promise<MerkleTreeWriteOperations> {
|
|
84
|
-
return this.merkleTreeDb.fork(blockNumber);
|
|
83
|
+
public fork(blockNumber?: BlockNumber, opts?: { closeDelayMs?: number }): Promise<MerkleTreeWriteOperations> {
|
|
84
|
+
return this.merkleTreeDb.fork(blockNumber, opts);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
public backupTo(dstPath: string, compact?: boolean): Promise<Record<Exclude<SnapshotDataKeys, 'archiver'>, string>> {
|
|
@@ -101,11 +101,7 @@ export class ServerWorldStateSynchronizer
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
// Get the current latest block number
|
|
104
|
-
this.latestBlockNumberAtStart = BlockNumber(
|
|
105
|
-
await (this.config.worldStateProvenBlocksOnly
|
|
106
|
-
? this.l2BlockSource.getProvenBlockNumber()
|
|
107
|
-
: this.l2BlockSource.getBlockNumber()),
|
|
108
|
-
);
|
|
104
|
+
this.latestBlockNumberAtStart = BlockNumber(await this.l2BlockSource.getBlockNumber());
|
|
109
105
|
|
|
110
106
|
const blockToDownloadFrom = (await this.getLatestBlockNumber()) + 1;
|
|
111
107
|
|
|
@@ -127,12 +123,11 @@ export class ServerWorldStateSynchronizer
|
|
|
127
123
|
}
|
|
128
124
|
|
|
129
125
|
protected createBlockStream(): L2BlockStream {
|
|
130
|
-
const tracer = this.instrumentation.telemetry.getTracer('WorldStateL2BlockStream');
|
|
131
126
|
const logger = createLogger('world-state:block_stream');
|
|
132
|
-
return new
|
|
133
|
-
proven: this.config.worldStateProvenBlocksOnly,
|
|
127
|
+
return new L2BlockStream(this.l2BlockSource, this, this, logger, {
|
|
134
128
|
pollIntervalMS: this.config.worldStateBlockCheckIntervalMS,
|
|
135
129
|
batchSize: this.config.worldStateBlockRequestBatchSize,
|
|
130
|
+
ignoreCheckpoints: true,
|
|
136
131
|
});
|
|
137
132
|
}
|
|
138
133
|
|
|
@@ -161,7 +156,7 @@ export class ServerWorldStateSynchronizer
|
|
|
161
156
|
}
|
|
162
157
|
|
|
163
158
|
public async getLatestBlockNumber() {
|
|
164
|
-
return (await this.getL2Tips()).
|
|
159
|
+
return (await this.getL2Tips()).proposed.number;
|
|
165
160
|
}
|
|
166
161
|
|
|
167
162
|
public async stopSync() {
|
|
@@ -240,27 +235,44 @@ export class ServerWorldStateSynchronizer
|
|
|
240
235
|
if (number === BlockNumber.ZERO) {
|
|
241
236
|
return (await this.merkleTreeCommitted.getInitialHeader().hash()).toString();
|
|
242
237
|
}
|
|
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;
|
|
238
|
+
return this.merkleTreeCommitted.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(number)).then(leaf => leaf?.toString());
|
|
252
239
|
}
|
|
253
240
|
|
|
254
241
|
/** Returns the latest L2 block number for each tip of the chain (latest, proven, finalized). */
|
|
255
242
|
public async getL2Tips(): Promise<L2Tips> {
|
|
256
243
|
const status = await this.merkleTreeDb.getStatusSummary();
|
|
257
|
-
const
|
|
244
|
+
const unfinalizedBlockHashPromise = this.getL2BlockHash(status.unfinalizedBlockNumber);
|
|
245
|
+
const finalizedBlockHashPromise = this.getL2BlockHash(status.finalizedBlockNumber);
|
|
246
|
+
|
|
247
|
+
const provenBlockNumber = this.provenBlockNumber ?? status.finalizedBlockNumber;
|
|
248
|
+
const provenBlockHashPromise =
|
|
249
|
+
this.provenBlockNumber === undefined ? finalizedBlockHashPromise : this.getL2BlockHash(this.provenBlockNumber);
|
|
250
|
+
|
|
251
|
+
const [unfinalizedBlockHash, finalizedBlockHash, provenBlockHash] = await Promise.all([
|
|
252
|
+
unfinalizedBlockHashPromise,
|
|
253
|
+
finalizedBlockHashPromise,
|
|
254
|
+
provenBlockHashPromise,
|
|
255
|
+
]);
|
|
258
256
|
const latestBlockId: L2BlockId = { number: status.unfinalizedBlockNumber, hash: unfinalizedBlockHash! };
|
|
259
257
|
|
|
258
|
+
// World state doesn't track checkpointed blocks or checkpoints themselves.
|
|
259
|
+
// but we use a block stream so we need to provide 'local' L2Tips.
|
|
260
|
+
// We configure the block stream to ignore checkpoints and set checkpoint values to genesis here.
|
|
261
|
+
const genesisCheckpointHeaderHash = GENESIS_CHECKPOINT_HEADER_HASH.toString();
|
|
260
262
|
return {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
263
|
+
proposed: latestBlockId,
|
|
264
|
+
checkpointed: {
|
|
265
|
+
block: { number: INITIAL_L2_BLOCK_NUM, hash: GENESIS_BLOCK_HEADER_HASH.toString() },
|
|
266
|
+
checkpoint: { number: INITIAL_L2_CHECKPOINT_NUM, hash: genesisCheckpointHeaderHash },
|
|
267
|
+
},
|
|
268
|
+
finalized: {
|
|
269
|
+
block: { number: status.finalizedBlockNumber, hash: finalizedBlockHash ?? '' },
|
|
270
|
+
checkpoint: { number: INITIAL_L2_CHECKPOINT_NUM, hash: genesisCheckpointHeaderHash },
|
|
271
|
+
},
|
|
272
|
+
proven: {
|
|
273
|
+
block: { number: provenBlockNumber, hash: provenBlockHash ?? '' },
|
|
274
|
+
checkpoint: { number: INITIAL_L2_CHECKPOINT_NUM, hash: genesisCheckpointHeaderHash },
|
|
275
|
+
},
|
|
264
276
|
};
|
|
265
277
|
}
|
|
266
278
|
|
|
@@ -268,16 +280,16 @@ export class ServerWorldStateSynchronizer
|
|
|
268
280
|
public async handleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void> {
|
|
269
281
|
switch (event.type) {
|
|
270
282
|
case 'blocks-added':
|
|
271
|
-
await this.handleL2Blocks(event.blocks
|
|
283
|
+
await this.handleL2Blocks(event.blocks);
|
|
272
284
|
break;
|
|
273
285
|
case 'chain-pruned':
|
|
274
|
-
await this.handleChainPruned(
|
|
286
|
+
await this.handleChainPruned(event.block.number);
|
|
275
287
|
break;
|
|
276
288
|
case 'chain-proven':
|
|
277
|
-
await this.handleChainProven(
|
|
289
|
+
await this.handleChainProven(event.block.number);
|
|
278
290
|
break;
|
|
279
291
|
case 'chain-finalized':
|
|
280
|
-
await this.handleChainFinalized(
|
|
292
|
+
await this.handleChainFinalized(event.block.number);
|
|
281
293
|
break;
|
|
282
294
|
}
|
|
283
295
|
}
|
|
@@ -287,16 +299,25 @@ export class ServerWorldStateSynchronizer
|
|
|
287
299
|
* @param l2Blocks - The L2 blocks to handle.
|
|
288
300
|
* @returns Whether the block handled was produced by this same node.
|
|
289
301
|
*/
|
|
290
|
-
private async handleL2Blocks(l2Blocks:
|
|
302
|
+
private async handleL2Blocks(l2Blocks: L2Block[]) {
|
|
291
303
|
this.log.trace(`Handling L2 blocks ${l2Blocks[0].number} to ${l2Blocks.at(-1)!.number}`);
|
|
292
304
|
|
|
293
|
-
|
|
305
|
+
// Fetch the L1->L2 messages for the first block in a checkpoint.
|
|
306
|
+
const messagesForBlocks = new Map<BlockNumber, Fr[]>();
|
|
307
|
+
await Promise.all(
|
|
308
|
+
l2Blocks
|
|
309
|
+
.filter(b => b.indexWithinCheckpoint === 0)
|
|
310
|
+
.map(async block => {
|
|
311
|
+
const l1ToL2Messages = await this.l2BlockSource.getL1ToL2Messages(block.checkpointNumber);
|
|
312
|
+
messagesForBlocks.set(block.number, l1ToL2Messages);
|
|
313
|
+
}),
|
|
314
|
+
);
|
|
294
315
|
|
|
295
316
|
let updateStatus: WorldStateStatusFull | undefined = undefined;
|
|
296
317
|
for (const block of l2Blocks) {
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
318
|
+
const [duration, result] = await elapsed(() =>
|
|
319
|
+
this.handleL2Block(block, messagesForBlocks.get(block.number) ?? []),
|
|
320
|
+
);
|
|
300
321
|
this.log.info(`World state updated with L2 block ${block.number}`, {
|
|
301
322
|
eventName: 'l2-block-handled',
|
|
302
323
|
duration,
|
|
@@ -319,18 +340,13 @@ export class ServerWorldStateSynchronizer
|
|
|
319
340
|
* @param l1ToL2Messages - The L1 to L2 messages for the block.
|
|
320
341
|
* @returns Whether the block handled was produced by this same node.
|
|
321
342
|
*/
|
|
322
|
-
private async handleL2Block(
|
|
323
|
-
l2Block: L2BlockNew,
|
|
324
|
-
l1ToL2Messages: Fr[],
|
|
325
|
-
isFirstBlock: boolean,
|
|
326
|
-
): Promise<WorldStateStatusFull> {
|
|
327
|
-
// If the above check succeeds, we can proceed to handle the block.
|
|
343
|
+
private async handleL2Block(l2Block: L2Block, l1ToL2Messages: Fr[]): Promise<WorldStateStatusFull> {
|
|
328
344
|
this.log.trace(`Pushing L2 block ${l2Block.number} to merkle tree db `, {
|
|
329
345
|
blockNumber: l2Block.number,
|
|
330
346
|
blockHash: await l2Block.hash().then(h => h.toString()),
|
|
331
347
|
l1ToL2Messages: l1ToL2Messages.map(msg => msg.toString()),
|
|
332
348
|
});
|
|
333
|
-
const result = await this.merkleTreeDb.handleL2BlockAndMessages(l2Block, l1ToL2Messages
|
|
349
|
+
const result = await this.merkleTreeDb.handleL2BlockAndMessages(l2Block, l1ToL2Messages);
|
|
334
350
|
|
|
335
351
|
if (this.currentState === WorldStateRunningState.SYNCHING && l2Block.number >= this.latestBlockNumberAtStart) {
|
|
336
352
|
this.setCurrentState(WorldStateRunningState.RUNNING);
|
|
@@ -346,12 +362,12 @@ export class ServerWorldStateSynchronizer
|
|
|
346
362
|
if (this.historyToKeep === undefined) {
|
|
347
363
|
return;
|
|
348
364
|
}
|
|
349
|
-
const newHistoricBlock =
|
|
350
|
-
if (newHistoricBlock <=
|
|
365
|
+
const newHistoricBlock = summary.finalizedBlockNumber - this.historyToKeep + 1;
|
|
366
|
+
if (newHistoricBlock <= 1) {
|
|
351
367
|
return;
|
|
352
368
|
}
|
|
353
369
|
this.log.verbose(`Pruning historic blocks to ${newHistoricBlock}`);
|
|
354
|
-
const status = await this.merkleTreeDb.removeHistoricalBlocks(newHistoricBlock);
|
|
370
|
+
const status = await this.merkleTreeDb.removeHistoricalBlocks(BlockNumber(newHistoricBlock));
|
|
355
371
|
this.log.debug(`World state summary `, status.summary);
|
|
356
372
|
}
|
|
357
373
|
|
|
@@ -364,7 +380,6 @@ export class ServerWorldStateSynchronizer
|
|
|
364
380
|
private async handleChainPruned(blockNumber: BlockNumber) {
|
|
365
381
|
this.log.warn(`Chain pruned to block ${blockNumber}`);
|
|
366
382
|
const status = await this.merkleTreeDb.unwindBlocks(blockNumber);
|
|
367
|
-
this.latestBlockHashQuery = undefined;
|
|
368
383
|
this.provenBlockNumber = undefined;
|
|
369
384
|
this.instrumentation.updateWorldStateMetrics(status);
|
|
370
385
|
}
|