@aztec/archiver 0.0.1-commit.f2ce05ee → 0.0.1-commit.f5d02921e

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 (108) hide show
  1. package/dest/archiver.d.ts +9 -6
  2. package/dest/archiver.d.ts.map +1 -1
  3. package/dest/archiver.js +76 -111
  4. package/dest/config.d.ts +3 -3
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +2 -1
  7. package/dest/errors.d.ts +34 -10
  8. package/dest/errors.d.ts.map +1 -1
  9. package/dest/errors.js +45 -16
  10. package/dest/factory.d.ts +4 -5
  11. package/dest/factory.d.ts.map +1 -1
  12. package/dest/factory.js +29 -26
  13. package/dest/index.d.ts +2 -1
  14. package/dest/index.d.ts.map +1 -1
  15. package/dest/index.js +1 -0
  16. package/dest/l1/bin/retrieve-calldata.js +32 -28
  17. package/dest/l1/calldata_retriever.d.ts +73 -50
  18. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  19. package/dest/l1/calldata_retriever.js +191 -259
  20. package/dest/l1/data_retrieval.d.ts +9 -9
  21. package/dest/l1/data_retrieval.d.ts.map +1 -1
  22. package/dest/l1/data_retrieval.js +21 -19
  23. package/dest/l1/spire_proposer.d.ts +5 -5
  24. package/dest/l1/spire_proposer.d.ts.map +1 -1
  25. package/dest/l1/spire_proposer.js +9 -17
  26. package/dest/modules/data_source_base.d.ts +14 -7
  27. package/dest/modules/data_source_base.d.ts.map +1 -1
  28. package/dest/modules/data_source_base.js +39 -77
  29. package/dest/modules/data_store_updater.d.ts +25 -12
  30. package/dest/modules/data_store_updater.d.ts.map +1 -1
  31. package/dest/modules/data_store_updater.js +125 -94
  32. package/dest/modules/instrumentation.d.ts +15 -2
  33. package/dest/modules/instrumentation.d.ts.map +1 -1
  34. package/dest/modules/instrumentation.js +19 -2
  35. package/dest/modules/l1_synchronizer.d.ts +5 -8
  36. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  37. package/dest/modules/l1_synchronizer.js +73 -33
  38. package/dest/modules/validation.d.ts +1 -1
  39. package/dest/modules/validation.d.ts.map +1 -1
  40. package/dest/modules/validation.js +2 -2
  41. package/dest/store/block_store.d.ts +65 -28
  42. package/dest/store/block_store.d.ts.map +1 -1
  43. package/dest/store/block_store.js +311 -134
  44. package/dest/store/contract_class_store.d.ts +2 -3
  45. package/dest/store/contract_class_store.d.ts.map +1 -1
  46. package/dest/store/contract_class_store.js +7 -67
  47. package/dest/store/contract_instance_store.d.ts +1 -1
  48. package/dest/store/contract_instance_store.d.ts.map +1 -1
  49. package/dest/store/contract_instance_store.js +6 -2
  50. package/dest/store/kv_archiver_store.d.ts +62 -21
  51. package/dest/store/kv_archiver_store.d.ts.map +1 -1
  52. package/dest/store/kv_archiver_store.js +75 -22
  53. package/dest/store/l2_tips_cache.d.ts +20 -0
  54. package/dest/store/l2_tips_cache.d.ts.map +1 -0
  55. package/dest/store/l2_tips_cache.js +109 -0
  56. package/dest/store/log_store.d.ts +6 -3
  57. package/dest/store/log_store.d.ts.map +1 -1
  58. package/dest/store/log_store.js +93 -16
  59. package/dest/store/message_store.d.ts +5 -1
  60. package/dest/store/message_store.d.ts.map +1 -1
  61. package/dest/store/message_store.js +14 -1
  62. package/dest/test/fake_l1_state.d.ts +13 -1
  63. package/dest/test/fake_l1_state.d.ts.map +1 -1
  64. package/dest/test/fake_l1_state.js +95 -23
  65. package/dest/test/mock_archiver.d.ts +1 -1
  66. package/dest/test/mock_archiver.d.ts.map +1 -1
  67. package/dest/test/mock_archiver.js +3 -2
  68. package/dest/test/mock_l1_to_l2_message_source.d.ts +1 -1
  69. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  70. package/dest/test/mock_l1_to_l2_message_source.js +2 -1
  71. package/dest/test/mock_l2_block_source.d.ts +26 -5
  72. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  73. package/dest/test/mock_l2_block_source.js +160 -89
  74. package/dest/test/mock_structs.d.ts +4 -1
  75. package/dest/test/mock_structs.d.ts.map +1 -1
  76. package/dest/test/mock_structs.js +13 -1
  77. package/dest/test/noop_l1_archiver.d.ts +4 -1
  78. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  79. package/dest/test/noop_l1_archiver.js +5 -1
  80. package/package.json +13 -13
  81. package/src/archiver.ts +93 -132
  82. package/src/config.ts +8 -1
  83. package/src/errors.ts +70 -26
  84. package/src/factory.ts +30 -16
  85. package/src/index.ts +1 -0
  86. package/src/l1/README.md +25 -68
  87. package/src/l1/bin/retrieve-calldata.ts +40 -27
  88. package/src/l1/calldata_retriever.ts +250 -379
  89. package/src/l1/data_retrieval.ts +23 -25
  90. package/src/l1/spire_proposer.ts +7 -15
  91. package/src/modules/data_source_base.ts +78 -98
  92. package/src/modules/data_store_updater.ts +138 -124
  93. package/src/modules/instrumentation.ts +29 -2
  94. package/src/modules/l1_synchronizer.ts +86 -43
  95. package/src/modules/validation.ts +2 -2
  96. package/src/store/block_store.ts +393 -170
  97. package/src/store/contract_class_store.ts +8 -106
  98. package/src/store/contract_instance_store.ts +8 -5
  99. package/src/store/kv_archiver_store.ts +117 -36
  100. package/src/store/l2_tips_cache.ts +128 -0
  101. package/src/store/log_store.ts +126 -27
  102. package/src/store/message_store.ts +20 -1
  103. package/src/test/fake_l1_state.ts +125 -26
  104. package/src/test/mock_archiver.ts +3 -2
  105. package/src/test/mock_l1_to_l2_message_source.ts +1 -0
  106. package/src/test/mock_l2_block_source.ts +209 -82
  107. package/src/test/mock_structs.ts +20 -6
  108. package/src/test/noop_l1_archiver.ts +7 -1
