@aztec/archiver 0.0.1-commit.0dc957cde → 0.0.1-commit.0ec55a70b
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/archiver.d.ts +15 -9
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +85 -53
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +2 -2
- package/dest/errors.d.ts +16 -5
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +29 -6
- package/dest/factory.d.ts +4 -4
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +11 -9
- package/dest/index.d.ts +8 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +7 -1
- package/dest/l1/calldata_retriever.d.ts +2 -1
- package/dest/l1/calldata_retriever.d.ts.map +1 -1
- package/dest/l1/calldata_retriever.js +9 -4
- package/dest/modules/contract_data_source_adapter.d.ts +25 -0
- package/dest/modules/contract_data_source_adapter.d.ts.map +1 -0
- package/dest/modules/contract_data_source_adapter.js +42 -0
- package/dest/modules/data_source_base.d.ts +16 -10
- package/dest/modules/data_source_base.d.ts.map +1 -1
- package/dest/modules/data_source_base.js +71 -60
- package/dest/modules/data_store_updater.d.ts +6 -6
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +42 -40
- package/dest/modules/l1_synchronizer.d.ts +5 -4
- package/dest/modules/l1_synchronizer.d.ts.map +1 -1
- package/dest/modules/l1_synchronizer.js +79 -54
- package/dest/modules/validation.d.ts +4 -3
- package/dest/modules/validation.d.ts.map +1 -1
- package/dest/modules/validation.js +4 -4
- package/dest/store/block_store.d.ts +58 -27
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +152 -75
- package/dest/store/contract_class_store.d.ts +17 -3
- package/dest/store/contract_class_store.d.ts.map +1 -1
- package/dest/store/contract_class_store.js +17 -1
- package/dest/store/contract_instance_store.d.ts +28 -1
- package/dest/store/contract_instance_store.d.ts.map +1 -1
- package/dest/store/contract_instance_store.js +31 -0
- package/dest/store/data_stores.d.ts +68 -0
- package/dest/store/data_stores.d.ts.map +1 -0
- package/dest/store/data_stores.js +50 -0
- package/dest/store/function_names_cache.d.ts +17 -0
- package/dest/store/function_names_cache.d.ts.map +1 -0
- package/dest/store/function_names_cache.js +30 -0
- package/dest/store/l2_tips_cache.js +1 -1
- package/dest/test/fake_l1_state.d.ts +2 -1
- package/dest/test/fake_l1_state.d.ts.map +1 -1
- package/dest/test/fake_l1_state.js +25 -8
- package/dest/test/mock_l2_block_source.d.ts +12 -3
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +24 -2
- package/dest/test/noop_l1_archiver.d.ts +4 -4
- package/dest/test/noop_l1_archiver.d.ts.map +1 -1
- package/dest/test/noop_l1_archiver.js +5 -5
- package/package.json +13 -13
- package/src/archiver.ts +91 -49
- package/src/config.ts +2 -1
- package/src/errors.ts +41 -8
- package/src/factory.ts +10 -10
- package/src/index.ts +15 -1
- package/src/l1/calldata_retriever.ts +15 -4
- package/src/modules/contract_data_source_adapter.ts +59 -0
- package/src/modules/data_source_base.ts +75 -57
- package/src/modules/data_store_updater.ts +46 -38
- package/src/modules/l1_synchronizer.ts +92 -60
- package/src/modules/validation.ts +8 -7
- package/src/store/block_store.ts +159 -80
- package/src/store/contract_class_store.ts +28 -2
- package/src/store/contract_instance_store.ts +43 -0
- package/src/store/data_stores.ts +108 -0
- package/src/store/function_names_cache.ts +37 -0
- package/src/store/l2_tips_cache.ts +1 -1
- package/src/test/fake_l1_state.ts +24 -14
- package/src/test/mock_l2_block_source.ts +23 -2
- package/src/test/noop_l1_archiver.ts +6 -6
- package/dest/store/kv_archiver_store.d.ts +0 -383
- package/dest/store/kv_archiver_store.d.ts.map +0 -1
- package/dest/store/kv_archiver_store.js +0 -501
- package/src/store/kv_archiver_store.ts +0 -728
|
@@ -10,6 +10,7 @@ import { BlockNumber, CheckpointNumber, EpochNumber } from '@aztec/foundation/br
|
|
|
10
10
|
import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
|
|
11
11
|
import { partition, pick } from '@aztec/foundation/collection';
|
|
12
12
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
13
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
13
14
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
14
15
|
import { retryTimes } from '@aztec/foundation/retry';
|
|
15
16
|
import { count } from '@aztec/foundation/string';
|
|
@@ -19,6 +20,7 @@ import { type ArchiverEmitter, L2BlockSourceEvents, type ValidateCheckpointResul
|
|
|
19
20
|
import { Checkpoint, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
20
21
|
import { type L1RollupConstants, getEpochAtSlot, getSlotAtNextL1Block } from '@aztec/stdlib/epoch-helpers';
|
|
21
22
|
import { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
|
|
23
|
+
import type { CoordinationSignatureContext } from '@aztec/stdlib/p2p';
|
|
22
24
|
import { type Traceable, type Tracer, execInSpan, trackSpan } from '@aztec/telemetry-client';
|
|
23
25
|
|
|
24
26
|
import { InitialCheckpointNumberNotSequentialError } from '../errors.js';
|
|
@@ -30,7 +32,7 @@ import {
|
|
|
30
32
|
retrieveL1ToL2Messages,
|
|
31
33
|
retrievedToPublishedCheckpoint,
|
|
32
34
|
} from '../l1/data_retrieval.js';
|
|
33
|
-
import type
|
|
35
|
+
import { type ArchiverDataStores, getArchiverSynchPoint } from '../store/data_stores.js';
|
|
34
36
|
import type { L2TipsCache } from '../store/l2_tips_cache.js';
|
|
35
37
|
import { MessageStoreError } from '../store/message_store.js';
|
|
36
38
|
import type { InboxMessage } from '../structs/inbox_message.js';
|
|
@@ -65,7 +67,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
65
67
|
private readonly debugClient: ViemPublicDebugClient,
|
|
66
68
|
private readonly rollup: RollupContract,
|
|
67
69
|
private readonly inbox: InboxContract,
|
|
68
|
-
private readonly
|
|
70
|
+
private readonly stores: ArchiverDataStores,
|
|
69
71
|
private config: {
|
|
70
72
|
batchSize: number;
|
|
71
73
|
skipValidateCheckpointAttestations?: boolean;
|
|
@@ -85,7 +87,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
85
87
|
l2TipsCache?: L2TipsCache,
|
|
86
88
|
private readonly log: Logger = createLogger('archiver:l1-sync'),
|
|
87
89
|
) {
|
|
88
|
-
this.updater = new ArchiverDataStoreUpdater(this.
|
|
90
|
+
this.updater = new ArchiverDataStoreUpdater(this.stores, l2TipsCache, {
|
|
89
91
|
rollupManaLimit: l1Constants.rollupManaLimit,
|
|
90
92
|
});
|
|
91
93
|
this.tracer = tracer;
|
|
@@ -111,6 +113,13 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
111
113
|
return this.l1Timestamp;
|
|
112
114
|
}
|
|
113
115
|
|
|
116
|
+
private getSignatureContext(): CoordinationSignatureContext {
|
|
117
|
+
return {
|
|
118
|
+
chainId: this.publicClient.chain.id,
|
|
119
|
+
rollupAddress: EthAddress.fromString(this.rollup.address),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
114
123
|
/** Checks that the ethereum node we are connected to has a latest timestamp no more than the allowed drift. Throw if not. */
|
|
115
124
|
public async testEthereumNodeSynced(): Promise<void> {
|
|
116
125
|
const maxAllowedDelay = this.config.maxAllowedEthClientDriftSeconds;
|
|
@@ -154,7 +163,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
154
163
|
}
|
|
155
164
|
|
|
156
165
|
// Load sync point for blocks defaulting to start block
|
|
157
|
-
const { blocksSynchedTo = this.l1Constants.l1StartBlock } = await this.
|
|
166
|
+
const { blocksSynchedTo = this.l1Constants.l1StartBlock } = await getArchiverSynchPoint(this.stores);
|
|
158
167
|
this.log.debug(`Starting new archiver sync iteration`, { blocksSynchedTo, currentL1BlockData });
|
|
159
168
|
|
|
160
169
|
// Sync L1 to L2 messages. We retry this a few times since there are error conditions that reset the sync point, requiring a new iteration.
|
|
@@ -185,7 +194,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
185
194
|
// past it, since otherwise we'll keep downloading it and reprocessing it on every iteration until
|
|
186
195
|
// we get a valid checkpoint to advance the syncpoint.
|
|
187
196
|
if (!rollupStatus.validationResult?.valid && rollupStatus.lastL1BlockWithCheckpoint !== undefined) {
|
|
188
|
-
await this.
|
|
197
|
+
await this.stores.blocks.setSynchedL1BlockNumber(rollupStatus.lastL1BlockWithCheckpoint);
|
|
189
198
|
}
|
|
190
199
|
|
|
191
200
|
// And lastly we check if we are missing any checkpoints behind us due to a possible L1 reorg.
|
|
@@ -229,7 +238,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
229
238
|
const finalizedCheckpointNumber = await this.rollup.getProvenCheckpointNumber({
|
|
230
239
|
blockNumber: finalizedL1BlockNumber,
|
|
231
240
|
});
|
|
232
|
-
const localFinalizedCheckpointNumber = await this.
|
|
241
|
+
const localFinalizedCheckpointNumber = await this.stores.blocks.getFinalizedCheckpointNumber();
|
|
233
242
|
if (localFinalizedCheckpointNumber !== finalizedCheckpointNumber) {
|
|
234
243
|
await this.updater.setFinalizedCheckpointNumber(finalizedCheckpointNumber);
|
|
235
244
|
this.log.info(`Updated finalized chain to checkpoint ${finalizedCheckpointNumber}`, {
|
|
@@ -248,8 +257,8 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
248
257
|
/** Prune all proposed local blocks that should have been checkpointed by now. */
|
|
249
258
|
private async pruneUncheckpointedBlocks(currentL1Timestamp: bigint) {
|
|
250
259
|
const [lastCheckpointedBlockNumber, lastProposedBlockNumber] = await Promise.all([
|
|
251
|
-
this.
|
|
252
|
-
this.
|
|
260
|
+
this.stores.blocks.getCheckpointedL2BlockNumber(),
|
|
261
|
+
this.stores.blocks.getLatestL2BlockNumber(),
|
|
253
262
|
]);
|
|
254
263
|
|
|
255
264
|
// If there are no uncheckpointed blocks, we got nothing to do
|
|
@@ -263,7 +272,10 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
263
272
|
const firstUncheckpointedBlockNumber = BlockNumber(lastCheckpointedBlockNumber + 1);
|
|
264
273
|
|
|
265
274
|
// What's the slot of the first uncheckpointed block?
|
|
266
|
-
const [firstUncheckpointedBlockHeader] = await this.
|
|
275
|
+
const [firstUncheckpointedBlockHeader] = await this.stores.blocks.getBlockHeaders(
|
|
276
|
+
firstUncheckpointedBlockNumber,
|
|
277
|
+
1,
|
|
278
|
+
);
|
|
267
279
|
const firstUncheckpointedBlockSlot = firstUncheckpointedBlockHeader?.getSlot();
|
|
268
280
|
|
|
269
281
|
if (firstUncheckpointedBlockSlot === undefined || firstUncheckpointedBlockSlot >= slotAtNextL1Block) {
|
|
@@ -310,7 +322,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
310
322
|
currentL1Timestamp: bigint,
|
|
311
323
|
): Promise<{ rollupCanPrune: boolean }> {
|
|
312
324
|
const rollupCanPrune = await this.canPrune(currentL1BlockNumber, currentL1Timestamp);
|
|
313
|
-
const localPendingCheckpointNumber = await this.
|
|
325
|
+
const localPendingCheckpointNumber = await this.stores.blocks.getLatestCheckpointNumber();
|
|
314
326
|
const canPrune = localPendingCheckpointNumber > provenCheckpointNumber && rollupCanPrune;
|
|
315
327
|
|
|
316
328
|
if (canPrune) {
|
|
@@ -331,12 +343,12 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
331
343
|
// promises when the gap between local pending and proven checkpoint numbers is large.
|
|
332
344
|
const BATCH_SIZE = 10;
|
|
333
345
|
const indices = Array.from({ length: checkpointsToUnwind }, (_, i) => CheckpointNumber(i + pruneFrom));
|
|
334
|
-
const checkpoints = (
|
|
335
|
-
|
|
336
|
-
);
|
|
346
|
+
const checkpoints = (
|
|
347
|
+
await asyncPool(BATCH_SIZE, indices, idx => this.stores.blocks.getCheckpointData(idx))
|
|
348
|
+
).filter(isDefined);
|
|
337
349
|
const newBlocks = (
|
|
338
350
|
await asyncPool(BATCH_SIZE, checkpoints, cp =>
|
|
339
|
-
this.
|
|
351
|
+
this.stores.blocks.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber)),
|
|
340
352
|
)
|
|
341
353
|
)
|
|
342
354
|
.filter(isDefined)
|
|
@@ -356,12 +368,12 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
356
368
|
this.log.warn(
|
|
357
369
|
`Removed ${count(checkpointsToUnwind, 'checkpoint')} after checkpoint ${provenCheckpointNumber} ` +
|
|
358
370
|
`due to predicted reorg at L1 block ${currentL1BlockNumber}. ` +
|
|
359
|
-
`Updated latest checkpoint is ${await this.
|
|
371
|
+
`Updated latest checkpoint is ${await this.stores.blocks.getLatestCheckpointNumber()}.`,
|
|
360
372
|
);
|
|
361
373
|
this.instrumentation.processPrune(timer.ms());
|
|
362
374
|
// TODO(palla/reorg): Do we need to set the block synched L1 block number here?
|
|
363
375
|
// Seems like the next iteration should handle this.
|
|
364
|
-
// await this.
|
|
376
|
+
// await this.stores.blocks.setSynchedL1BlockNumber(currentL1BlockNumber);
|
|
365
377
|
}
|
|
366
378
|
|
|
367
379
|
return { rollupCanPrune };
|
|
@@ -385,7 +397,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
385
397
|
l1BlockNumber: this.l1Constants.l1StartBlock,
|
|
386
398
|
l1BlockHash: this.l1Constants.l1StartBlockHash,
|
|
387
399
|
},
|
|
388
|
-
} = await this.
|
|
400
|
+
} = await getArchiverSynchPoint(this.stores);
|
|
389
401
|
|
|
390
402
|
// Nothing to do if L1 block number has not moved forward
|
|
391
403
|
const currentL1BlockNumber = currentL1Block.l1BlockNumber;
|
|
@@ -395,10 +407,10 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
395
407
|
|
|
396
408
|
// Compare local message store state with the remote. If they match, we just advance the match pointer.
|
|
397
409
|
const remoteMessagesState = await this.inbox.getState({ blockNumber: currentL1BlockNumber });
|
|
398
|
-
const localLastMessage = await this.
|
|
410
|
+
const localLastMessage = await this.stores.messages.getLastMessage();
|
|
399
411
|
if (await this.localStateMatches(localLastMessage, remoteMessagesState)) {
|
|
400
412
|
this.log.trace(`Local L1 to L2 messages are already in sync with remote at L1 block ${currentL1BlockNumber}`);
|
|
401
|
-
await this.
|
|
413
|
+
await this.stores.messages.setMessageSyncState(currentL1Block, remoteMessagesState.treeInProgress);
|
|
402
414
|
return true;
|
|
403
415
|
}
|
|
404
416
|
|
|
@@ -423,7 +435,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
423
435
|
// Note that, if there are no new messages to insert, but there was an L1 reorg that pruned out last messages,
|
|
424
436
|
// we'd notice by comparing our local state with the remote one again, and seeing they don't match even after
|
|
425
437
|
// our sync attempt. In this case, we also rollback our syncpoint, and trigger a retry.
|
|
426
|
-
const localLastMessageAfterSync = await this.
|
|
438
|
+
const localLastMessageAfterSync = await this.stores.messages.getLastMessage();
|
|
427
439
|
if (!(await this.localStateMatches(localLastMessageAfterSync, remoteMessagesState))) {
|
|
428
440
|
this.log.warn(
|
|
429
441
|
`Local L1 to L2 messages state does not match remote after sync attempt. Rolling back syncpoint to retry.`,
|
|
@@ -434,13 +446,13 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
434
446
|
}
|
|
435
447
|
|
|
436
448
|
// Advance the syncpoint after a successful sync
|
|
437
|
-
await this.
|
|
449
|
+
await this.stores.messages.setMessageSyncState(currentL1Block, remoteMessagesState.treeInProgress);
|
|
438
450
|
return true;
|
|
439
451
|
}
|
|
440
452
|
|
|
441
453
|
/** Checks if the local rolling hash and message count matches the remote state */
|
|
442
454
|
private async localStateMatches(localLastMessage: InboxMessage | undefined, remoteState: InboxContractState) {
|
|
443
|
-
const localMessageCount = await this.
|
|
455
|
+
const localMessageCount = await this.stores.messages.getTotalL1ToL2MessageCount();
|
|
444
456
|
this.log.trace(`Comparing local and remote inbox state`, { localMessageCount, localLastMessage, remoteState });
|
|
445
457
|
|
|
446
458
|
return (
|
|
@@ -462,7 +474,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
462
474
|
this.log.trace(`Retrieving L1 to L2 messages in L1 blocks ${searchStartBlock}-${searchEndBlock}`);
|
|
463
475
|
const messages = await retrieveL1ToL2Messages(this.inbox, searchStartBlock, searchEndBlock);
|
|
464
476
|
const timer = new Timer();
|
|
465
|
-
await this.
|
|
477
|
+
await this.stores.messages.addL1ToL2Messages(messages);
|
|
466
478
|
const perMsg = timer.ms() / messages.length;
|
|
467
479
|
this.instrumentation.processNewMessages(messages.length, perMsg);
|
|
468
480
|
for (const msg of messages) {
|
|
@@ -493,7 +505,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
493
505
|
let commonMsg: undefined | InboxMessage;
|
|
494
506
|
let messagesToDelete = 0;
|
|
495
507
|
this.log.verbose(`Searching most recent common L1 to L2 message`);
|
|
496
|
-
for await (const localMsg of this.
|
|
508
|
+
for await (const localMsg of this.stores.messages.iterateL1ToL2Messages({ reverse: true })) {
|
|
497
509
|
const logCtx = { remoteMsg: undefined as InboxMessage | undefined, localMsg, remoteMessagesState };
|
|
498
510
|
|
|
499
511
|
// First check if the local message rolling hash matches the current rolling hash of the inbox contract,
|
|
@@ -536,7 +548,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
536
548
|
if (messagesToDelete > 0) {
|
|
537
549
|
const lastGoodIndex = commonMsg?.index;
|
|
538
550
|
this.log.warn(`Rolling back all local L1 to L2 messages after index ${lastGoodIndex ?? 'initial'}`);
|
|
539
|
-
await this.
|
|
551
|
+
await this.stores.messages.removeL1ToL2Messages(lastGoodIndex !== undefined ? lastGoodIndex + 1n : 0n);
|
|
540
552
|
}
|
|
541
553
|
|
|
542
554
|
// Update the syncpoint so the loop below reprocesses the changed messages. We go to the block before
|
|
@@ -545,7 +557,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
545
557
|
const syncPointL1BlockNumber = commonMsg ? commonMsg.l1BlockNumber - 1n : this.l1Constants.l1StartBlock;
|
|
546
558
|
const syncPointL1BlockHash = await this.getL1BlockHash(syncPointL1BlockNumber);
|
|
547
559
|
const messagesSyncPoint = { l1BlockNumber: syncPointL1BlockNumber, l1BlockHash: syncPointL1BlockHash };
|
|
548
|
-
await this.
|
|
560
|
+
await this.stores.messages.setMessageSyncState(messagesSyncPoint, remoteTreeInProgress);
|
|
549
561
|
this.log.verbose(`Updated messages syncpoint to L1 block ${syncPointL1BlockNumber}`, {
|
|
550
562
|
...messagesSyncPoint,
|
|
551
563
|
remoteTreeInProgress,
|
|
@@ -567,9 +579,9 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
567
579
|
currentL1BlockNumber: bigint,
|
|
568
580
|
initialSyncComplete: boolean,
|
|
569
581
|
): Promise<RollupStatus> {
|
|
570
|
-
const localPendingCheckpointNumber = await this.
|
|
582
|
+
const localPendingCheckpointNumber = await this.stores.blocks.getLatestCheckpointNumber();
|
|
571
583
|
const initialValidationResult: ValidateCheckpointResult | undefined =
|
|
572
|
-
await this.
|
|
584
|
+
await this.stores.blocks.getPendingChainValidationStatus();
|
|
573
585
|
const {
|
|
574
586
|
provenCheckpointNumber,
|
|
575
587
|
provenArchive,
|
|
@@ -599,7 +611,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
599
611
|
// we need to set it to zero. This is an edge case because we dont have a checkpoint zero (initial checkpoint is one),
|
|
600
612
|
// so localCheckpointForDestinationProvenCheckpointNumber would not be found below.
|
|
601
613
|
if (provenCheckpointNumber === 0) {
|
|
602
|
-
const localProvenCheckpointNumber = await this.
|
|
614
|
+
const localProvenCheckpointNumber = await this.stores.blocks.getProvenCheckpointNumber();
|
|
603
615
|
if (localProvenCheckpointNumber !== provenCheckpointNumber) {
|
|
604
616
|
await this.updater.setProvenCheckpointNumber(provenCheckpointNumber);
|
|
605
617
|
this.log.info(`Rolled back proven chain to checkpoint ${provenCheckpointNumber}`, { provenCheckpointNumber });
|
|
@@ -607,11 +619,11 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
607
619
|
}
|
|
608
620
|
|
|
609
621
|
const localCheckpointForDestinationProvenCheckpointNumber =
|
|
610
|
-
await this.
|
|
622
|
+
await this.stores.blocks.getCheckpointData(provenCheckpointNumber);
|
|
611
623
|
|
|
612
624
|
// Sanity check. I've hit what seems to be a state where the proven checkpoint is set to a value greater than the latest
|
|
613
625
|
// synched checkpoint when requesting L2Tips from the archiver. This is the only place where the proven checkpoint is set.
|
|
614
|
-
const synched = await this.
|
|
626
|
+
const synched = await this.stores.blocks.getLatestCheckpointNumber();
|
|
615
627
|
if (
|
|
616
628
|
localCheckpointForDestinationProvenCheckpointNumber &&
|
|
617
629
|
synched < localCheckpointForDestinationProvenCheckpointNumber.checkpointNumber
|
|
@@ -631,7 +643,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
631
643
|
localCheckpointForDestinationProvenCheckpointNumber &&
|
|
632
644
|
provenArchive.equals(localCheckpointForDestinationProvenCheckpointNumber.archive.root)
|
|
633
645
|
) {
|
|
634
|
-
const localProvenCheckpointNumber = await this.
|
|
646
|
+
const localProvenCheckpointNumber = await this.stores.blocks.getProvenCheckpointNumber();
|
|
635
647
|
if (localProvenCheckpointNumber !== provenCheckpointNumber) {
|
|
636
648
|
await this.updater.setProvenCheckpointNumber(provenCheckpointNumber);
|
|
637
649
|
this.log.info(`Updated proven chain to checkpoint ${provenCheckpointNumber}`, { provenCheckpointNumber });
|
|
@@ -659,7 +671,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
659
671
|
// If we have 0 checkpoints locally and there are no checkpoints onchain there is nothing to do.
|
|
660
672
|
const noCheckpoints = localPendingCheckpointNumber === 0 && pendingCheckpointNumber === 0;
|
|
661
673
|
if (noCheckpoints) {
|
|
662
|
-
await this.
|
|
674
|
+
await this.stores.blocks.setSynchedL1BlockNumber(currentL1BlockNumber);
|
|
663
675
|
this.log.debug(
|
|
664
676
|
`No checkpoints to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}, no checkpoints on chain`,
|
|
665
677
|
);
|
|
@@ -671,7 +683,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
671
683
|
// Related to the L2 reorgs of the pending chain. We are only interested in actually addressing a reorg if there
|
|
672
684
|
// are any state that could be impacted by it. If we have no checkpoints, there is no impact.
|
|
673
685
|
if (localPendingCheckpointNumber > 0) {
|
|
674
|
-
const localPendingCheckpoint = await this.
|
|
686
|
+
const localPendingCheckpoint = await this.stores.blocks.getCheckpointData(localPendingCheckpointNumber);
|
|
675
687
|
if (localPendingCheckpoint === undefined) {
|
|
676
688
|
throw new Error(`Missing checkpoint ${localPendingCheckpointNumber}`);
|
|
677
689
|
}
|
|
@@ -686,7 +698,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
686
698
|
// However, in the re-org scenario, our L1 node is temporarily lying to us and we end up potentially missing checkpoints.
|
|
687
699
|
// We must only set this block number based on actually retrieved logs.
|
|
688
700
|
// TODO(#8621): Tackle this properly when we handle L1 Re-orgs.
|
|
689
|
-
// await this.
|
|
701
|
+
// await this.stores.blocks.setSynchedL1BlockNumber(currentL1BlockNumber);
|
|
690
702
|
this.log.debug(`No checkpoints to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
|
|
691
703
|
return rollupStatus;
|
|
692
704
|
}
|
|
@@ -706,7 +718,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
706
718
|
|
|
707
719
|
let tipAfterUnwind = localPendingCheckpointNumber;
|
|
708
720
|
while (true) {
|
|
709
|
-
const candidateCheckpoint = await this.
|
|
721
|
+
const candidateCheckpoint = await this.stores.blocks.getCheckpointData(tipAfterUnwind);
|
|
710
722
|
if (candidateCheckpoint === undefined) {
|
|
711
723
|
break;
|
|
712
724
|
}
|
|
@@ -731,7 +743,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
731
743
|
this.log.warn(
|
|
732
744
|
`Removed ${count(checkpointsToRemove, 'checkpoint')} after checkpoint ${tipAfterUnwind} ` +
|
|
733
745
|
`due to mismatched checkpoint hashes at L1 block ${currentL1BlockNumber}. ` +
|
|
734
|
-
`Updated L2 latest checkpoint is ${await this.
|
|
746
|
+
`Updated L2 latest checkpoint is ${await this.stores.blocks.getLatestCheckpointNumber()}.`,
|
|
735
747
|
);
|
|
736
748
|
}
|
|
737
749
|
}
|
|
@@ -777,11 +789,14 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
777
789
|
},
|
|
778
790
|
);
|
|
779
791
|
|
|
780
|
-
// Check if the last checkpoint matches
|
|
781
|
-
// We only check the last one
|
|
782
|
-
//
|
|
792
|
+
// Check if the last checkpoint matches a local pending entry (so we can skip blob fetch).
|
|
793
|
+
// We only check the last one; if it matches, the blob fetch is skipped for that entry.
|
|
794
|
+
// TODO(palla/pipelining): We may have more than a single checkpoint to promote
|
|
783
795
|
const lastCalldataCheckpoint = calldataCheckpoints[calldataCheckpoints.length - 1];
|
|
784
|
-
const
|
|
796
|
+
const promoteResult = await this.tryBuildPublishedCheckpointFromProposed(lastCalldataCheckpoint);
|
|
797
|
+
const checkpointToPromote = promoteResult && !('diverged' in promoteResult) ? promoteResult : undefined;
|
|
798
|
+
const evictProposedFrom =
|
|
799
|
+
promoteResult && 'diverged' in promoteResult ? promoteResult.fromCheckpointNumber : undefined;
|
|
785
800
|
|
|
786
801
|
// Then fetch blobs in parallel and build the full published checkpoints
|
|
787
802
|
const toFetchBlobs = checkpointToPromote ? calldataCheckpoints.slice(0, -1) : calldataCheckpoints;
|
|
@@ -809,7 +824,13 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
809
824
|
for (const published of publishedCheckpoints) {
|
|
810
825
|
const validationResult = this.config.skipValidateCheckpointAttestations
|
|
811
826
|
? { valid: true as const }
|
|
812
|
-
: await validateCheckpointAttestations(
|
|
827
|
+
: await validateCheckpointAttestations(
|
|
828
|
+
published,
|
|
829
|
+
this.epochCache,
|
|
830
|
+
this.l1Constants,
|
|
831
|
+
this.getSignatureContext(),
|
|
832
|
+
this.log,
|
|
833
|
+
);
|
|
813
834
|
|
|
814
835
|
// Only update the validation result if it has changed, so we can keep track of the first invalid checkpoint
|
|
815
836
|
// in case there is a sequence of more than one invalid checkpoint, as we need to invalidate the first one.
|
|
@@ -847,7 +868,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
847
868
|
// Check the inHash of the checkpoint against the l1->l2 messages.
|
|
848
869
|
// The messages should've been synced up to the currentL1BlockNumber and must be available for the published
|
|
849
870
|
// checkpoints we just retrieved.
|
|
850
|
-
const l1ToL2Messages = await this.
|
|
871
|
+
const l1ToL2Messages = await this.stores.messages.getL1ToL2Messages(published.checkpoint.number);
|
|
851
872
|
const computedInHash = computeInHashFromL1ToL2Messages(l1ToL2Messages);
|
|
852
873
|
const publishedInHash = published.checkpoint.header.inHash;
|
|
853
874
|
if (!computedInHash.equals(publishedInHash)) {
|
|
@@ -904,6 +925,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
904
925
|
attestations: lastCalldataCheckpoint.attestations,
|
|
905
926
|
checkpoint: maybeValidCheckpointToPromote,
|
|
906
927
|
},
|
|
928
|
+
evictProposedFrom,
|
|
907
929
|
),
|
|
908
930
|
),
|
|
909
931
|
);
|
|
@@ -938,10 +960,10 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
938
960
|
if (err instanceof InitialCheckpointNumberNotSequentialError) {
|
|
939
961
|
const { previousCheckpointNumber, newCheckpointNumber } = err;
|
|
940
962
|
const previousCheckpoint = previousCheckpointNumber
|
|
941
|
-
? await this.
|
|
963
|
+
? await this.stores.blocks.getCheckpointData(CheckpointNumber(previousCheckpointNumber))
|
|
942
964
|
: undefined;
|
|
943
965
|
const updatedL1SyncPoint = previousCheckpoint?.l1.blockNumber ?? this.l1Constants.l1StartBlock;
|
|
944
|
-
await this.
|
|
966
|
+
await this.stores.blocks.setSynchedL1BlockNumber(updatedL1SyncPoint);
|
|
945
967
|
this.log.warn(
|
|
946
968
|
`Attempting to insert checkpoint ${newCheckpointNumber} with previous block ${previousCheckpointNumber}. Rolling back L1 sync point to ${updatedL1SyncPoint} to try and fetch the missing blocks.`,
|
|
947
969
|
{
|
|
@@ -975,17 +997,24 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
975
997
|
return { ...rollupStatus, lastRetrievedCheckpoint, lastL1BlockWithCheckpoint };
|
|
976
998
|
}
|
|
977
999
|
|
|
978
|
-
/**
|
|
1000
|
+
/**
|
|
1001
|
+
* Checks if a specific checkpoint matches a local pending entry, and if so, loads local data to build
|
|
1002
|
+
* a synthetic published checkpoint (skipping blob fetch).
|
|
1003
|
+
*
|
|
1004
|
+
* Returns { diverged: true, fromCheckpointNumber } when the L1 checkpoint does NOT match local pending
|
|
1005
|
+
* data for that number, so the caller can evict the entire pending suffix >= fromCheckpointNumber
|
|
1006
|
+
* (those entries chain off the now-invalid local state) within the same addCheckpoints transaction.
|
|
1007
|
+
*/
|
|
979
1008
|
private async tryBuildPublishedCheckpointFromProposed(
|
|
980
1009
|
calldataCheckpoint: RetrievedCheckpointFromCalldata | undefined,
|
|
981
|
-
): Promise<PublishedCheckpoint | undefined> {
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
) {
|
|
1010
|
+
): Promise<PublishedCheckpoint | { diverged: true; fromCheckpointNumber: CheckpointNumber } | undefined> {
|
|
1011
|
+
if (this.config.skipPromoteProposedCheckpointDuringL1Sync || !calldataCheckpoint) {
|
|
1012
|
+
return undefined;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// Look up the specific pending entry for the checkpoint being mined, not just the tip
|
|
1016
|
+
const proposed = await this.stores.blocks.getProposedCheckpointByNumber(calldataCheckpoint.checkpointNumber);
|
|
1017
|
+
if (!proposed) {
|
|
989
1018
|
return undefined;
|
|
990
1019
|
}
|
|
991
1020
|
|
|
@@ -1004,7 +1033,8 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
1004
1033
|
calldataArchiveRoot: calldataCheckpoint.archiveRoot.toString(),
|
|
1005
1034
|
},
|
|
1006
1035
|
);
|
|
1007
|
-
|
|
1036
|
+
// Return a divergence signal so the caller can evict pending >= this number
|
|
1037
|
+
return { diverged: true, fromCheckpointNumber: proposed.checkpointNumber };
|
|
1008
1038
|
}
|
|
1009
1039
|
|
|
1010
1040
|
this.log.debug(
|
|
@@ -1012,7 +1042,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
1012
1042
|
{ proposedHeader: proposed.header.toInspect(), proposedArchiveRoot: proposed.archive.root.toString() },
|
|
1013
1043
|
);
|
|
1014
1044
|
|
|
1015
|
-
const blocks = await this.
|
|
1045
|
+
const blocks = await this.stores.blocks.getBlocks(BlockNumber(proposed.startBlock), proposed.blockCount);
|
|
1016
1046
|
if (blocks.length !== proposed.blockCount) {
|
|
1017
1047
|
this.log.warn(
|
|
1018
1048
|
`Local proposed checkpoint ${proposed.checkpointNumber} has wrong block count (expected ${proposed.blockCount} blocks starting at ${proposed.startBlock} but got ${blocks.length})`,
|
|
@@ -1052,7 +1082,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
1052
1082
|
// Compare the last checkpoint we have (either retrieved in this round or loaded from store) with what the
|
|
1053
1083
|
// rollup contract told us was the latest one (pinned at the currentL1BlockNumber).
|
|
1054
1084
|
const latestLocalCheckpointNumber =
|
|
1055
|
-
lastRetrievedCheckpoint?.checkpoint.number ?? (await this.
|
|
1085
|
+
lastRetrievedCheckpoint?.checkpoint.number ?? (await this.stores.blocks.getLatestCheckpointNumber());
|
|
1056
1086
|
if (latestLocalCheckpointNumber < pendingCheckpointNumber) {
|
|
1057
1087
|
// Here we have consumed all logs until the `currentL1Block` we pinned at the beginning of the archiver loop,
|
|
1058
1088
|
// but still haven't reached the pending checkpoint according to the call to the rollup contract.
|
|
@@ -1065,7 +1095,9 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
1065
1095
|
latestLocalCheckpointArchive = lastRetrievedCheckpoint.checkpoint.archive.root.toString();
|
|
1066
1096
|
targetL1BlockNumber = lastRetrievedCheckpoint.l1.blockNumber;
|
|
1067
1097
|
} else if (latestLocalCheckpointNumber > 0) {
|
|
1068
|
-
const checkpoint = await this.
|
|
1098
|
+
const checkpoint = await this.stores.blocks
|
|
1099
|
+
.getRangeOfCheckpoints(latestLocalCheckpointNumber, 1)
|
|
1100
|
+
.then(([c]) => c);
|
|
1069
1101
|
latestLocalCheckpointArchive = checkpoint.archive.root.toString();
|
|
1070
1102
|
targetL1BlockNumber = checkpoint.l1.blockNumber;
|
|
1071
1103
|
}
|
|
@@ -1080,7 +1112,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
1080
1112
|
...status,
|
|
1081
1113
|
},
|
|
1082
1114
|
);
|
|
1083
|
-
await this.
|
|
1115
|
+
await this.stores.blocks.setSynchedL1BlockNumber(targetL1BlockNumber);
|
|
1084
1116
|
} else {
|
|
1085
1117
|
this.log.trace(`No new checkpoints behind L1 sync point to retrieve.`, {
|
|
1086
1118
|
latestLocalCheckpointNumber,
|
|
@@ -1090,7 +1122,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
1090
1122
|
}
|
|
1091
1123
|
|
|
1092
1124
|
private async getCheckpointHeader(number: CheckpointNumber) {
|
|
1093
|
-
const checkpoint = await this.
|
|
1125
|
+
const checkpoint = await this.stores.blocks.getCheckpointData(number);
|
|
1094
1126
|
if (!checkpoint) {
|
|
1095
1127
|
return undefined;
|
|
1096
1128
|
}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from '@aztec/stdlib/block';
|
|
11
11
|
import type { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
12
12
|
import { type L1RollupConstants, computeQuorum, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
13
|
-
import { ConsensusPayload } from '@aztec/stdlib/p2p';
|
|
13
|
+
import { ConsensusPayload, type CoordinationSignatureContext } from '@aztec/stdlib/p2p';
|
|
14
14
|
|
|
15
15
|
export type { ValidateCheckpointResult };
|
|
16
16
|
|
|
@@ -18,11 +18,11 @@ export type { ValidateCheckpointResult };
|
|
|
18
18
|
* Extracts attestation information from a published checkpoint.
|
|
19
19
|
* Returns info for each attestation, preserving array indices.
|
|
20
20
|
*/
|
|
21
|
-
export function getAttestationInfoFromPublishedCheckpoint(
|
|
22
|
-
checkpoint,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const payload = ConsensusPayload.fromCheckpoint(checkpoint);
|
|
21
|
+
export function getAttestationInfoFromPublishedCheckpoint(
|
|
22
|
+
{ checkpoint, attestations }: PublishedCheckpoint,
|
|
23
|
+
signatureContext: CoordinationSignatureContext,
|
|
24
|
+
): AttestationInfo[] {
|
|
25
|
+
const payload = ConsensusPayload.fromCheckpoint(checkpoint, signatureContext);
|
|
26
26
|
return getAttestationInfoFromPayload(payload, attestations);
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -34,9 +34,10 @@ export async function validateCheckpointAttestations(
|
|
|
34
34
|
publishedCheckpoint: PublishedCheckpoint,
|
|
35
35
|
epochCache: EpochCache,
|
|
36
36
|
constants: Pick<L1RollupConstants, 'epochDuration'>,
|
|
37
|
+
signatureContext: CoordinationSignatureContext,
|
|
37
38
|
logger?: Logger,
|
|
38
39
|
): Promise<ValidateCheckpointResult> {
|
|
39
|
-
const attestorInfos = getAttestationInfoFromPublishedCheckpoint(publishedCheckpoint);
|
|
40
|
+
const attestorInfos = getAttestationInfoFromPublishedCheckpoint(publishedCheckpoint, signatureContext);
|
|
40
41
|
const attestors = compactArray(attestorInfos.map(info => ('address' in info ? info.address : undefined)));
|
|
41
42
|
const { checkpoint, attestations } = publishedCheckpoint;
|
|
42
43
|
const headerHash = checkpoint.header.hash();
|