@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.
Files changed (83) hide show
  1. package/dest/archiver.d.ts +15 -9
  2. package/dest/archiver.d.ts.map +1 -1
  3. package/dest/archiver.js +85 -53
  4. package/dest/config.d.ts +1 -1
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +2 -2
  7. package/dest/errors.d.ts +16 -5
  8. package/dest/errors.d.ts.map +1 -1
  9. package/dest/errors.js +29 -6
  10. package/dest/factory.d.ts +4 -4
  11. package/dest/factory.d.ts.map +1 -1
  12. package/dest/factory.js +11 -9
  13. package/dest/index.d.ts +8 -2
  14. package/dest/index.d.ts.map +1 -1
  15. package/dest/index.js +7 -1
  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/modules/contract_data_source_adapter.d.ts +25 -0
  20. package/dest/modules/contract_data_source_adapter.d.ts.map +1 -0
  21. package/dest/modules/contract_data_source_adapter.js +42 -0
  22. package/dest/modules/data_source_base.d.ts +16 -10
  23. package/dest/modules/data_source_base.d.ts.map +1 -1
  24. package/dest/modules/data_source_base.js +71 -60
  25. package/dest/modules/data_store_updater.d.ts +6 -6
  26. package/dest/modules/data_store_updater.d.ts.map +1 -1
  27. package/dest/modules/data_store_updater.js +42 -40
  28. package/dest/modules/l1_synchronizer.d.ts +5 -4
  29. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  30. package/dest/modules/l1_synchronizer.js +79 -54
  31. package/dest/modules/validation.d.ts +4 -3
  32. package/dest/modules/validation.d.ts.map +1 -1
  33. package/dest/modules/validation.js +4 -4
  34. package/dest/store/block_store.d.ts +58 -27
  35. package/dest/store/block_store.d.ts.map +1 -1
  36. package/dest/store/block_store.js +152 -75
  37. package/dest/store/contract_class_store.d.ts +17 -3
  38. package/dest/store/contract_class_store.d.ts.map +1 -1
  39. package/dest/store/contract_class_store.js +17 -1
  40. package/dest/store/contract_instance_store.d.ts +28 -1
  41. package/dest/store/contract_instance_store.d.ts.map +1 -1
  42. package/dest/store/contract_instance_store.js +31 -0
  43. package/dest/store/data_stores.d.ts +68 -0
  44. package/dest/store/data_stores.d.ts.map +1 -0
  45. package/dest/store/data_stores.js +50 -0
  46. package/dest/store/function_names_cache.d.ts +17 -0
  47. package/dest/store/function_names_cache.d.ts.map +1 -0
  48. package/dest/store/function_names_cache.js +30 -0
  49. package/dest/store/l2_tips_cache.js +1 -1
  50. package/dest/test/fake_l1_state.d.ts +2 -1
  51. package/dest/test/fake_l1_state.d.ts.map +1 -1
  52. package/dest/test/fake_l1_state.js +25 -8
  53. package/dest/test/mock_l2_block_source.d.ts +12 -3
  54. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  55. package/dest/test/mock_l2_block_source.js +24 -2
  56. package/dest/test/noop_l1_archiver.d.ts +4 -4
  57. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  58. package/dest/test/noop_l1_archiver.js +5 -5
  59. package/package.json +13 -13
  60. package/src/archiver.ts +91 -49
  61. package/src/config.ts +2 -1
  62. package/src/errors.ts +41 -8
  63. package/src/factory.ts +10 -10
  64. package/src/index.ts +15 -1
  65. package/src/l1/calldata_retriever.ts +15 -4
  66. package/src/modules/contract_data_source_adapter.ts +59 -0
  67. package/src/modules/data_source_base.ts +75 -57
  68. package/src/modules/data_store_updater.ts +46 -38
  69. package/src/modules/l1_synchronizer.ts +92 -60
  70. package/src/modules/validation.ts +8 -7
  71. package/src/store/block_store.ts +159 -80
  72. package/src/store/contract_class_store.ts +28 -2
  73. package/src/store/contract_instance_store.ts +43 -0
  74. package/src/store/data_stores.ts +108 -0
  75. package/src/store/function_names_cache.ts +37 -0
  76. package/src/store/l2_tips_cache.ts +1 -1
  77. package/src/test/fake_l1_state.ts +24 -14
  78. package/src/test/mock_l2_block_source.ts +23 -2
  79. package/src/test/noop_l1_archiver.ts +6 -6
  80. package/dest/store/kv_archiver_store.d.ts +0 -383
  81. package/dest/store/kv_archiver_store.d.ts.map +0 -1
  82. package/dest/store/kv_archiver_store.js +0 -501
  83. 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 { KVArchiverDataStore } from '../store/kv_archiver_store.js';
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 store: KVArchiverDataStore,
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.store, l2TipsCache, {
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.store.getSynchPoint();
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.store.setCheckpointSynchedL1BlockNumber(rollupStatus.lastL1BlockWithCheckpoint);
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.store.getFinalizedCheckpointNumber();
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.store.getCheckpointedL2BlockNumber(),
252
- this.store.getLatestBlockNumber(),
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.store.getBlockHeaders(firstUncheckpointedBlockNumber, 1);
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.store.getSynchedCheckpointNumber();
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 = (await asyncPool(BATCH_SIZE, indices, idx => this.store.getCheckpointData(idx))).filter(
335
- isDefined,
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.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber)),
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.store.getSynchedCheckpointNumber()}.`,
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.store.setCheckpointSynchedL1BlockNumber(currentL1BlockNumber);
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.store.getSynchPoint();
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.store.getLastL1ToL2Message();
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.store.setMessageSyncState(currentL1Block, remoteMessagesState.treeInProgress);
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.store.getLastL1ToL2Message();
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.store.setMessageSyncState(currentL1Block, remoteMessagesState.treeInProgress);
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.store.getTotalL1ToL2MessageCount();
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.store.addL1ToL2Messages(messages);
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.store.iterateL1ToL2Messages({ reverse: true })) {
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.store.removeL1ToL2Messages(lastGoodIndex !== undefined ? lastGoodIndex + 1n : 0n);
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.store.setMessageSyncState(messagesSyncPoint, remoteTreeInProgress);
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.store.getSynchedCheckpointNumber();
582
+ const localPendingCheckpointNumber = await this.stores.blocks.getLatestCheckpointNumber();
571
583
  const initialValidationResult: ValidateCheckpointResult | undefined =
572
- await this.store.getPendingChainValidationStatus();
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.store.getProvenCheckpointNumber();
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.store.getCheckpointData(provenCheckpointNumber);
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.store.getSynchedCheckpointNumber();
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.store.getProvenCheckpointNumber();
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.store.setCheckpointSynchedL1BlockNumber(currentL1BlockNumber);
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.store.getCheckpointData(localPendingCheckpointNumber);
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.store.setCheckpointSynchedL1BlockNumber(currentL1BlockNumber);
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.store.getCheckpointData(tipAfterUnwind);
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.store.getSynchedCheckpointNumber()}.`,
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 the proposed one (so we can skip blob fetch).
781
- // We only check the last one because the proposed checkpoint is always the most recent one,
782
- // and if it's in a multi-checkpoint batch it will always be last (sorted by L1 block number).
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 checkpointToPromote = await this.tryBuildPublishedCheckpointFromProposed(lastCalldataCheckpoint);
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(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
+ );
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.store.getL1ToL2Messages(published.checkpoint.number);
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.store.getCheckpointData(CheckpointNumber(previousCheckpointNumber))
963
+ ? await this.stores.blocks.getCheckpointData(CheckpointNumber(previousCheckpointNumber))
942
964
  : undefined;
943
965
  const updatedL1SyncPoint = previousCheckpoint?.l1.blockNumber ?? this.l1Constants.l1StartBlock;
944
- await this.store.setCheckpointSynchedL1BlockNumber(updatedL1SyncPoint);
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
- /** Checks if this checkpoint matches the local proposed one, and if so, loads local data to build a synthetic published checkpoint. */
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
- const proposed = await this.store.getProposedCheckpointOnly();
983
- if (
984
- this.config.skipPromoteProposedCheckpointDuringL1Sync ||
985
- !proposed ||
986
- !calldataCheckpoint ||
987
- proposed.checkpointNumber !== calldataCheckpoint.checkpointNumber
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
- return undefined;
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.store.getBlocks(BlockNumber(proposed.startBlock), proposed.blockCount);
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.store.getSynchedCheckpointNumber());
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.store.getRangeOfCheckpoints(latestLocalCheckpointNumber, 1).then(([c]) => c);
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.store.setCheckpointSynchedL1BlockNumber(targetL1BlockNumber);
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.store.getCheckpointData(number);
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
- attestations,
24
- }: PublishedCheckpoint): AttestationInfo[] {
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();