@@ -1,15 +1,14 @@
1
1
  import type { BlobClientInterface } from '@aztec/blob-client/client';
2
2
  import { EpochCache } from '@aztec/epoch-cache';
3
3
  import { InboxContract, RollupContract } from '@aztec/ethereum/contracts';
4
- import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
5
4
  import type { L1BlockId } from '@aztec/ethereum/l1-types';
6
5
  import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
6
+ import { asyncPool } from '@aztec/foundation/async-pool';
7
7
  import { maxBigint } from '@aztec/foundation/bigint';
8
8
  import { BlockNumber, CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
9
- import { Buffer32 } from '@aztec/foundation/buffer';
9
+ import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
10
10
  import { pick } from '@aztec/foundation/collection';
11
11
  import { Fr } from '@aztec/foundation/curves/bn254';
12
- import { EthAddress } from '@aztec/foundation/eth-address';
13
12
  import { type Logger, createLogger } from '@aztec/foundation/log';
14
13
  import { count } from '@aztec/foundation/string';
15
14
  import { DateProvider, Timer, elapsed } from '@aztec/foundation/timer';
@@ -28,6 +27,7 @@ import {
28
27
  retrievedToPublishedCheckpoint,
29
28
  } from '../l1/data_retrieval.js';
30
29
  import type { KVArchiverDataStore } from '../store/kv_archiver_store.js';
30
+ import type { L2TipsCache } from '../store/l2_tips_cache.js';
31
31
  import type { InboxMessage } from '../structs/inbox_message.js';
32
32
  import { ArchiverDataStoreUpdater } from './data_store_updater.js';
33
33
  import type { ArchiverInstrumentation } from './instrumentation.js';
@@ -60,10 +60,6 @@ export class ArchiverL1Synchronizer implements Traceable {
60
60
  private readonly debugClient: ViemPublicDebugClient,
61
61
  private readonly rollup: RollupContract,
62
62
  private readonly inbox: InboxContract,
63
- private readonly l1Addresses: Pick<
64
- L1ContractAddresses,
65
- 'registryAddress' | 'governanceProposerAddress' | 'slashFactoryAddress'
66
- > & { slashingProposerAddress: EthAddress },
67
63
  private readonly store: KVArchiverDataStore,
68
64
  private config: {
69
65
  batchSize: number;
@@ -74,12 +70,18 @@ export class ArchiverL1Synchronizer implements Traceable {
74
70
  private readonly epochCache: EpochCache,
75
71
  private readonly dateProvider: DateProvider,
76
72
  private readonly instrumentation: ArchiverInstrumentation,
77
- private readonly l1Constants: L1RollupConstants & { l1StartBlockHash: Buffer32; genesisArchiveRoot: Fr },
73
+ private readonly l1Constants: L1RollupConstants & {
74
+ l1StartBlockHash: Buffer32;
75
+ genesisArchiveRoot: Fr;
76
+ },
78
77
  private readonly events: ArchiverEmitter,
79
78
  tracer: Tracer,
79
+ l2TipsCache?: L2TipsCache,
80
80
  private readonly log: Logger = createLogger('archiver:l1-sync'),
81
81
  ) {
82
- this.updater = new ArchiverDataStoreUpdater(this.store);
82
+ this.updater = new ArchiverDataStoreUpdater(this.store, l2TipsCache, {
83
+ rollupManaLimit: l1Constants.rollupManaLimit,
84
+ });
83
85
  this.tracer = tracer;
84
86
  }
85
87
 
@@ -215,6 +217,9 @@ export class ArchiverL1Synchronizer implements Traceable {
215
217
  this.instrumentation.updateL1BlockHeight(currentL1BlockNumber);
216
218
  }
217
219
 
220
+ // Update the finalized L2 checkpoint based on L1 finality.
221
+ await this.updateFinalizedCheckpoint();
222
+
218
223
  // After syncing has completed, update the current l1 block number and timestamp,
219
224
  // otherwise we risk announcing to the world that we've synced to a given point,
220
225
  // but the corresponding blocks have not been processed (see #12631).
@@ -230,6 +235,30 @@ export class ArchiverL1Synchronizer implements Traceable {
230
235
  });
231
236
  }
232
237
 
238
+ /** Query L1 for its finalized block and update the finalized checkpoint accordingly. */
239
+ private async updateFinalizedCheckpoint(): Promise<void> {
240
+ try {
241
+ const finalizedL1Block = await this.publicClient.getBlock({ blockTag: 'finalized', includeTransactions: false });
242
+ const finalizedL1BlockNumber = finalizedL1Block.number;
243
+ const finalizedCheckpointNumber = await this.rollup.getProvenCheckpointNumber({
244
+ blockNumber: finalizedL1BlockNumber,
245
+ });
246
+ const localFinalizedCheckpointNumber = await this.store.getFinalizedCheckpointNumber();
247
+ if (localFinalizedCheckpointNumber !== finalizedCheckpointNumber) {
248
+ await this.updater.setFinalizedCheckpointNumber(finalizedCheckpointNumber);
249
+ this.log.info(`Updated finalized chain to checkpoint ${finalizedCheckpointNumber}`, {
250
+ finalizedCheckpointNumber,
251
+ finalizedL1BlockNumber,
252
+ });
253
+ }
254
+ } catch (err: any) {
255
+ // The rollup contract may not exist at the finalized L1 block right after deployment.
256
+ if (!err?.message?.includes('returned no data')) {
257
+ this.log.warn(`Failed to update finalized checkpoint: ${err}`);
258
+ }
259
+ }
260
+ }
261
+
233
262
  /** Prune all proposed local blocks that should have been checkpointed by now. */
234
263
  private async pruneUncheckpointedBlocks(currentL1Timestamp: bigint) {
235
264
  const [lastCheckpointedBlockNumber, lastProposedBlockNumber] = await Promise.all([
@@ -243,29 +272,32 @@ export class ArchiverL1Synchronizer implements Traceable {
243
272
  return;
244
273
  }
245
274
 
246
- // What's the slot of the first uncheckpointed block?
275
+ // What's the slot at the next L1 block? All blocks for slots strictly before this one should've been checkpointed by now.
276
+ const slotAtNextL1Block = getSlotAtNextL1Block(currentL1Timestamp, this.l1Constants);
247
277
  const firstUncheckpointedBlockNumber = BlockNumber(lastCheckpointedBlockNumber + 1);
278
+
279
+ // What's the slot of the first uncheckpointed block?
248
280
  const [firstUncheckpointedBlockHeader] = await this.store.getBlockHeaders(firstUncheckpointedBlockNumber, 1);
249
281
  const firstUncheckpointedBlockSlot = firstUncheckpointedBlockHeader?.getSlot();
250
282
 
251
- // What's the slot at the next L1 block? All blocks for slots strictly before this one should've been checkpointed by now.
252
- const slotAtNextL1Block = getSlotAtNextL1Block(currentL1Timestamp, this.l1Constants);
283
+ if (firstUncheckpointedBlockSlot === undefined || firstUncheckpointedBlockSlot >= slotAtNextL1Block) {
284
+ return;
285
+ }
253
286
 
254
- // Prune provisional blocks from slots that have ended without being checkpointed
255
- if (firstUncheckpointedBlockSlot !== undefined && firstUncheckpointedBlockSlot < slotAtNextL1Block) {
256
- this.log.warn(
257
- `Pruning blocks after block ${lastCheckpointedBlockNumber} due to slot ${firstUncheckpointedBlockSlot} not being checkpointed`,
258
- { firstUncheckpointedBlockHeader: firstUncheckpointedBlockHeader.toInspect(), slotAtNextL1Block },
259
- );
260
- const prunedBlocks = await this.updater.removeUncheckpointedBlocksAfter(lastCheckpointedBlockNumber);
287
+ // Prune provisional blocks from slots that have ended without being checkpointed.
288
+ // This also clears any proposed checkpoint whose blocks are being pruned.
289
+ this.log.warn(
290
+ `Pruning blocks after block ${lastCheckpointedBlockNumber} due to slot ${firstUncheckpointedBlockSlot} not being checkpointed`,
291
+ { firstUncheckpointedBlockHeader: firstUncheckpointedBlockHeader.toInspect(), slotAtNextL1Block },
292
+ );
293
+ const prunedBlocks = await this.updater.removeUncheckpointedBlocksAfter(lastCheckpointedBlockNumber);
261
294
 
262
- if (prunedBlocks.length > 0) {
263
- this.events.emit(L2BlockSourceEvents.L2PruneUncheckpointed, {
264
- type: L2BlockSourceEvents.L2PruneUncheckpointed,
265
- slotNumber: firstUncheckpointedBlockSlot,
266
- blocks: prunedBlocks,
267
- });
268
- }
295
+ if (prunedBlocks.length > 0) {
296
+ this.events.emit(L2BlockSourceEvents.L2PruneUncheckpointed, {
297
+ type: L2BlockSourceEvents.L2PruneUncheckpointed,
298
+ slotNumber: firstUncheckpointedBlockSlot,
299
+ blocks: prunedBlocks,
300
+ });
269
301
  }
270
302
  }
271
303
 
@@ -308,17 +340,20 @@ export class ArchiverL1Synchronizer implements Traceable {
308
340
 
309
341
  const checkpointsToUnwind = localPendingCheckpointNumber - provenCheckpointNumber;
310
342
 
311
- const checkpointPromises = Array.from({ length: checkpointsToUnwind })
312
- .fill(0)
313
- .map((_, i) => this.store.getCheckpointData(CheckpointNumber(i + pruneFrom)));
314
- const checkpoints = await Promise.all(checkpointPromises);
315
-
316
- const blockPromises = await Promise.all(
317
- checkpoints
318
- .filter(isDefined)
319
- .map(cp => this.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber))),
343
+ // Fetch checkpoints and blocks in bounded batches to avoid unbounded concurrent
344
+ // promises when the gap between local pending and proven checkpoint numbers is large.
345
+ const BATCH_SIZE = 10;
346
+ const indices = Array.from({ length: checkpointsToUnwind }, (_, i) => CheckpointNumber(i + pruneFrom));
347
+ const checkpoints = (await asyncPool(BATCH_SIZE, indices, idx => this.store.getCheckpointData(idx))).filter(
348
+ isDefined,
320
349
  );
321
- const newBlocks = blockPromises.filter(isDefined).flat();
350
+ const newBlocks = (
351
+ await asyncPool(BATCH_SIZE, checkpoints, cp =>
352
+ this.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber)),
353
+ )
354
+ )
355
+ .filter(isDefined)
356
+ .flat();
322
357
 
323
358
  // Emit an event for listening services to react to the chain prune
324
359
  this.events.emit(L2BlockSourceEvents.L2PruneUnproven, {
@@ -366,6 +401,7 @@ export class ArchiverL1Synchronizer implements Traceable {
366
401
  const localMessagesInserted = await this.store.getTotalL1ToL2MessageCount();
367
402
  const localLastMessage = await this.store.getLastL1ToL2Message();
368
403
  const remoteMessagesState = await this.inbox.getState({ blockNumber: currentL1BlockNumber });
404
+ await this.store.setInboxTreeInProgress(remoteMessagesState.treeInProgress);
369
405
 
370
406
  this.log.trace(`Retrieved remote inbox state at L1 block ${currentL1BlockNumber}.`, {
371
407
  localMessagesInserted,
@@ -376,7 +412,7 @@ export class ArchiverL1Synchronizer implements Traceable {
376
412
  // Compare message count and rolling hash. If they match, no need to retrieve anything.
377
413
  if (
378
414
  remoteMessagesState.totalMessagesInserted === localMessagesInserted &&
379
- remoteMessagesState.messagesRollingHash.equals(localLastMessage?.rollingHash ?? Buffer32.ZERO)
415
+ remoteMessagesState.messagesRollingHash.equals(localLastMessage?.rollingHash ?? Buffer16.ZERO)
380
416
  ) {
381
417
  this.log.trace(
382
418
  `No L1 to L2 messages to query between L1 blocks ${messagesSyncPoint.l1BlockNumber} and ${currentL1BlockNumber}.`,
@@ -550,7 +586,7 @@ export class ArchiverL1Synchronizer implements Traceable {
550
586
  if (provenCheckpointNumber === 0) {
551
587
  const localProvenCheckpointNumber = await this.store.getProvenCheckpointNumber();
552
588
  if (localProvenCheckpointNumber !== provenCheckpointNumber) {
553
- await this.store.setProvenCheckpointNumber(provenCheckpointNumber);
589
+ await this.updater.setProvenCheckpointNumber(provenCheckpointNumber);
554
590
  this.log.info(`Rolled back proven chain to checkpoint ${provenCheckpointNumber}`, { provenCheckpointNumber });
555
591
  }
556
592
  }
@@ -582,13 +618,13 @@ export class ArchiverL1Synchronizer implements Traceable {
582
618
  ) {
583
619
  const localProvenCheckpointNumber = await this.store.getProvenCheckpointNumber();
584
620
  if (localProvenCheckpointNumber !== provenCheckpointNumber) {
585
- await this.store.setProvenCheckpointNumber(provenCheckpointNumber);
621
+ await this.updater.setProvenCheckpointNumber(provenCheckpointNumber);
586
622
  this.log.info(`Updated proven chain to checkpoint ${provenCheckpointNumber}`, { provenCheckpointNumber });
587
623
  const provenSlotNumber = localCheckpointForDestinationProvenCheckpointNumber.header.slotNumber;
588
624
  const provenEpochNumber: EpochNumber = getEpochAtSlot(provenSlotNumber, this.l1Constants);
589
625
  const lastBlockNumberInCheckpoint =
590
626
  localCheckpointForDestinationProvenCheckpointNumber.startBlock +
591
- localCheckpointForDestinationProvenCheckpointNumber.numBlocks -
627
+ localCheckpointForDestinationProvenCheckpointNumber.blockCount -
592
628
  1;
593
629
 
594
630
  this.events.emit(L2BlockSourceEvents.L2BlockProven, {
@@ -597,7 +633,7 @@ export class ArchiverL1Synchronizer implements Traceable {
597
633
  slotNumber: provenSlotNumber,
598
634
  epochNumber: provenEpochNumber,
599
635
  });
600
- this.instrumentation.updateLastProvenBlock(lastBlockNumberInCheckpoint);
636
+ this.instrumentation.updateLastProvenCheckpoint(localCheckpointForDestinationProvenCheckpointNumber);
601
637
  } else {
602
638
  this.log.trace(`Proven checkpoint ${provenCheckpointNumber} already stored.`);
603
639
  }
@@ -706,7 +742,6 @@ export class ArchiverL1Synchronizer implements Traceable {
706
742
  this.blobClient,
707
743
  searchStartBlock, // TODO(palla/reorg): If the L2 reorg was due to an L1 reorg, we need to start search earlier
708
744
  searchEndBlock,
709
- this.l1Addresses,
710
745
  this.instrumentation,
711
746
  this.log,
712
747
  !initialSyncComplete, // isHistoricalSync
@@ -801,6 +836,14 @@ export class ArchiverL1Synchronizer implements Traceable {
801
836
  );
802
837
  }
803
838
 
839
+ for (const published of validCheckpoints) {
840
+ this.instrumentation.processCheckpointL1Timing({
841
+ slotNumber: published.checkpoint.header.slotNumber,
842
+ l1Timestamp: published.l1.timestamp,
843
+ l1Constants: this.l1Constants,
844
+ });
845
+ }
846
+
804
847
  try {
805
848
  const updatedValidationResult =
806
849
  rollupStatus.validationResult === initialValidationResult ? undefined : rollupStatus.validationResult;
@@ -819,7 +862,7 @@ export class ArchiverL1Synchronizer implements Traceable {
819
862
  const prunedCheckpointNumber = result.prunedBlocks[0].checkpointNumber;
820
863
  const prunedSlotNumber = result.prunedBlocks[0].header.globalVariables.slotNumber;
821
864
 
822
- this.log.warn(
865
+ this.log.info(
823
866
  `Pruned ${result.prunedBlocks.length} mismatching blocks for checkpoint ${prunedCheckpointNumber}`,
824
867
  { prunedBlocks: result.prunedBlocks.map(b => b.toBlockInfo()), prunedSlotNumber, prunedCheckpointNumber },
825
868
  );
@@ -9,7 +9,7 @@ import {
9
9
  getAttestationInfoFromPayload,
10
10
  } from '@aztec/stdlib/block';
11
11
  import type { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
12
- import { type L1RollupConstants, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
12
+ import { type L1RollupConstants, computeQuorum, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
13
13
  import { ConsensusPayload } from '@aztec/stdlib/p2p';
14
14
 
15
15
  export type { ValidateCheckpointResult };
@@ -66,7 +66,7 @@ export async function validateCheckpointAttestations(
66
66
  return { valid: true };
67
67
  }
68
68
 
69
- const requiredAttestationCount = Math.floor((committee.length * 2) / 3) + 1;
69
+ const requiredAttestationCount = computeQuorum(committee.length);
70
70
 
71
71
  const failedValidationResult = <TReason extends ValidateCheckpointNegativeResult['reason']>(reason: TReason) => ({
72
72
  valid: false as const,