@aztec/archiver 0.0.1-commit.6c91f13 → 0.0.1-commit.96bb3f7

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 (45) hide show
  1. package/README.md +14 -14
  2. package/dest/archiver/archiver.d.ts +13 -10
  3. package/dest/archiver/archiver.d.ts.map +1 -1
  4. package/dest/archiver/archiver.js +514 -57
  5. package/dest/archiver/archiver_store.d.ts +11 -4
  6. package/dest/archiver/archiver_store.d.ts.map +1 -1
  7. package/dest/archiver/archiver_store_test_suite.d.ts +1 -1
  8. package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
  9. package/dest/archiver/archiver_store_test_suite.js +6 -6
  10. package/dest/archiver/config.js +2 -2
  11. package/dest/archiver/instrumentation.d.ts +1 -1
  12. package/dest/archiver/instrumentation.d.ts.map +1 -1
  13. package/dest/archiver/instrumentation.js +15 -63
  14. package/dest/archiver/kv_archiver_store/block_store.d.ts +11 -4
  15. package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
  16. package/dest/archiver/kv_archiver_store/block_store.js +22 -3
  17. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +5 -4
  18. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
  19. package/dest/archiver/kv_archiver_store/kv_archiver_store.js +3 -0
  20. package/dest/archiver/l1/calldata_retriever.d.ts +2 -2
  21. package/dest/archiver/l1/calldata_retriever.d.ts.map +1 -1
  22. package/dest/archiver/l1/calldata_retriever.js +2 -2
  23. package/dest/archiver/l1/validate_trace.js +1 -1
  24. package/dest/archiver/validation.d.ts +4 -4
  25. package/dest/archiver/validation.d.ts.map +1 -1
  26. package/dest/archiver/validation.js +1 -1
  27. package/dest/test/mock_l1_to_l2_message_source.d.ts +2 -2
  28. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  29. package/dest/test/mock_l1_to_l2_message_source.js +12 -3
  30. package/dest/test/mock_l2_block_source.d.ts +8 -4
  31. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  32. package/dest/test/mock_l2_block_source.js +65 -19
  33. package/package.json +13 -13
  34. package/src/archiver/archiver.ts +179 -71
  35. package/src/archiver/archiver_store.ts +11 -3
  36. package/src/archiver/archiver_store_test_suite.ts +12 -13
  37. package/src/archiver/config.ts +2 -2
  38. package/src/archiver/instrumentation.ts +14 -63
  39. package/src/archiver/kv_archiver_store/block_store.ts +35 -7
  40. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +7 -3
  41. package/src/archiver/l1/calldata_retriever.ts +2 -2
  42. package/src/archiver/l1/validate_trace.ts +1 -1
  43. package/src/archiver/validation.ts +6 -6
  44. package/src/test/mock_l1_to_l2_message_source.ts +10 -4
  45. package/src/test/mock_l2_block_source.ts +76 -18
@@ -1,5 +1,5 @@
1
1
  import type { BlobClientInterface } from '@aztec/blob-client/client';
