@aztec/archiver 0.0.1-commit.2b2662070 → 0.0.1-commit.2c0ee1788

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dest/archiver.d.ts +17 -10
  2. package/dest/archiver.d.ts.map +1 -1
  3. package/dest/archiver.js +92 -53
  4. package/dest/config.d.ts +3 -1
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +14 -3
  7. package/dest/errors.d.ts +32 -5
  8. package/dest/errors.d.ts.map +1 -1
  9. package/dest/errors.js +51 -6
  10. package/dest/factory.d.ts +4 -4
  11. package/dest/factory.d.ts.map +1 -1
  12. package/dest/factory.js +13 -10
  13. package/dest/index.d.ts +10 -3
  14. package/dest/index.d.ts.map +1 -1
  15. package/dest/index.js +9 -2
  16. package/dest/l1/calldata_retriever.d.ts +2 -1
  17. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  18. package/dest/l1/calldata_retriever.js +9 -4
  19. package/dest/l1/data_retrieval.d.ts +18 -9
  20. package/dest/l1/data_retrieval.d.ts.map +1 -1
  21. package/dest/l1/data_retrieval.js +13 -19
  22. package/dest/l1/validate_historical_logs.d.ts +23 -0
  23. package/dest/l1/validate_historical_logs.d.ts.map +1 -0
  24. package/dest/l1/validate_historical_logs.js +108 -0
  25. package/dest/modules/contract_data_source_adapter.d.ts +25 -0
  26. package/dest/modules/contract_data_source_adapter.d.ts.map +1 -0
  27. package/dest/modules/contract_data_source_adapter.js +42 -0
  28. package/dest/modules/data_source_base.d.ts +16 -10
  29. package/dest/modules/data_source_base.d.ts.map +1 -1
  30. package/dest/modules/data_source_base.js +71 -60
  31. package/dest/modules/data_store_updater.d.ts +16 -9
  32. package/dest/modules/data_store_updater.d.ts.map +1 -1
  33. package/dest/modules/data_store_updater.js +52 -40
  34. package/dest/modules/instrumentation.d.ts +4 -1
  35. package/dest/modules/instrumentation.d.ts.map +1 -1
  36. package/dest/modules/instrumentation.js +5 -0
  37. package/dest/modules/l1_synchronizer.d.ts +8 -4
  38. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  39. package/dest/modules/l1_synchronizer.js +182 -70
  40. package/dest/modules/validation.d.ts +4 -3
  41. package/dest/modules/validation.d.ts.map +1 -1
  42. package/dest/modules/validation.js +4 -4
  43. package/dest/store/block_store.d.ts +59 -21
  44. package/dest/store/block_store.d.ts.map +1 -1
  45. package/dest/store/block_store.js +181 -66
  46. package/dest/store/contract_class_store.d.ts +17 -3
  47. package/dest/store/contract_class_store.d.ts.map +1 -1
  48. package/dest/store/contract_class_store.js +17 -1
  49. package/dest/store/contract_instance_store.d.ts +28 -1
  50. package/dest/store/contract_instance_store.d.ts.map +1 -1
  51. package/dest/store/contract_instance_store.js +31 -0
  52. package/dest/store/data_stores.d.ts +68 -0
  53. package/dest/store/data_stores.d.ts.map +1 -0
  54. package/dest/store/data_stores.js +50 -0
  55. package/dest/store/function_names_cache.d.ts +17 -0
  56. package/dest/store/function_names_cache.d.ts.map +1 -0
  57. package/dest/store/function_names_cache.js +30 -0
  58. package/dest/store/l2_tips_cache.js +1 -1
  59. package/dest/test/fake_l1_state.d.ts +7 -3
  60. package/dest/test/fake_l1_state.d.ts.map +1 -1
  61. package/dest/test/fake_l1_state.js +42 -15
  62. package/dest/test/mock_l2_block_source.d.ts +12 -3
  63. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  64. package/dest/test/mock_l2_block_source.js +24 -2
  65. package/dest/test/noop_l1_archiver.d.ts +4 -4
  66. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  67. package/dest/test/noop_l1_archiver.js +9 -6
  68. package/package.json +13 -13
  69. package/src/archiver.ts +108 -50
  70. package/src/config.ts +15 -1
  71. package/src/errors.ts +75 -8
  72. package/src/factory.ts +11 -10
  73. package/src/index.ts +17 -2
  74. package/src/l1/calldata_retriever.ts +15 -4
  75. package/src/l1/data_retrieval.ts +30 -35
  76. package/src/l1/validate_historical_logs.ts +140 -0
  77. package/src/modules/contract_data_source_adapter.ts +59 -0
  78. package/src/modules/data_source_base.ts +75 -57
  79. package/src/modules/data_store_updater.ts +71 -39
  80. package/src/modules/instrumentation.ts +8 -0
  81. package/src/modules/l1_synchronizer.ts +241 -71
  82. package/src/modules/validation.ts +8 -7
  83. package/src/store/block_store.ts +204 -73
  84. package/src/store/contract_class_store.ts +28 -2
  85. package/src/store/contract_instance_store.ts +43 -0
  86. package/src/store/data_stores.ts +108 -0
  87. package/src/store/function_names_cache.ts +37 -0
  88. package/src/store/l2_tips_cache.ts +1 -1
  89. package/src/test/fake_l1_state.ts +47 -24
  90. package/src/test/mock_l2_block_source.ts +23 -2
  91. package/src/test/noop_l1_archiver.ts +9 -6
  92. package/dest/store/kv_archiver_store.d.ts +0 -377
  93. package/dest/store/kv_archiver_store.d.ts.map +0 -1
  94. package/dest/store/kv_archiver_store.js +0 -494
  95. package/src/store/kv_archiver_store.ts +0 -713
@@ -2,32 +2,37 @@ import type { BlobClientInterface } from '@aztec/blob-client/client';
2
2
  import { EpochCache } from '@aztec/epoch-cache';
3
3
  import { InboxContract, type InboxContractState, RollupContract } from '@aztec/ethereum/contracts';
4
4
  import type { L1BlockId } from '@aztec/ethereum/l1-types';
5
+ import { getFinalizedL1Block } from '@aztec/ethereum/queries';
5
6
  import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
6
7
  import { asyncPool } from '@aztec/foundation/async-pool';