2
- import { GENESIS_BLOCK_HEADER_HASH } from '@aztec/constants';
2
+ import { GENESIS_BLOCK_HEADER_HASH, INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
3
3
  import { EpochCache } from '@aztec/epoch-cache';
4
4
  import { createEthereumChain } from '@aztec/ethereum/chain';
5
5
  import { BlockTagTooOldError, InboxContract, RollupContract } from '@aztec/ethereum/contracts';
@@ -33,8 +33,10 @@ import type { FunctionSelector } from '@aztec/stdlib/abi';
33
33
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
34
34
  import {
35
35
  type ArchiverEmitter,
36
+ type CheckpointId,
36
37
  CheckpointedL2Block,
37
38
  CommitteeAttestation,
39
+ GENESIS_CHECKPOINT_HEADER_HASH,
38
40
  L2Block,
39
41
  L2BlockNew,
40
42
  type L2BlockSink,
@@ -81,6 +83,7 @@ import {
81
83
  type TelemetryClient,
82
84
  type Traceable,
83
85
  type Tracer,
86
+ execInSpan,
84
87
  getTelemetryClient,
85
88
  trackSpan,
86
89
  } from '@aztec/telemetry-client';
@@ -102,7 +105,7 @@ import {
102
105
  } from './l1/data_retrieval.js';
103
106
  import { validateAndLogTraceAvailability } from './l1/validate_trace.js';
104
107
  import type { InboxMessage } from './structs/inbox_message.js';
105
- import { type ValidateBlockResult, validateCheckpointAttestations } from './validation.js';
108
+ import { type ValidateCheckpointResult, validateCheckpointAttestations } from './validation.js';
106
109
 
107
110
  /**
108
111
  * Helper interface to combine all sources this archiver implementation provides.
@@ -127,7 +130,7 @@ function mapArchiverConfig(config: Partial<ArchiverConfig>) {
127
130
  return {
128
131
  pollingIntervalMs: config.archiverPollingIntervalMS,
129
132
  batchSize: config.archiverBatchSize,
130
- skipValidateBlockAttestations: config.skipValidateBlockAttestations,
133
+ skipValidateCheckpointAttestations: config.skipValidateCheckpointAttestations,
131
134
  maxAllowedEthClientDriftSeconds: config.maxAllowedEthClientDriftSeconds,
132
135
  ethereumAllowNoDebugHosts: config.ethereumAllowNoDebugHosts,
133
136
  };
@@ -138,7 +141,7 @@ type RollupStatus = {
138
141
  provenArchive: Hex;
139
142
  pendingCheckpointNumber: CheckpointNumber;
140
143
  pendingArchive: Hex;
141
- validationResult: ValidateBlockResult | undefined;
144
+ validationResult: ValidateCheckpointResult | undefined;
142
145
  lastRetrievedCheckpoint?: PublishedCheckpoint;
143
146
  lastL1BlockWithCheckpoint?: bigint;
144
147
  };
@@ -192,7 +195,7 @@ export class Archiver
192
195
  private config: {
193
196
  pollingIntervalMs: number;
194
197
  batchSize: number;
195
- skipValidateBlockAttestations?: boolean;
198
+ skipValidateCheckpointAttestations?: boolean;
196
199
  maxAllowedEthClientDriftSeconds: number;
197
200
  ethereumAllowNoDebugHosts?: boolean;
198
201
  },
@@ -275,7 +278,7 @@ export class Archiver
275
278
  slotDuration,
276
279
  ethereumSlotDuration,
277
280
  proofSubmissionEpochs: Number(proofSubmissionEpochs),
278
- genesisArchiveRoot: Fr.fromHexString(genesisArchiveRoot),
281
+ genesisArchiveRoot: Fr.fromString(genesisArchiveRoot.toString()),
279
282
  };
280
283
 
281
284
  const opts = merge(
@@ -409,6 +412,7 @@ export class Archiver
409
412
  }
410
413
  }
411
414
 
415
+ @trackSpan('Archiver.syncFromL1')
412
416
  private async syncFromL1() {
413
417
  /**
414
418
  * We keep track of three "pointers" to L1 blocks:
@@ -555,6 +559,7 @@ export class Archiver
555
559
  }
556
560
 
557
561
  /** Checks if there'd be a reorg for the next checkpoint submission and start pruning now. */
562
+ @trackSpan('Archiver.handleEpochPrune')
558
563
  private async handleEpochPrune(
559
564
  provenCheckpointNumber: CheckpointNumber,
560
565
  currentL1BlockNumber: bigint,
@@ -590,14 +595,11 @@ export class Archiver
590
595
  );
591
596
  const newBlocks = blockPromises.filter(isDefined).flat();
592
597
 
593
- // TODO(pw/mbps): Don't convert to legacy blocks here
594
- const blocks: L2Block[] = (await Promise.all(newBlocks.map(x => this.getBlock(x.number)))).filter(isDefined);
595
-
596
598
  // Emit an event for listening services to react to the chain prune
597
599
  this.emit(L2BlockSourceEvents.L2PruneDetected, {
598
600
  type: L2BlockSourceEvents.L2PruneDetected,
599
601
  epochNumber: pruneFromEpochNumber,
600
- blocks,
602
+ blocks: newBlocks,
601
603
  });
602
604
 
603
605
  this.log.debug(
@@ -628,6 +630,7 @@ export class Archiver
628
630
  return [nextStart, nextEnd];
629
631
  }
630
632
 
633
+ @trackSpan('Archiver.handleL1ToL2Messages')
631
634
  private async handleL1ToL2Messages(
632
635
  messagesSyncPoint: L1BlockId,
633
636
  currentL1BlockNumber: bigint,
@@ -786,23 +789,25 @@ export class Archiver
786
789
  return Buffer32.fromString(block.hash);
787
790
  }
788
791
 
792
+ @trackSpan('Archiver.handleCheckpoints')
789
793
  private async handleCheckpoints(blocksSynchedTo: bigint, currentL1BlockNumber: bigint): Promise<RollupStatus> {
790
794
  const localPendingCheckpointNumber = await this.getSynchedCheckpointNumber();
791
- const initialValidationResult: ValidateBlockResult | undefined = await this.store.getPendingChainValidationStatus();
792
- const [
793
- rollupProvenCheckpointNumber,
794
- provenArchive,
795
- rollupPendingCheckpointNumber,
796
- pendingArchive,
797
- archiveForLocalPendingCheckpointNumber,
798
- ] = await this.rollup.status(localPendingCheckpointNumber, { blockNumber: currentL1BlockNumber });
799
- const provenCheckpointNumber = CheckpointNumber.fromBigInt(rollupProvenCheckpointNumber);
800
- const pendingCheckpointNumber = CheckpointNumber.fromBigInt(rollupPendingCheckpointNumber);
801
- const rollupStatus = {
795
+ const initialValidationResult: ValidateCheckpointResult | undefined =
796
+ await this.store.getPendingChainValidationStatus();
797
+ const {
802
798
  provenCheckpointNumber,
803
799
  provenArchive,
804
800
  pendingCheckpointNumber,
805
801
  pendingArchive,
802
+ archiveOfMyCheckpoint: archiveForLocalPendingCheckpointNumber,
803
+ } = await execInSpan(this.tracer, 'Archiver.getRollupStatus', () =>
804
+ this.rollup.status(localPendingCheckpointNumber, { blockNumber: currentL1BlockNumber }),
805
+ );
806
+ const rollupStatus: RollupStatus = {
807
+ provenCheckpointNumber,
808
+ provenArchive: provenArchive.toString(),
809
+ pendingCheckpointNumber,
810
+ pendingArchive: pendingArchive.toString(),
806
811
  validationResult: initialValidationResult,
807
812
  };
808
813
  this.log.trace(`Retrieved rollup status at current L1 block ${currentL1BlockNumber}.`, {
@@ -848,14 +853,12 @@ export class Archiver
848
853
 
849
854
  if (
850
855
  localCheckpointForDestinationProvenCheckpointNumber &&
851
- provenArchive === localCheckpointForDestinationProvenCheckpointNumber.archive.root.toString()
856
+ provenArchive.equals(localCheckpointForDestinationProvenCheckpointNumber.archive.root)
852
857
  ) {
853
858
  const localProvenCheckpointNumber = await this.getProvenCheckpointNumber();
854
859
  if (localProvenCheckpointNumber !== provenCheckpointNumber) {
855
860
  await this.setProvenCheckpointNumber(provenCheckpointNumber);
856
- this.log.info(`Updated proven chain to checkpoint ${provenCheckpointNumber}`, {
857
- provenCheckpointNumber,
858
- });
861
+ this.log.info(`Updated proven chain to checkpoint ${provenCheckpointNumber}`, { provenCheckpointNumber });
859
862
  const provenSlotNumber = localCheckpointForDestinationProvenCheckpointNumber.header.slotNumber;
860
863
  const provenEpochNumber: EpochNumber = getEpochAtSlot(provenSlotNumber, this.l1constants);
861
864
  const lastBlockNumberInCheckpoint =
@@ -898,7 +901,7 @@ export class Archiver
898
901
  }
899
902
 
900
903
  const localPendingArchiveRoot = localPendingCheckpoint.archive.root.toString();
901
- const noCheckpointSinceLast = localPendingCheckpoint && pendingArchive === localPendingArchiveRoot;
904
+ const noCheckpointSinceLast = localPendingCheckpoint && pendingArchive.toString() === localPendingArchiveRoot;
902
905
  if (noCheckpointSinceLast) {
903
906
  // We believe the following line causes a problem when we encounter L1 re-orgs.
904
907
  // Basically, by setting the synched L1 block number here, we are saying that we have
@@ -912,7 +915,9 @@ export class Archiver
912
915
  return rollupStatus;
913
916
  }
914
917
 
915
- const localPendingCheckpointInChain = archiveForLocalPendingCheckpointNumber === localPendingArchiveRoot;
918
+ const localPendingCheckpointInChain = archiveForLocalPendingCheckpointNumber.equals(
919
+ localPendingCheckpoint.archive.root,
920
+ );
916
921
  if (!localPendingCheckpointInChain) {
917
922
  // If our local pending checkpoint tip is not in the chain on L1 a "prune" must have happened
918
923
  // or the L1 have reorged.
@@ -938,7 +943,7 @@ export class Archiver
938
943
  archiveLocal: candidateCheckpoint.archive.root.toString(),
939
944
  },
940
945
  );
941
- if (archiveAtContract === candidateCheckpoint.archive.root.toString()) {
946
+ if (archiveAtContract.equals(candidateCheckpoint.archive.root)) {
942
947
  break;
943
948
  }
944
949
  tipAfterUnwind--;
@@ -968,17 +973,19 @@ export class Archiver
968
973
  this.log.trace(`Retrieving checkpoints from L1 block ${searchStartBlock} to ${searchEndBlock}`);
969
974
 
970
975
  // TODO(md): Retrieve from blob client then from consensus client, then from peers
971
- const retrievedCheckpoints = await retrieveCheckpointsFromRollup(
972
- this.rollup.getContract() as GetContractReturnType<typeof RollupAbi, ViemPublicClient>,
973
- this.publicClient,
974
- this.debugClient,
975
- this.blobClient,
976
- searchStartBlock, // TODO(palla/reorg): If the L2 reorg was due to an L1 reorg, we need to start search earlier
977
- searchEndBlock,
978
- this.l1Addresses,
979
- this.instrumentation,
980
- this.log,
981
- !this.initialSyncComplete, // isHistoricalSync
976
+ const retrievedCheckpoints = await execInSpan(this.tracer, 'Archiver.retrieveCheckpointsFromRollup', () =>
977
+ retrieveCheckpointsFromRollup(
978
+ this.rollup.getContract() as GetContractReturnType<typeof RollupAbi, ViemPublicClient>,
979
+ this.publicClient,
980
+ this.debugClient,
981
+ this.blobClient,
982
+ searchStartBlock, // TODO(palla/reorg): If the L2 reorg was due to an L1 reorg, we need to start search earlier
983
+ searchEndBlock,
984
+ this.l1Addresses,
985
+ this.instrumentation,
986
+ this.log,
987
+ !this.initialSyncComplete, // isHistoricalSync
988
+ ),
982
989
  );
983
990
 
984
991
  if (retrievedCheckpoints.length === 0) {
@@ -1001,7 +1008,7 @@ export class Archiver
1001
1008
  const validCheckpoints: PublishedCheckpoint[] = [];
1002
1009
 
1003
1010
  for (const published of publishedCheckpoints) {
1004
- const validationResult = this.config.skipValidateBlockAttestations
1011
+ const validationResult = this.config.skipValidateCheckpointAttestations
1005
1012
  ? { valid: true as const }
1006
1013
  : await validateCheckpointAttestations(published, this.epochCache, this.l1constants, this.log);
1007
1014
 
@@ -1014,7 +1021,7 @@ export class Archiver
1014
1021
  rollupStatus.validationResult?.valid !== validationResult.valid ||
1015
1022
  (!rollupStatus.validationResult.valid &&
1016
1023
  !validationResult.valid &&
1017
- rollupStatus.validationResult.block.blockNumber === validationResult.block.blockNumber)
1024
+ rollupStatus.validationResult.checkpoint.checkpointNumber === validationResult.checkpoint.checkpointNumber)
1018
1025
  ) {
1019
1026
  rollupStatus.validationResult = validationResult;
1020
1027
  }
@@ -1026,9 +1033,9 @@ export class Archiver
1026
1033
  ...pick(validationResult, 'reason'),
1027
1034
  });
1028
1035
 
1029
- // Emit event for invalid block detection
1030
- this.emit(L2BlockSourceEvents.InvalidAttestationsBlockDetected, {
1031
- type: L2BlockSourceEvents.InvalidAttestationsBlockDetected,
1036
+ // Emit event for invalid checkpoint detection
1037
+ this.emit(L2BlockSourceEvents.InvalidAttestationsCheckpointDetected, {
1038
+ type: L2BlockSourceEvents.InvalidAttestationsCheckpointDetected,
1032
1039
  validationResult,
1033
1040
  });
1034
1041
 
@@ -1043,7 +1050,7 @@ export class Archiver
1043
1050
  // checkpoints we just retrieved.
1044
1051
  const l1ToL2Messages = await this.getL1ToL2Messages(published.checkpoint.number);
1045
1052
  const computedInHash = computeInHashFromL1ToL2Messages(l1ToL2Messages);
1046
- const publishedInHash = published.checkpoint.header.contentCommitment.inHash;
1053
+ const publishedInHash = published.checkpoint.header.inHash;
1047
1054
  if (!computedInHash.equals(publishedInHash)) {
1048
1055
  this.log.fatal(`Mismatch inHash for checkpoint ${published.checkpoint.number}`, {
1049
1056
  checkpointHash: published.checkpoint.hash(),
@@ -1072,7 +1079,11 @@ export class Archiver
1072
1079
  try {
1073
1080
  const updatedValidationResult =
1074
1081
  rollupStatus.validationResult === initialValidationResult ? undefined : rollupStatus.validationResult;
1075
- const [processDuration] = await elapsed(() => this.addCheckpoints(validCheckpoints, updatedValidationResult));
1082
+ const [processDuration] = await elapsed(() =>
1083
+ execInSpan(this.tracer, 'Archiver.addCheckpoints', () =>
1084
+ this.addCheckpoints(validCheckpoints, updatedValidationResult),
1085
+ ),
1086
+ );
1076
1087
  this.instrumentation.processNewBlocks(
1077
1088
  processDuration / validCheckpoints.length,
1078
1089
  validCheckpoints.flatMap(c => c.checkpoint.blocks),
@@ -1351,7 +1362,7 @@ export class Archiver
1351
1362
 
1352
1363
  public addCheckpoints(
1353
1364
  checkpoints: PublishedCheckpoint[],
1354
- pendingChainValidationStatus?: ValidateBlockResult,
1365
+ pendingChainValidationStatus?: ValidateCheckpointResult,
1355
1366
  ): Promise<boolean> {
1356
1367
  return this.store.addCheckpoints(checkpoints, pendingChainValidationStatus);
1357
1368
  }
@@ -1381,6 +1392,16 @@ export class Archiver
1381
1392
  return publishedBlock;
1382
1393
  }
1383
1394
 
1395
+ public async getL2BlocksNew(from: BlockNumber, limit: number, proven?: boolean): Promise<L2BlockNew[]> {
1396
+ const blocks = await this.store.store.getBlocks(from, limit);
1397
+
1398
+ if (proven === true) {
1399
+ const provenBlockNumber = await this.store.getProvenBlockNumber();
1400
+ return blocks.filter(b => b.number <= provenBlockNumber);
1401
+ }
1402
+ return blocks;
1403
+ }
1404
+
1384
1405
  public async getBlockHeader(number: BlockNumber | 'latest'): Promise<BlockHeader | undefined> {
1385
1406
  if (number === 'latest') {
1386
1407
  number = await this.store.getSynchedL2BlockNumber();
@@ -1396,6 +1417,20 @@ export class Archiver
1396
1417
  return this.store.getCheckpointedBlock(number);
1397
1418
  }
1398
1419
 
1420
+ public async getCheckpointedBlocks(
1421
+ from: BlockNumber,
1422
+ limit: number,
1423
+ proven?: boolean,
1424
+ ): Promise<CheckpointedL2Block[]> {
1425
+ const blocks = await this.store.store.getCheckpointedBlocks(from, limit);
1426
+
1427
+ if (proven === true) {
1428
+ const provenBlockNumber = await this.store.getProvenBlockNumber();
1429
+ return blocks.filter(b => b.block.number <= provenBlockNumber);
1430
+ }
1431
+ return blocks;
1432
+ }
1433
+
1399
1434
  getCheckpointedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
1400
1435
  return this.store.getCheckpointedBlockByHash(blockHash);
1401
1436
  }
@@ -1403,6 +1438,9 @@ export class Archiver
1403
1438
  getProvenBlockNumber(): Promise<BlockNumber> {
1404
1439
  return this.store.getProvenBlockNumber();
1405
1440
  }
1441
+ getCheckpointedBlockNumber(): Promise<BlockNumber> {
1442
+ return this.store.getCheckpointedL2BlockNumber();
1443
+ }
1406
1444
  getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
1407
1445
  return this.store.getCheckpointedBlockByArchive(archive);
1408
1446
  }
@@ -1504,7 +1542,7 @@ export class Archiver
1504
1542
  return this.store.getDebugFunctionName(address, selector);
1505
1543
  }
1506
1544
 
1507
- async getPendingChainValidationStatus(): Promise<ValidateBlockResult> {
1545
+ async getPendingChainValidationStatus(): Promise<ValidateCheckpointResult> {
1508
1546
  return (await this.store.getPendingChainValidationStatus()) ?? { valid: true };
1509
1547
  }
1510
1548
 
@@ -1513,9 +1551,10 @@ export class Archiver
1513
1551
  }
1514
1552
 
1515
1553
  async getL2Tips(): Promise<L2Tips> {
1516
- const [latestBlockNumber, provenBlockNumber] = await Promise.all([
1554
+ const [latestBlockNumber, provenBlockNumber, checkpointedBlockNumber] = await Promise.all([
1517
1555
  this.getBlockNumber(),
1518
1556
  this.getProvenBlockNumber(),
1557
+ this.getCheckpointedBlockNumber(),
1519
1558
  ] as const);
1520
1559
 
1521
1560
  // TODO(#13569): Compute proper finalized block number based on L1 finalized block.
@@ -1523,44 +1562,112 @@ export class Archiver
1523
1562
  // NOTE: update end-to-end/src/e2e_epochs/epochs_empty_blocks.test.ts as that uses finalized blocks in computations
1524
1563
  const finalizedBlockNumber = BlockNumber(Math.max(provenBlockNumber - this.l1constants.epochDuration * 2, 0));
1525
1564
 
1526
- const [latestBlockHeader, provenBlockHeader, finalizedBlockHeader] = await Promise.all([
1527
- latestBlockNumber > 0 ? this.getBlockHeader(latestBlockNumber) : undefined,
1528
- provenBlockNumber > 0 ? this.getBlockHeader(provenBlockNumber) : undefined,
1529
- finalizedBlockNumber > 0 ? this.getBlockHeader(finalizedBlockNumber) : undefined,
1530
- ] as const);
1565
+ const beforeInitialblockNumber = BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
1531
1566
 
1532
- if (latestBlockNumber > 0 && !latestBlockHeader) {
1567
+ // Get the latest block header and checkpointed blocks for proven, finalised and checkpointed blocks
1568
+ const [latestBlockHeader, provenCheckpointedBlock, finalizedCheckpointedBlock, checkpointedBlock] =
1569
+ await Promise.all([
1570
+ latestBlockNumber > beforeInitialblockNumber ? this.getBlockHeader(latestBlockNumber) : undefined,
1571
+ provenBlockNumber > beforeInitialblockNumber ? this.getCheckpointedBlock(provenBlockNumber) : undefined,
1572
+ finalizedBlockNumber > beforeInitialblockNumber ? this.getCheckpointedBlock(finalizedBlockNumber) : undefined,
1573
+ checkpointedBlockNumber > beforeInitialblockNumber
1574
+ ? this.getCheckpointedBlock(checkpointedBlockNumber)
1575
+ : undefined,
1576
+ ] as const);
1577
+
1578
+ if (latestBlockNumber > beforeInitialblockNumber && !latestBlockHeader) {
1533
1579
  throw new Error(`Failed to retrieve latest block header for block ${latestBlockNumber}`);
1534
1580
  }
1535
1581
 
1536
- if (provenBlockNumber > 0 && !provenBlockHeader) {
1582
+ // Checkpointed blocks must exist for proven, finalized and checkpointed tips if they are beyond the initial block number.
1583
+ if (checkpointedBlockNumber > beforeInitialblockNumber && !checkpointedBlock?.block.header) {
1584
+ throw new Error(
1585
+ `Failed to retrieve checkpointed block header for block ${checkpointedBlockNumber} (latest block is ${latestBlockNumber})`,
1586
+ );
1587
+ }
1588
+
1589
+ if (provenBlockNumber > beforeInitialblockNumber && !provenCheckpointedBlock?.block.header) {
1537
1590
  throw new Error(
1538
- `Failed to retrieve proven block header for block ${provenBlockNumber} (latest block is ${latestBlockNumber})`,
1591
+ `Failed to retrieve proven checkpointed for block ${provenBlockNumber} (latest block is ${latestBlockNumber})`,
1539
1592
  );
1540
1593
  }
1541
1594
 
1542
- if (finalizedBlockNumber > 0 && !finalizedBlockHeader) {
1595
+ if (finalizedBlockNumber > beforeInitialblockNumber && !finalizedCheckpointedBlock?.block.header) {
1543
1596
  throw new Error(
1544
1597
  `Failed to retrieve finalized block header for block ${finalizedBlockNumber} (latest block is ${latestBlockNumber})`,
1545
1598
  );
1546
1599
  }
1547
1600
 
1548
1601
  const latestBlockHeaderHash = (await latestBlockHeader?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
1549
- const provenBlockHeaderHash = (await provenBlockHeader?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
1550
- const finalizedBlockHeaderHash = (await finalizedBlockHeader?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
1602
+ const provenBlockHeaderHash = (await provenCheckpointedBlock?.block.header?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
1603
+ const finalizedBlockHeaderHash =
1604
+ (await finalizedCheckpointedBlock?.block.header?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
1605
+ const checkpointedBlockHeaderHash = (await checkpointedBlock?.block.header?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
1606
+
1607
+ // Now attempt to retrieve checkpoints for proven, finalised and checkpointed blocks
1608
+ const [[provenBlockCheckpoint], [finalizedBlockCheckpoint], [checkpointedBlockCheckpoint]] = await Promise.all([
1609
+ provenCheckpointedBlock !== undefined
1610
+ ? await this.getPublishedCheckpoints(provenCheckpointedBlock?.checkpointNumber, 1)
1611
+ : [undefined],
1612
+ finalizedCheckpointedBlock !== undefined
1613
+ ? await this.getPublishedCheckpoints(finalizedCheckpointedBlock?.checkpointNumber, 1)
1614
+ : [undefined],
1615
+ checkpointedBlock !== undefined
1616
+ ? await this.getPublishedCheckpoints(checkpointedBlock?.checkpointNumber, 1)
1617
+ : [undefined],
1618
+ ]);
1619
+
1620
+ const initialcheckpointId: CheckpointId = {
1621
+ number: CheckpointNumber.ZERO,
1622
+ hash: GENESIS_CHECKPOINT_HEADER_HASH.toString(),
1623
+ };
1551
1624
 
1552
- return {
1553
- latest: { number: latestBlockNumber, hash: latestBlockHeaderHash.toString() },
1554
- proven: { number: provenBlockNumber, hash: provenBlockHeaderHash.toString() },
1555
- finalized: { number: finalizedBlockNumber, hash: finalizedBlockHeaderHash.toString() },
1625
+ const makeCheckpointId = (checkpoint: PublishedCheckpoint | undefined) => {
1626
+ if (checkpoint === undefined) {
1627
+ return initialcheckpointId;
1628
+ }
1629
+ return {
1630
+ number: checkpoint.checkpoint.number,
1631
+ hash: checkpoint.checkpoint.hash().toString(),
1632
+ };
1556
1633
  };
1634
+
1635
+ const l2Tips: L2Tips = {
1636
+ proposed: {
1637
+ number: latestBlockNumber,
1638
+ hash: latestBlockHeaderHash.toString(),
1639
+ },
1640
+ proven: {
1641
+ block: {
1642
+ number: provenBlockNumber,
1643
+ hash: provenBlockHeaderHash.toString(),
1644
+ },
1645
+ checkpoint: makeCheckpointId(provenBlockCheckpoint),
1646
+ },
1647
+ finalized: {
1648
+ block: {
1649
+ number: finalizedBlockNumber,
1650
+ hash: finalizedBlockHeaderHash.toString(),
1651
+ },
1652
+ checkpoint: makeCheckpointId(finalizedBlockCheckpoint),
1653
+ },
1654
+ checkpointed: {
1655
+ block: {
1656
+ number: checkpointedBlockNumber,
1657
+ hash: checkpointedBlockHeaderHash.toString(),
1658
+ },
1659
+ checkpoint: makeCheckpointId(checkpointedBlockCheckpoint),
1660
+ },
1661
+ };
1662
+
1663
+ return l2Tips;
1557
1664
  }
1558
1665
 
1559
1666
  public async rollbackTo(targetL2BlockNumber: BlockNumber): Promise<void> {
1560
1667
  // TODO(pw/mbps): This still assumes 1 block per checkpoint
1561
1668
  const currentBlocks = await this.getL2Tips();
1562
- const currentL2Block = currentBlocks.latest.number;
1563
- const currentProvenBlock = currentBlocks.proven.number;
1669
+ const currentL2Block = currentBlocks.proposed.number;
1670
+ const currentProvenBlock = currentBlocks.proven.block.number;
1564
1671
 
1565
1672
  if (targetL2BlockNumber >= currentL2Block) {
1566
1673
  throw new Error(`Target L2 block ${targetL2BlockNumber} must be less than current L2 block ${currentL2Block}`);
@@ -1766,6 +1873,7 @@ export class ArchiverStoreHelper
1766
1873
  | 'addBlocks'
1767
1874
  | 'getBlock'
1768
1875
  | 'getBlocks'
1876
+ | 'getCheckpointedBlocks'
1769
1877
  >
1770
1878
  {
1771
1879
  #log = createLogger('archiver:block-helper');
@@ -1924,7 +2032,7 @@ export class ArchiverStoreHelper
1924
2032
  ).every(Boolean);
1925
2033
  }
1926
2034
 
1927
- public addBlocks(blocks: L2BlockNew[], pendingChainValidationStatus?: ValidateBlockResult): Promise<boolean> {
2035
+ public addBlocks(blocks: L2BlockNew[], pendingChainValidationStatus?: ValidateCheckpointResult): Promise<boolean> {
1928
2036
  // Add the blocks to the store. Store will throw if the blocks are not in order, there are gaps,
1929
2037
  // or if the previous block is not in the store.
1930
2038
  return this.store.transactionAsync(async () => {
@@ -1947,7 +2055,7 @@ export class ArchiverStoreHelper
1947
2055
 
1948
2056
  public addCheckpoints(
1949
2057
  checkpoints: PublishedCheckpoint[],
1950
- pendingChainValidationStatus?: ValidateBlockResult,
2058
+ pendingChainValidationStatus?: ValidateCheckpointResult,
1951
2059
  ): Promise<boolean> {
1952
2060
  // Add the blocks to the store. Store will throw if the blocks are not in order, there are gaps,
1953
2061
  // or if the previous block is not in the store.
@@ -2091,7 +2199,7 @@ export class ArchiverStoreHelper
2091
2199
  return this.store.getContractClassLogs(filter);
2092
2200
  }
2093
2201
  getSynchedL2BlockNumber(): Promise<BlockNumber> {
2094
- return this.store.getCheckpointedL2BlockNumber();
2202
+ return this.store.getLatestBlockNumber();
2095
2203
  }
2096
2204
  getProvenCheckpointNumber(): Promise<CheckpointNumber> {
2097
2205
  return this.store.getProvenCheckpointNumber();
@@ -2147,10 +2255,10 @@ export class ArchiverStoreHelper
2147
2255
  getLastL1ToL2Message(): Promise<InboxMessage | undefined> {
2148
2256
  return this.store.getLastL1ToL2Message();
2149
2257
  }
2150
- getPendingChainValidationStatus(): Promise<ValidateBlockResult | undefined> {
2258
+ getPendingChainValidationStatus(): Promise<ValidateCheckpointResult | undefined> {
2151
2259
  return this.store.getPendingChainValidationStatus();
2152
2260
  }
2153
- setPendingChainValidationStatus(status: ValidateBlockResult | undefined): Promise<void> {
2261
+ setPendingChainValidationStatus(status: ValidateCheckpointResult | undefined): Promise<void> {
2154
2262
  this.#log.debug(`Setting pending chain validation status to valid ${status?.valid}`, status);
2155
2263
  return this.store.setPendingChainValidationStatus(status);
2156
2264
  }
@@ -4,7 +4,7 @@ import type { Fr } from '@aztec/foundation/curves/bn254';
4
4
  import type { CustomRange } from '@aztec/kv-store';
5
5
  import type { FunctionSelector } from '@aztec/stdlib/abi';
6
6
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
7
- import type { CheckpointedL2Block, L2BlockNew, ValidateBlockResult } from '@aztec/stdlib/block';
7
+ import type { CheckpointedL2Block, L2BlockNew, ValidateCheckpointResult } from '@aztec/stdlib/block';
8
8
  import type { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
9
9
  import type {
10
10
  ContractClassPublic,
@@ -85,6 +85,14 @@ export interface ArchiverDataStore {
85
85
  */
86
86
  getCheckpointedBlock(number: number): Promise<CheckpointedL2Block | undefined>;
87
87
 
88
+ /**
89
+ * Gets up to `limit` amount of checkpointed L2 blocks starting from `from`.
90
+ * @param from - Number of the first block to return (inclusive).
91
+ * @param limit - The number of blocks to return.
92
+ * @returns The requested checkpointed L2 blocks.
93
+ */
94
+ getCheckpointedBlocks(from: number, limit: number): Promise<CheckpointedL2Block[]>;
95
+
88
96
  /**
89
97
  * Returns the block for the given hash, or undefined if not exists.
90
98
  * @param blockHash - The block hash to return.
@@ -365,8 +373,8 @@ export interface ArchiverDataStore {
365
373
  getLastL1ToL2Message(): Promise<InboxMessage | undefined>;
366
374
 
367
375
  /** Returns the last synced validation status of the pending chain. */
368
- getPendingChainValidationStatus(): Promise<ValidateBlockResult | undefined>;
376
+ getPendingChainValidationStatus(): Promise<ValidateCheckpointResult | undefined>;
369
377
 
370
378
  /** Sets the last synced validation status of the pending chain. */
371
- setPendingChainValidationStatus(status: ValidateBlockResult | undefined): Promise<void>;
379
+ setPendingChainValidationStatus(status: ValidateCheckpointResult | undefined): Promise<void>;
372
380
  }
@@ -20,10 +20,9 @@ import {
20
20
  EthAddress,
21
21
  L2BlockHash,
22
22
  L2BlockNew,
23
- type ValidateBlockResult,
24
- randomBlockInfo,
23
+ type ValidateCheckpointResult,
25
24
  } from '@aztec/stdlib/block';
26
- import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
25
+ import { Checkpoint, L1PublishedData, PublishedCheckpoint, randomCheckpointInfo } from '@aztec/stdlib/checkpoint';
27
26
  import {
28
27
  type ContractClassPublic,
29
28
  type ContractInstanceWithAddress,
@@ -2756,7 +2755,7 @@ export function describeArchiverDataStore(
2756
2755
  });
2757
2756
 
2758
2757
  it('should store and retrieve a valid validation status', async () => {
2759
- const validStatus: ValidateBlockResult = { valid: true };
2758
+ const validStatus: ValidateCheckpointResult = { valid: true };
2760
2759
 
2761
2760
  await store.setPendingChainValidationStatus(validStatus);
2762
2761
  const retrievedStatus = await store.getPendingChainValidationStatus();
@@ -2765,9 +2764,9 @@ export function describeArchiverDataStore(
2765
2764
  });
2766
2765
 
2767
2766
  it('should store and retrieve an invalid validation status with insufficient attestations', async () => {
2768
- const invalidStatus: ValidateBlockResult = {
2767
+ const invalidStatus: ValidateCheckpointResult = {
2769
2768
  valid: false,
2770
- block: randomBlockInfo(1),
2769
+ checkpoint: randomCheckpointInfo(1),
2771
2770
  committee: [EthAddress.random(), EthAddress.random()],
2772
2771
  epoch: EpochNumber(123),
2773
2772
  seed: 456n,
@@ -2783,9 +2782,9 @@ export function describeArchiverDataStore(
2783
2782
  });
2784
2783
 
2785
2784
  it('should store and retrieve an invalid validation status with invalid attestation', async () => {
2786
- const invalidStatus: ValidateBlockResult = {
2785
+ const invalidStatus: ValidateCheckpointResult = {
2787
2786
  valid: false,
2788
- block: randomBlockInfo(2),
2787
+ checkpoint: randomCheckpointInfo(2),
2789
2788
  committee: [EthAddress.random()],
2790
2789
  attestors: [EthAddress.random()],
2791
2790
  epoch: EpochNumber(789),
@@ -2802,10 +2801,10 @@ export function describeArchiverDataStore(
2802
2801
  });
2803
2802
 
2804
2803
  it('should overwrite existing status when setting a new one', async () => {
2805
- const firstStatus: ValidateBlockResult = { valid: true };
2806
- const secondStatus: ValidateBlockResult = {
2804
+ const firstStatus: ValidateCheckpointResult = { valid: true };
2805
+ const secondStatus: ValidateCheckpointResult = {
2807
2806
  valid: false,
2808
- block: randomBlockInfo(3),
2807
+ checkpoint: randomCheckpointInfo(3),
2809
2808
  committee: [EthAddress.random()],
2810
2809
  epoch: EpochNumber(999),
2811
2810
  seed: 888n,
@@ -2822,9 +2821,9 @@ export function describeArchiverDataStore(
2822
2821
  });
2823
2822
 
2824
2823
  it('should handle empty committee and attestations arrays', async () => {
2825
- const statusWithEmptyArrays: ValidateBlockResult = {
2824
+ const statusWithEmptyArrays: ValidateCheckpointResult = {
2826
2825
  valid: false,
2827
- block: randomBlockInfo(4),
2826
+ checkpoint: randomCheckpointInfo(4),
2828
2827
  committee: [],
2829
2828
  epoch: EpochNumber(0),
2830
2829
  seed: 0n,
@@ -46,8 +46,8 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
46
46
  parseEnv: (val: string | undefined) => (val ? +val : undefined),
47
47
  description: 'The maximum possible size of the archiver DB in KB. Overwrites the general dataStoreMapSizeKb.',
48
48
  },
49
- skipValidateBlockAttestations: {
50
- description: 'Whether to skip validating block attestations (use only for testing).',
49
+ skipValidateCheckpointAttestations: {
50
+ description: 'Skip validating checkpoint attestations (for testing purposes only)',
51
51
  ...booleanConfigHelper(false),
52
52
  },
53
53
  maxAllowedEthClientDriftSeconds: {