7
8
  import { maxBigint } from '@aztec/foundation/bigint';
8
9
  import { BlockNumber, CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
9
10
  import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
10
- import { pick } from '@aztec/foundation/collection';
11
+ import { partition, pick } from '@aztec/foundation/collection';
11
12
  import { Fr } from '@aztec/foundation/curves/bn254';
13
+ import { EthAddress } from '@aztec/foundation/eth-address';
12
14
  import { type Logger, createLogger } from '@aztec/foundation/log';
13
15
  import { retryTimes } from '@aztec/foundation/retry';
14
16
  import { count } from '@aztec/foundation/string';
15
17
  import { DateProvider, Timer, elapsed } from '@aztec/foundation/timer';
16
18
  import { isDefined, isErrorClass } from '@aztec/foundation/types';
17
19
  import { type ArchiverEmitter, L2BlockSourceEvents, type ValidateCheckpointResult } from '@aztec/stdlib/block';
18
- import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
20
+ import { Checkpoint, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
19
21
  import { type L1RollupConstants, getEpochAtSlot, getSlotAtNextL1Block } from '@aztec/stdlib/epoch-helpers';
20
22
  import { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
23
+ import type { CoordinationSignatureContext } from '@aztec/stdlib/p2p';
21
24
  import { type Traceable, type Tracer, execInSpan, trackSpan } from '@aztec/telemetry-client';
22
25
 
23
26
  import { InitialCheckpointNumberNotSequentialError } from '../errors.js';
24
27
  import {
25
- retrieveCheckpointsFromRollup,
28
+ type RetrievedCheckpointFromCalldata,
29
+ getCheckpointBlobDataFromBlobs,
30
+ retrieveCheckpointCalldataFromRollup,
26
31
  retrieveL1ToL2Message,
27
32
  retrieveL1ToL2Messages,
28
33
  retrievedToPublishedCheckpoint,
29
34
  } from '../l1/data_retrieval.js';
30
- import type { KVArchiverDataStore } from '../store/kv_archiver_store.js';
35
+ import { type ArchiverDataStores, getArchiverSynchPoint } from '../store/data_stores.js';
31
36
  import type { L2TipsCache } from '../store/l2_tips_cache.js';
32
37
  import { MessageStoreError } from '../store/message_store.js';
33
38
  import type { InboxMessage } from '../structs/inbox_message.js';
@@ -62,10 +67,11 @@ export class ArchiverL1Synchronizer implements Traceable {
62
67
  private readonly debugClient: ViemPublicDebugClient,
63
68
  private readonly rollup: RollupContract,
64
69
  private readonly inbox: InboxContract,
65
- private readonly store: KVArchiverDataStore,
70
+ private readonly stores: ArchiverDataStores,
66
71
  private config: {
67
72
  batchSize: number;
68
73
  skipValidateCheckpointAttestations?: boolean;
74
+ skipPromoteProposedCheckpointDuringL1Sync?: boolean;
69
75
  maxAllowedEthClientDriftSeconds: number;
70
76
  },
71
77
  private readonly blobClient: BlobClientInterface,
@@ -81,7 +87,7 @@ export class ArchiverL1Synchronizer implements Traceable {
81
87
  l2TipsCache?: L2TipsCache,
82
88
  private readonly log: Logger = createLogger('archiver:l1-sync'),
83
89
  ) {
84
- this.updater = new ArchiverDataStoreUpdater(this.store, l2TipsCache, {
90
+ this.updater = new ArchiverDataStoreUpdater(this.stores, l2TipsCache, {
85
91
  rollupManaLimit: l1Constants.rollupManaLimit,
86
92
  });
87
93
  this.tracer = tracer;
@@ -91,6 +97,7 @@ export class ArchiverL1Synchronizer implements Traceable {
91
97
  public setConfig(newConfig: {
92
98
  batchSize: number;
93
99
  skipValidateCheckpointAttestations?: boolean;
100
+ skipPromoteProposedCheckpointDuringL1Sync?: boolean;
94
101
  maxAllowedEthClientDriftSeconds: number;
95
102
  }) {
96
103
  this.config = newConfig;
@@ -106,6 +113,13 @@ export class ArchiverL1Synchronizer implements Traceable {
106
113
  return this.l1Timestamp;
107
114
  }
108
115
 
116
+ private getSignatureContext(): CoordinationSignatureContext {
117
+ return {
118
+ chainId: this.publicClient.chain.id,
119
+ rollupAddress: EthAddress.fromString(this.rollup.address),
120
+ };
121
+ }
122
+
109
123
  /** Checks that the ethereum node we are connected to has a latest timestamp no more than the allowed drift. Throw if not. */
110
124
  public async testEthereumNodeSynced(): Promise<void> {
111
125
  const maxAllowedDelay = this.config.maxAllowedEthClientDriftSeconds;
@@ -149,7 +163,7 @@ export class ArchiverL1Synchronizer implements Traceable {
149
163
  }
150
164
 
151
165
  // Load sync point for blocks defaulting to start block
152
- const { blocksSynchedTo = this.l1Constants.l1StartBlock } = await this.store.getSynchPoint();
166
+ const { blocksSynchedTo = this.l1Constants.l1StartBlock } = await getArchiverSynchPoint(this.stores);
153
167
  this.log.debug(`Starting new archiver sync iteration`, { blocksSynchedTo, currentL1BlockData });
154
168
 
155
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.
@@ -180,7 +194,7 @@ export class ArchiverL1Synchronizer implements Traceable {
180
194
  // past it, since otherwise we'll keep downloading it and reprocessing it on every iteration until
181
195
  // we get a valid checkpoint to advance the syncpoint.
182
196
  if (!rollupStatus.validationResult?.valid && rollupStatus.lastL1BlockWithCheckpoint !== undefined) {
183
- await this.store.setCheckpointSynchedL1BlockNumber(rollupStatus.lastL1BlockWithCheckpoint);
197
+ await this.stores.blocks.setSynchedL1BlockNumber(rollupStatus.lastL1BlockWithCheckpoint);
184
198
  }
185
199
 
186
200
  // And lastly we check if we are missing any checkpoints behind us due to a possible L1 reorg.
@@ -215,12 +229,16 @@ export class ArchiverL1Synchronizer implements Traceable {
215
229
  /** Query L1 for its finalized block and update the finalized checkpoint accordingly. */
216
230
  private async updateFinalizedCheckpoint(): Promise<void> {
217
231
  try {
218
- const finalizedL1Block = await this.publicClient.getBlock({ blockTag: 'finalized', includeTransactions: false });
232
+ const finalizedL1Block = await getFinalizedL1Block(this.publicClient);
233
+ if (!finalizedL1Block) {
234
+ this.log.trace(`Skipping finalized checkpoint update: L1 has no finalized block yet.`);
235
+ return;
236
+ }
219
237
  const finalizedL1BlockNumber = finalizedL1Block.number;
220
238
  const finalizedCheckpointNumber = await this.rollup.getProvenCheckpointNumber({
221
239
  blockNumber: finalizedL1BlockNumber,
222
240
  });
223
- const localFinalizedCheckpointNumber = await this.store.getFinalizedCheckpointNumber();
241
+ const localFinalizedCheckpointNumber = await this.stores.blocks.getFinalizedCheckpointNumber();
224
242
  if (localFinalizedCheckpointNumber !== finalizedCheckpointNumber) {
225
243
  await this.updater.setFinalizedCheckpointNumber(finalizedCheckpointNumber);
226
244
  this.log.info(`Updated finalized chain to checkpoint ${finalizedCheckpointNumber}`, {
@@ -239,8 +257,8 @@ export class ArchiverL1Synchronizer implements Traceable {
239
257
  /** Prune all proposed local blocks that should have been checkpointed by now. */
240
258
  private async pruneUncheckpointedBlocks(currentL1Timestamp: bigint) {
241
259
  const [lastCheckpointedBlockNumber, lastProposedBlockNumber] = await Promise.all([
242
- this.store.getCheckpointedL2BlockNumber(),
243
- this.store.getLatestBlockNumber(),
260
+ this.stores.blocks.getCheckpointedL2BlockNumber(),
261
+ this.stores.blocks.getLatestL2BlockNumber(),
244
262
  ]);
245
263
 
246
264
  // If there are no uncheckpointed blocks, we got nothing to do
@@ -254,7 +272,10 @@ export class ArchiverL1Synchronizer implements Traceable {
254
272
  const firstUncheckpointedBlockNumber = BlockNumber(lastCheckpointedBlockNumber + 1);
255
273
 
256
274
  // What's the slot of the first uncheckpointed block?
257
- const [firstUncheckpointedBlockHeader] = await this.store.getBlockHeaders(firstUncheckpointedBlockNumber, 1);
275
+ const [firstUncheckpointedBlockHeader] = await this.stores.blocks.getBlockHeaders(
276
+ firstUncheckpointedBlockNumber,
277
+ 1,
278
+ );
258
279
  const firstUncheckpointedBlockSlot = firstUncheckpointedBlockHeader?.getSlot();
259
280
 
260
281
  if (firstUncheckpointedBlockSlot === undefined || firstUncheckpointedBlockSlot >= slotAtNextL1Block) {
@@ -301,7 +322,7 @@ export class ArchiverL1Synchronizer implements Traceable {
301
322
  currentL1Timestamp: bigint,
302
323
  ): Promise<{ rollupCanPrune: boolean }> {
303
324
  const rollupCanPrune = await this.canPrune(currentL1BlockNumber, currentL1Timestamp);
304
- const localPendingCheckpointNumber = await this.store.getSynchedCheckpointNumber();
325
+ const localPendingCheckpointNumber = await this.stores.blocks.getLatestCheckpointNumber();
305
326
  const canPrune = localPendingCheckpointNumber > provenCheckpointNumber && rollupCanPrune;
306
327
 
307
328
  if (canPrune) {
@@ -322,12 +343,12 @@ export class ArchiverL1Synchronizer implements Traceable {
322
343
  // promises when the gap between local pending and proven checkpoint numbers is large.
323
344
  const BATCH_SIZE = 10;
324
345
  const indices = Array.from({ length: checkpointsToUnwind }, (_, i) => CheckpointNumber(i + pruneFrom));
325
- const checkpoints = (await asyncPool(BATCH_SIZE, indices, idx => this.store.getCheckpointData(idx))).filter(
326
- isDefined,
327
- );
346
+ const checkpoints = (
347
+ await asyncPool(BATCH_SIZE, indices, idx => this.stores.blocks.getCheckpointData(idx))
348
+ ).filter(isDefined);
328
349
  const newBlocks = (
329
350
  await asyncPool(BATCH_SIZE, checkpoints, cp =>
330
- this.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber)),
351
+ this.stores.blocks.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber)),
331
352
  )
332
353
  )
333
354
  .filter(isDefined)
@@ -347,12 +368,12 @@ export class ArchiverL1Synchronizer implements Traceable {
347
368
  this.log.warn(
348
369
  `Removed ${count(checkpointsToUnwind, 'checkpoint')} after checkpoint ${provenCheckpointNumber} ` +
349
370
  `due to predicted reorg at L1 block ${currentL1BlockNumber}. ` +
350
- `Updated latest checkpoint is ${await this.store.getSynchedCheckpointNumber()}.`,
371
+ `Updated latest checkpoint is ${await this.stores.blocks.getLatestCheckpointNumber()}.`,
351
372
  );
352
373
  this.instrumentation.processPrune(timer.ms());
353
374
  // TODO(palla/reorg): Do we need to set the block synched L1 block number here?
354
375
  // Seems like the next iteration should handle this.
355
- // await this.store.setCheckpointSynchedL1BlockNumber(currentL1BlockNumber);
376
+ // await this.stores.blocks.setSynchedL1BlockNumber(currentL1BlockNumber);
356
377
  }
357
378
 
358
379
  return { rollupCanPrune };
@@ -376,7 +397,7 @@ export class ArchiverL1Synchronizer implements Traceable {
376
397
  l1BlockNumber: this.l1Constants.l1StartBlock,
377
398
  l1BlockHash: this.l1Constants.l1StartBlockHash,
378
399
  },
379
- } = await this.store.getSynchPoint();
400
+ } = await getArchiverSynchPoint(this.stores);
380
401
 
381
402
  // Nothing to do if L1 block number has not moved forward
382
403
  const currentL1BlockNumber = currentL1Block.l1BlockNumber;
@@ -386,10 +407,10 @@ export class ArchiverL1Synchronizer implements Traceable {
386
407
 
387
408
  // Compare local message store state with the remote. If they match, we just advance the match pointer.
388
409
  const remoteMessagesState = await this.inbox.getState({ blockNumber: currentL1BlockNumber });
389
- const localLastMessage = await this.store.getLastL1ToL2Message();
410
+ const localLastMessage = await this.stores.messages.getLastMessage();
390
411
  if (await this.localStateMatches(localLastMessage, remoteMessagesState)) {
391
412
  this.log.trace(`Local L1 to L2 messages are already in sync with remote at L1 block ${currentL1BlockNumber}`);
392
- await this.store.setMessageSyncState(currentL1Block, remoteMessagesState.treeInProgress);
413
+ await this.stores.messages.setMessageSyncState(currentL1Block, remoteMessagesState.treeInProgress);
393
414
  return true;
394
415
  }
395
416
 
@@ -405,7 +426,7 @@ export class ArchiverL1Synchronizer implements Traceable {
405
426
  `Failed to store L1 to L2 messages retrieved from L1: ${error.message}. Rolling back syncpoint to retry.`,
406
427
  { inboxMessage: error.inboxMessage },
407
428
  );
408
- await this.rollbackL1ToL2Messages(remoteMessagesState.treeInProgress);
429
+ await this.rollbackL1ToL2Messages(remoteMessagesState);
409
430
  return false;
410
431
  }
411
432
  throw error;
@@ -414,24 +435,24 @@ export class ArchiverL1Synchronizer implements Traceable {
414
435
  // Note that, if there are no new messages to insert, but there was an L1 reorg that pruned out last messages,
415
436
  // we'd notice by comparing our local state with the remote one again, and seeing they don't match even after
416
437
  // our sync attempt. In this case, we also rollback our syncpoint, and trigger a retry.
417
- const localLastMessageAfterSync = await this.store.getLastL1ToL2Message();
438
+ const localLastMessageAfterSync = await this.stores.messages.getLastMessage();
418
439
  if (!(await this.localStateMatches(localLastMessageAfterSync, remoteMessagesState))) {
419
440
  this.log.warn(
420
441
  `Local L1 to L2 messages state does not match remote after sync attempt. Rolling back syncpoint to retry.`,
421
442
  { localLastMessageAfterSync, remoteMessagesState },
422
443
  );
423
- await this.rollbackL1ToL2Messages(remoteMessagesState.treeInProgress);
444
+ await this.rollbackL1ToL2Messages(remoteMessagesState);
424
445
  return false;
425
446
  }
426
447
 
427
448
  // Advance the syncpoint after a successful sync
428
- await this.store.setMessageSyncState(currentL1Block, remoteMessagesState.treeInProgress);
449
+ await this.stores.messages.setMessageSyncState(currentL1Block, remoteMessagesState.treeInProgress);
429
450
  return true;
430
451
  }
431
452
 
432
453
  /** Checks if the local rolling hash and message count matches the remote state */
433
454
  private async localStateMatches(localLastMessage: InboxMessage | undefined, remoteState: InboxContractState) {
434
- const localMessageCount = await this.store.getTotalL1ToL2MessageCount();
455
+ const localMessageCount = await this.stores.messages.getTotalL1ToL2MessageCount();
435
456
  this.log.trace(`Comparing local and remote inbox state`, { localMessageCount, localLastMessage, remoteState });
436
457
 
437
458
  return (
@@ -453,7 +474,7 @@ export class ArchiverL1Synchronizer implements Traceable {
453
474
  this.log.trace(`Retrieving L1 to L2 messages in L1 blocks ${searchStartBlock}-${searchEndBlock}`);
454
475
  const messages = await retrieveL1ToL2Messages(this.inbox, searchStartBlock, searchEndBlock);
455
476
  const timer = new Timer();
456
- await this.store.addL1ToL2Messages(messages);
477
+ await this.stores.messages.addL1ToL2Messages(messages);
457
478
  const perMsg = timer.ms() / messages.length;
458
479
  this.instrumentation.processNewMessages(messages.length, perMsg);
459
480
  for (const msg of messages) {
@@ -475,18 +496,39 @@ export class ArchiverL1Synchronizer implements Traceable {
475
496
  * Rolls back local L1 to L2 messages to the last common message with L1, and updates the syncpoint to the L1 block of that message.
476
497
  * If no common message is found, rolls back all messages and sets the syncpoint to the start block.
477
498
  */
478
- private async rollbackL1ToL2Messages(remoteTreeInProgress: bigint): Promise<L1BlockId> {
479
- // Slowly go back through our messages until we find the last common message.
480
- // We could query the logs in batch as an optimization, but the depth of the reorg should not be deep, and this
481
- // is a very rare case, so it's fine to query one log at a time.
499
+ private async rollbackL1ToL2Messages(remoteMessagesState: InboxContractState): Promise<L1BlockId> {
500
+ const { treeInProgress: remoteTreeInProgress, messagesRollingHash: remoteRollingHash } = remoteMessagesState;
501
+
502
+ // Slowly go back through our messages until we find the last common message. We could query the logs in
503
+ // batch as an optimization, but the depth of the reorg should not be deep, and this is a very rare case,
504
+ // so it's fine to query one log at a time.
482
505
  let commonMsg: undefined | InboxMessage;
483
506
  let messagesToDelete = 0;
484
507
  this.log.verbose(`Searching most recent common L1 to L2 message`);
485
- for await (const localMsg of this.store.iterateL1ToL2Messages({ reverse: true })) {
508
+ for await (const localMsg of this.stores.messages.iterateL1ToL2Messages({ reverse: true })) {
509
+ const logCtx = { remoteMsg: undefined as InboxMessage | undefined, localMsg, remoteMessagesState };
510
+
511
+ // First check if the local message rolling hash matches the current rolling hash of the inbox contract,
512
+ // which means we just need to rollback some local messages and we should be back in sync. This means there
513
+ // was an L1 reorg that removed some of the messages we had, but no new messages were added compared.
514
+ if (localMsg.rollingHash.equals(remoteRollingHash)) {
515
+ this.log.info(
516
+ `Found common L1 to L2 message at index ${localMsg.index} on L1 block ${localMsg.l1BlockNumber} matching current remote state`,
517
+ logCtx,
518
+ );
519
+ commonMsg = localMsg;
520
+ break;
521
+ }
522
+
523
+ // If there's no match with the current remote state, check if the message exists on the inbox contract at all
524
+ // by looking at the inbox events. If the L1 reorg *added* new messages in addition to deleting existing ones,
525
+ // then the current remote state's rolling hash will not match anything we have locally, so we need to check existence
526
+ // of individual messages via logs. Note we use logs and not historical queries so we don't have to depend on
527
+ // an archival rpc node, since the message could be from a long time ago if we're catching up with syncing.
486
528
  const remoteMsg = await retrieveL1ToL2Message(this.inbox, localMsg);
487
- const logCtx = { remoteMsg, localMsg: localMsg };
529
+ logCtx.remoteMsg = remoteMsg;
488
530
  if (remoteMsg && remoteMsg.rollingHash.equals(localMsg.rollingHash)) {
489
- this.log.verbose(
531
+ this.log.info(
490
532
  `Found most recent common L1 to L2 message at index ${localMsg.index} on L1 block ${localMsg.l1BlockNumber}`,
491
533
  logCtx,
492
534
  );
@@ -506,7 +548,7 @@ export class ArchiverL1Synchronizer implements Traceable {
506
548
  if (messagesToDelete > 0) {
507
549
  const lastGoodIndex = commonMsg?.index;
508
550
  this.log.warn(`Rolling back all local L1 to L2 messages after index ${lastGoodIndex ?? 'initial'}`);
509
- await this.store.removeL1ToL2Messages(lastGoodIndex !== undefined ? lastGoodIndex + 1n : 0n);
551
+ await this.stores.messages.removeL1ToL2Messages(lastGoodIndex !== undefined ? lastGoodIndex + 1n : 0n);
510
552
  }
511
553
 
512
554
  // Update the syncpoint so the loop below reprocesses the changed messages. We go to the block before
@@ -515,7 +557,7 @@ export class ArchiverL1Synchronizer implements Traceable {
515
557
  const syncPointL1BlockNumber = commonMsg ? commonMsg.l1BlockNumber - 1n : this.l1Constants.l1StartBlock;
516
558
  const syncPointL1BlockHash = await this.getL1BlockHash(syncPointL1BlockNumber);
517
559
  const messagesSyncPoint = { l1BlockNumber: syncPointL1BlockNumber, l1BlockHash: syncPointL1BlockHash };
518
- await this.store.setMessageSyncState(messagesSyncPoint, remoteTreeInProgress);
560
+ await this.stores.messages.setMessageSyncState(messagesSyncPoint, remoteTreeInProgress);
519
561
  this.log.verbose(`Updated messages syncpoint to L1 block ${syncPointL1BlockNumber}`, {
520
562
  ...messagesSyncPoint,
521
563
  remoteTreeInProgress,
@@ -537,9 +579,9 @@ export class ArchiverL1Synchronizer implements Traceable {
537
579
  currentL1BlockNumber: bigint,
538
580
  initialSyncComplete: boolean,
539
581
  ): Promise<RollupStatus> {
540
- const localPendingCheckpointNumber = await this.store.getSynchedCheckpointNumber();
582
+ const localPendingCheckpointNumber = await this.stores.blocks.getLatestCheckpointNumber();
541
583
  const initialValidationResult: ValidateCheckpointResult | undefined =
542
- await this.store.getPendingChainValidationStatus();
584
+ await this.stores.blocks.getPendingChainValidationStatus();
543
585
  const {
544
586
  provenCheckpointNumber,
545
587
  provenArchive,
@@ -569,7 +611,7 @@ export class ArchiverL1Synchronizer implements Traceable {
569
611
  // we need to set it to zero. This is an edge case because we dont have a checkpoint zero (initial checkpoint is one),
570
612
  // so localCheckpointForDestinationProvenCheckpointNumber would not be found below.
571
613
  if (provenCheckpointNumber === 0) {
572
- const localProvenCheckpointNumber = await this.store.getProvenCheckpointNumber();
614
+ const localProvenCheckpointNumber = await this.stores.blocks.getProvenCheckpointNumber();
573
615
  if (localProvenCheckpointNumber !== provenCheckpointNumber) {
574
616
  await this.updater.setProvenCheckpointNumber(provenCheckpointNumber);
575
617
  this.log.info(`Rolled back proven chain to checkpoint ${provenCheckpointNumber}`, { provenCheckpointNumber });
@@ -577,11 +619,11 @@ export class ArchiverL1Synchronizer implements Traceable {
577
619
  }
578
620
 
579
621
  const localCheckpointForDestinationProvenCheckpointNumber =
580
- await this.store.getCheckpointData(provenCheckpointNumber);
622
+ await this.stores.blocks.getCheckpointData(provenCheckpointNumber);
581
623
 
582
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
583
625
  // synched checkpoint when requesting L2Tips from the archiver. This is the only place where the proven checkpoint is set.
584
- const synched = await this.store.getSynchedCheckpointNumber();
626
+ const synched = await this.stores.blocks.getLatestCheckpointNumber();
585
627
  if (
586
628
  localCheckpointForDestinationProvenCheckpointNumber &&
587
629
  synched < localCheckpointForDestinationProvenCheckpointNumber.checkpointNumber
@@ -601,7 +643,7 @@ export class ArchiverL1Synchronizer implements Traceable {
601
643
  localCheckpointForDestinationProvenCheckpointNumber &&
602
644
  provenArchive.equals(localCheckpointForDestinationProvenCheckpointNumber.archive.root)
603
645
  ) {
604
- const localProvenCheckpointNumber = await this.store.getProvenCheckpointNumber();
646
+ const localProvenCheckpointNumber = await this.stores.blocks.getProvenCheckpointNumber();
605
647
  if (localProvenCheckpointNumber !== provenCheckpointNumber) {
606
648
  await this.updater.setProvenCheckpointNumber(provenCheckpointNumber);
607
649
  this.log.info(`Updated proven chain to checkpoint ${provenCheckpointNumber}`, { provenCheckpointNumber });
@@ -629,7 +671,7 @@ export class ArchiverL1Synchronizer implements Traceable {
629
671
  // If we have 0 checkpoints locally and there are no checkpoints onchain there is nothing to do.
630
672
  const noCheckpoints = localPendingCheckpointNumber === 0 && pendingCheckpointNumber === 0;
631
673
  if (noCheckpoints) {
632
- await this.store.setCheckpointSynchedL1BlockNumber(currentL1BlockNumber);
674
+ await this.stores.blocks.setSynchedL1BlockNumber(currentL1BlockNumber);
633
675
  this.log.debug(
634
676
  `No checkpoints to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}, no checkpoints on chain`,
635
677
  );
@@ -641,7 +683,7 @@ export class ArchiverL1Synchronizer implements Traceable {
641
683
  // Related to the L2 reorgs of the pending chain. We are only interested in actually addressing a reorg if there
642
684
  // are any state that could be impacted by it. If we have no checkpoints, there is no impact.
643
685
  if (localPendingCheckpointNumber > 0) {
644
- const localPendingCheckpoint = await this.store.getCheckpointData(localPendingCheckpointNumber);
686
+ const localPendingCheckpoint = await this.stores.blocks.getCheckpointData(localPendingCheckpointNumber);
645
687
  if (localPendingCheckpoint === undefined) {
646
688
  throw new Error(`Missing checkpoint ${localPendingCheckpointNumber}`);
647
689
  }
@@ -656,7 +698,7 @@ export class ArchiverL1Synchronizer implements Traceable {
656
698
  // However, in the re-org scenario, our L1 node is temporarily lying to us and we end up potentially missing checkpoints.
657
699
  // We must only set this block number based on actually retrieved logs.
658
700
  // TODO(#8621): Tackle this properly when we handle L1 Re-orgs.
659
- // await this.store.setCheckpointSynchedL1BlockNumber(currentL1BlockNumber);
701
+ // await this.stores.blocks.setSynchedL1BlockNumber(currentL1BlockNumber);
660
702
  this.log.debug(`No checkpoints to retrieve from ${blocksSynchedTo + 1n} to ${currentL1BlockNumber}`);
661
703
  return rollupStatus;
662
704
  }
@@ -676,7 +718,7 @@ export class ArchiverL1Synchronizer implements Traceable {
676
718
 
677
719
  let tipAfterUnwind = localPendingCheckpointNumber;
678
720
  while (true) {
679
- const candidateCheckpoint = await this.store.getCheckpointData(tipAfterUnwind);
721
+ const candidateCheckpoint = await this.stores.blocks.getCheckpointData(tipAfterUnwind);
680
722
  if (candidateCheckpoint === undefined) {
681
723
  break;
682
724
  }
@@ -701,7 +743,7 @@ export class ArchiverL1Synchronizer implements Traceable {
701
743
  this.log.warn(
702
744
  `Removed ${count(checkpointsToRemove, 'checkpoint')} after checkpoint ${tipAfterUnwind} ` +
703
745
  `due to mismatched checkpoint hashes at L1 block ${currentL1BlockNumber}. ` +
704
- `Updated L2 latest checkpoint is ${await this.store.getSynchedCheckpointNumber()}.`,
746
+ `Updated L2 latest checkpoint is ${await this.stores.blocks.getLatestCheckpointNumber()}.`,
705
747
  );
706
748
  }
707
749
  }
@@ -718,22 +760,20 @@ export class ArchiverL1Synchronizer implements Traceable {
718
760
 
719
761
  this.log.trace(`Retrieving checkpoints from L1 block ${searchStartBlock} to ${searchEndBlock}`);
720
762
 
721
- // TODO(md): Retrieve from blob client then from consensus client, then from peers
722
- const retrievedCheckpoints = await execInSpan(this.tracer, 'Archiver.retrieveCheckpointsFromRollup', () =>
723
- retrieveCheckpointsFromRollup(
763
+ // First fetch calldata only, no blobs yet, since we may be able to just get that data out of the proposed chain
764
+ const calldataCheckpoints = await execInSpan(this.tracer, 'Archiver.retrieveCheckpointCalldataFromRollup', () =>
765
+ retrieveCheckpointCalldataFromRollup(
724
766
  this.rollup,
725
767
  this.publicClient,
726
768
  this.debugClient,
727
- this.blobClient,
728
769
  searchStartBlock, // TODO(palla/reorg): If the L2 reorg was due to an L1 reorg, we need to start search earlier
729
770
  searchEndBlock,
730
771
  this.instrumentation,
731
772
  this.log,
732
- !initialSyncComplete, // isHistoricalSync
733
773
  ),
734
774
  );
735
775
 
736
- if (retrievedCheckpoints.length === 0) {
776
+ if (calldataCheckpoints.length === 0) {
737
777
  // We are not calling `setBlockSynchedL1BlockNumber` because it may cause sync issues if based off infura.
738
778
  // See further details in earlier comments.
739
779
  this.log.trace(`Retrieved no new checkpoints from L1 block ${searchStartBlock} to ${searchEndBlock}`);
@@ -741,21 +781,56 @@ export class ArchiverL1Synchronizer implements Traceable {
741
781
  }
742
782
 
743
783
  this.log.debug(
744
- `Retrieved ${retrievedCheckpoints.length} new checkpoints between L1 blocks ${searchStartBlock} and ${searchEndBlock}`,
784
+ `Retrieved ${calldataCheckpoints.length} new checkpoint calldata between L1 blocks ${searchStartBlock} and ${searchEndBlock}`,
745
785
  {
746
- lastProcessedCheckpoint: retrievedCheckpoints[retrievedCheckpoints.length - 1].l1,
786
+ lastProcessedCheckpoint: calldataCheckpoints[calldataCheckpoints.length - 1].l1,
747
787
  searchStartBlock,
748
788
  searchEndBlock,
749
789
  },
750
790
  );
751
791
 
752
- const publishedCheckpoints = await Promise.all(retrievedCheckpoints.map(b => retrievedToPublishedCheckpoint(b)));
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
795
+ const lastCalldataCheckpoint = calldataCheckpoints[calldataCheckpoints.length - 1];
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;
800
+
801
+ // Then fetch blobs in parallel and build the full published checkpoints
802
+ const toFetchBlobs = checkpointToPromote ? calldataCheckpoints.slice(0, -1) : calldataCheckpoints;
803
+ const blobFetched = await asyncPool(10, toFetchBlobs, async checkpoint =>
804
+ retrievedToPublishedCheckpoint({
805
+ ...checkpoint,
806
+ checkpointBlobData: await getCheckpointBlobDataFromBlobs(
807
+ this.blobClient,
808
+ checkpoint.l1.blockHash,
809
+ checkpoint.blobHashes,
810
+ checkpoint.checkpointNumber,
811
+ this.log,
812
+ !initialSyncComplete,
813
+ checkpoint.parentBeaconBlockRoot,
814
+ checkpoint.l1.timestamp,
815
+ ),
816
+ }),
817
+ );
818
+
819
+ // And add the promoted checkpoint to the list of all checkpoints
820
+ const publishedCheckpoints = checkpointToPromote ? [...blobFetched, checkpointToPromote] : blobFetched;
753
821
  const validCheckpoints: PublishedCheckpoint[] = [];
754
822
 
823
+ // Now loop through all checkpoints and validate their attestations
755
824
  for (const published of publishedCheckpoints) {
756
825
  const validationResult = this.config.skipValidateCheckpointAttestations
757
826
  ? { valid: true as const }
758
- : await validateCheckpointAttestations(published, this.epochCache, this.l1Constants, this.log);
827
+ : await validateCheckpointAttestations(
828
+ published,
829
+ this.epochCache,
830
+ this.l1Constants,
831
+ this.getSignatureContext(),
832
+ this.log,
833
+ );
759
834
 
760
835
  // Only update the validation result if it has changed, so we can keep track of the first invalid checkpoint
761
836
  // in case there is a sequence of more than one invalid checkpoint, as we need to invalidate the first one.
@@ -793,7 +868,7 @@ export class ArchiverL1Synchronizer implements Traceable {
793
868
  // Check the inHash of the checkpoint against the l1->l2 messages.
794
869
  // The messages should've been synced up to the currentL1BlockNumber and must be available for the published
795
870
  // checkpoints we just retrieved.
796
- const l1ToL2Messages = await this.store.getL1ToL2Messages(published.checkpoint.number);
871
+ const l1ToL2Messages = await this.stores.messages.getL1ToL2Messages(published.checkpoint.number);
797
872
  const computedInHash = computeInHashFromL1ToL2Messages(l1ToL2Messages);
798
873
  const publishedInHash = published.checkpoint.header.inHash;
799
874
  if (!computedInHash.equals(publishedInHash)) {
@@ -832,16 +907,33 @@ export class ArchiverL1Synchronizer implements Traceable {
832
907
  try {
833
908
  const updatedValidationResult =
834
909
  rollupStatus.validationResult === initialValidationResult ? undefined : rollupStatus.validationResult;
910
+
911
+ // Split valid checkpoints: the promoted one (if any) is persisted via the proposed-promotion path,
912
+ // the rest via addCheckpoints. Both paths run within the same store transaction for atomicity.
913
+ const [[maybeValidCheckpointToPromote], checkpointsToAdd] = partition(
914
+ validCheckpoints,
915
+ c => c.checkpoint.number === checkpointToPromote?.checkpoint.number,
916
+ );
917
+
835
918
  const [processDuration, result] = await elapsed(() =>
836
919
  execInSpan(this.tracer, 'Archiver.addCheckpoints', () =>
837
- this.updater.addCheckpoints(validCheckpoints, updatedValidationResult),
920
+ this.updater.addCheckpoints(
921
+ checkpointsToAdd,
922
+ updatedValidationResult,
923
+ maybeValidCheckpointToPromote && {
924
+ l1: lastCalldataCheckpoint.l1,
925
+ attestations: lastCalldataCheckpoint.attestations,
926
+ checkpoint: maybeValidCheckpointToPromote,
927
+ },
928
+ evictProposedFrom,
929
+ ),
838
930
  ),
839
931
  );
840
932
 
841
- if (validCheckpoints.length > 0) {
933
+ if (checkpointsToAdd.length > 0) {
842
934
  this.instrumentation.processNewCheckpointedBlocks(
843
- processDuration / validCheckpoints.length,
844
- validCheckpoints.flatMap(c => c.checkpoint.blocks),
935
+ processDuration / checkpointsToAdd.length,
936
+ checkpointsToAdd.flatMap(c => c.checkpoint.blocks),
845
937
  );
846
938
  }
847
939
 
@@ -868,10 +960,10 @@ export class ArchiverL1Synchronizer implements Traceable {
868
960
  if (err instanceof InitialCheckpointNumberNotSequentialError) {
869
961
  const { previousCheckpointNumber, newCheckpointNumber } = err;
870
962
  const previousCheckpoint = previousCheckpointNumber
871
- ? await this.store.getCheckpointData(CheckpointNumber(previousCheckpointNumber))
963
+ ? await this.stores.blocks.getCheckpointData(CheckpointNumber(previousCheckpointNumber))
872
964
  : undefined;
873
965
  const updatedL1SyncPoint = previousCheckpoint?.l1.blockNumber ?? this.l1Constants.l1StartBlock;
874
- await this.store.setCheckpointSynchedL1BlockNumber(updatedL1SyncPoint);
966
+ await this.stores.blocks.setSynchedL1BlockNumber(updatedL1SyncPoint);
875
967
  this.log.warn(
876
968
  `Attempting to insert checkpoint ${newCheckpointNumber} with previous block ${previousCheckpointNumber}. Rolling back L1 sync point to ${updatedL1SyncPoint} to try and fetch the missing blocks.`,
877
969
  {
@@ -896,7 +988,7 @@ export class ArchiverL1Synchronizer implements Traceable {
896
988
  });
897
989
  }
898
990
  lastRetrievedCheckpoint = validCheckpoints.at(-1) ?? lastRetrievedCheckpoint;
899
- lastL1BlockWithCheckpoint = retrievedCheckpoints.at(-1)?.l1.blockNumber ?? lastL1BlockWithCheckpoint;
991
+ lastL1BlockWithCheckpoint = calldataCheckpoints.at(-1)?.l1.blockNumber ?? lastL1BlockWithCheckpoint;
900
992
  } while (searchEndBlock < currentL1BlockNumber);
901
993
 
902
994
  // Important that we update AFTER inserting the blocks.
@@ -905,6 +997,82 @@ export class ArchiverL1Synchronizer implements Traceable {
905
997
  return { ...rollupStatus, lastRetrievedCheckpoint, lastL1BlockWithCheckpoint };
906
998
  }
907
999
 
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
+ */
1008
+ private async tryBuildPublishedCheckpointFromProposed(
1009
+ calldataCheckpoint: RetrievedCheckpointFromCalldata | undefined,
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) {
1018
+ return undefined;
1019
+ }
1020
+
1021
+ if (
1022
+ !proposed.header.equals(calldataCheckpoint.header) ||
1023
+ !proposed.archive.root.equals(calldataCheckpoint.archiveRoot)
1024
+ ) {
1025
+ this.log.warn(
1026
+ `Local proposed checkpoint ${proposed.checkpointNumber} does not match checkpoint retrieved from L1, overriding with L1 data`,
1027
+ {
1028
+ proposedCheckpointNumber: proposed.checkpointNumber,
1029
+ proposedHeader: proposed.header.toInspect(),
1030
+ proposedArchiveRoot: proposed.archive.root.toString(),
1031
+ calldataCheckpointNumber: calldataCheckpoint.checkpointNumber,
1032
+ calldataHeader: calldataCheckpoint.header.toInspect(),
1033
+ calldataArchiveRoot: calldataCheckpoint.archiveRoot.toString(),
1034
+ },
1035
+ );
1036
+ // Return a divergence signal so the caller can evict pending >= this number
1037
+ return { diverged: true, fromCheckpointNumber: proposed.checkpointNumber };
1038
+ }
1039
+
1040
+ this.log.debug(
1041
+ `Building published checkpoint from proposed ${calldataCheckpoint.checkpointNumber} (skipping blob fetch)`,
1042
+ { proposedHeader: proposed.header.toInspect(), proposedArchiveRoot: proposed.archive.root.toString() },
1043
+ );
1044
+
1045
+ const blocks = await this.stores.blocks.getBlocks(BlockNumber(proposed.startBlock), proposed.blockCount);
1046
+ if (blocks.length !== proposed.blockCount) {
1047
+ this.log.warn(
1048
+ `Local proposed checkpoint ${proposed.checkpointNumber} has wrong block count (expected ${proposed.blockCount} blocks starting at ${proposed.startBlock} but got ${blocks.length})`,
1049
+ {
1050
+ proposedCheckpointNumber: proposed.checkpointNumber,
1051
+ proposedStartBlock: proposed.startBlock,
1052
+ proposedBlockCount: proposed.blockCount,
1053
+ retrievedBlocks: blocks.map(b => b.number),
1054
+ },
1055
+ );
1056
+ return undefined;
1057
+ }
1058
+
1059
+ const checkpoint = Checkpoint.from({
1060
+ archive: proposed.archive,
1061
+ header: proposed.header,
1062
+ blocks,
1063
+ number: proposed.checkpointNumber,
1064
+ feeAssetPriceModifier: proposed.feeAssetPriceModifier,
1065
+ });
1066
+ const promotedCheckpoint = PublishedCheckpoint.from({
1067
+ checkpoint,
1068
+ l1: calldataCheckpoint.l1,
1069
+ attestations: calldataCheckpoint.attestations,
1070
+ });
1071
+ this.instrumentation.processCheckpointPromoted();
1072
+
1073
+ return promotedCheckpoint;
1074
+ }
1075
+
908
1076
  private async checkForNewCheckpointsBeforeL1SyncPoint(
909
1077
  status: RollupStatus,
910
1078
  blocksSynchedTo: bigint,
@@ -914,7 +1082,7 @@ export class ArchiverL1Synchronizer implements Traceable {
914
1082
  // Compare the last checkpoint we have (either retrieved in this round or loaded from store) with what the
915
1083
  // rollup contract told us was the latest one (pinned at the currentL1BlockNumber).
916
1084
  const latestLocalCheckpointNumber =
917
- lastRetrievedCheckpoint?.checkpoint.number ?? (await this.store.getSynchedCheckpointNumber());
1085
+ lastRetrievedCheckpoint?.checkpoint.number ?? (await this.stores.blocks.getLatestCheckpointNumber());
918
1086
  if (latestLocalCheckpointNumber < pendingCheckpointNumber) {
919
1087
  // Here we have consumed all logs until the `currentL1Block` we pinned at the beginning of the archiver loop,
920
1088
  // but still haven't reached the pending checkpoint according to the call to the rollup contract.
@@ -927,7 +1095,9 @@ export class ArchiverL1Synchronizer implements Traceable {
927
1095
  latestLocalCheckpointArchive = lastRetrievedCheckpoint.checkpoint.archive.root.toString();
928
1096
  targetL1BlockNumber = lastRetrievedCheckpoint.l1.blockNumber;
929
1097
  } else if (latestLocalCheckpointNumber > 0) {
930
- const checkpoint = await this.store.getRangeOfCheckpoints(latestLocalCheckpointNumber, 1).then(([c]) => c);
1098
+ const checkpoint = await this.stores.blocks
1099
+ .getRangeOfCheckpoints(latestLocalCheckpointNumber, 1)
1100
+ .then(([c]) => c);
931
1101
  latestLocalCheckpointArchive = checkpoint.archive.root.toString();
932
1102
  targetL1BlockNumber = checkpoint.l1.blockNumber;
933
1103
  }
@@ -942,7 +1112,7 @@ export class ArchiverL1Synchronizer implements Traceable {
942
1112
  ...status,
943
1113
  },
944
1114
  );
945
- await this.store.setCheckpointSynchedL1BlockNumber(targetL1BlockNumber);
1115
+ await this.stores.blocks.setSynchedL1BlockNumber(targetL1BlockNumber);
946
1116
  } else {
947
1117
  this.log.trace(`No new checkpoints behind L1 sync point to retrieve.`, {
948
1118
  latestLocalCheckpointNumber,
@@ -952,7 +1122,7 @@ export class ArchiverL1Synchronizer implements Traceable {
952
1122
  }
953
1123
 
954
1124
  private async getCheckpointHeader(number: CheckpointNumber) {
955
- const checkpoint = await this.store.getCheckpointData(number);
1125
+ const checkpoint = await this.stores.blocks.getCheckpointData(number);
956
1126
  if (!checkpoint) {
957
1127
  return undefined;
958
1128
  }