@aztec/archiver 0.0.1-commit.e0f15ab9b → 0.0.1-commit.e304674f1

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 (67) hide show
  1. package/README.md +12 -6
  2. package/dest/archiver.d.ts +5 -4
  3. package/dest/archiver.d.ts.map +1 -1
  4. package/dest/archiver.js +6 -3
  5. package/dest/errors.d.ts +14 -2
  6. package/dest/errors.d.ts.map +1 -1
  7. package/dest/errors.js +18 -2
  8. package/dest/l1/calldata_retriever.d.ts +1 -1
  9. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  10. package/dest/l1/calldata_retriever.js +2 -1
  11. package/dest/l1/data_retrieval.d.ts +3 -3
  12. package/dest/l1/data_retrieval.d.ts.map +1 -1
  13. package/dest/l1/data_retrieval.js +14 -15
  14. package/dest/modules/data_source_base.d.ts +4 -2
  15. package/dest/modules/data_source_base.d.ts.map +1 -1
  16. package/dest/modules/data_source_base.js +6 -0
  17. package/dest/modules/data_store_updater.d.ts +3 -2
  18. package/dest/modules/data_store_updater.d.ts.map +1 -1
  19. package/dest/modules/data_store_updater.js +9 -0
  20. package/dest/modules/l1_synchronizer.d.ts +3 -2
  21. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  22. package/dest/modules/l1_synchronizer.js +128 -123
  23. package/dest/modules/validation.d.ts +1 -1
  24. package/dest/modules/validation.d.ts.map +1 -1
  25. package/dest/modules/validation.js +2 -2
  26. package/dest/store/block_store.d.ts +39 -4
  27. package/dest/store/block_store.d.ts.map +1 -1
  28. package/dest/store/block_store.js +230 -61
  29. package/dest/store/kv_archiver_store.d.ts +21 -8
  30. package/dest/store/kv_archiver_store.d.ts.map +1 -1
  31. package/dest/store/kv_archiver_store.js +25 -8
  32. package/dest/store/l2_tips_cache.d.ts +2 -1
  33. package/dest/store/l2_tips_cache.d.ts.map +1 -1
  34. package/dest/store/l2_tips_cache.js +25 -5
  35. package/dest/store/message_store.d.ts +3 -3
  36. package/dest/store/message_store.d.ts.map +1 -1
  37. package/dest/store/message_store.js +9 -10
  38. package/dest/test/fake_l1_state.d.ts +9 -1
  39. package/dest/test/fake_l1_state.d.ts.map +1 -1
  40. package/dest/test/fake_l1_state.js +41 -6
  41. package/dest/test/mock_l1_to_l2_message_source.d.ts +1 -1
  42. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  43. package/dest/test/mock_l1_to_l2_message_source.js +2 -1
  44. package/dest/test/mock_l2_block_source.d.ts +7 -2
  45. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  46. package/dest/test/mock_l2_block_source.js +28 -3
  47. package/dest/test/noop_l1_archiver.d.ts +1 -1
  48. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  49. package/dest/test/noop_l1_archiver.js +0 -1
  50. package/package.json +13 -13
  51. package/src/archiver.ts +13 -7
  52. package/src/errors.ts +30 -2
  53. package/src/l1/calldata_retriever.ts +2 -1
  54. package/src/l1/data_retrieval.ts +8 -12
  55. package/src/modules/data_source_base.ts +15 -1
  56. package/src/modules/data_store_updater.ts +14 -1
  57. package/src/modules/l1_synchronizer.ts +138 -147
  58. package/src/modules/validation.ts +2 -2
  59. package/src/store/block_store.ts +300 -73
  60. package/src/store/kv_archiver_store.ts +43 -12
  61. package/src/store/l2_tips_cache.ts +50 -11
  62. package/src/store/message_store.ts +10 -12
  63. package/src/structs/inbox_message.ts +1 -1
  64. package/src/test/fake_l1_state.ts +56 -7
  65. package/src/test/mock_l1_to_l2_message_source.ts +1 -0
  66. package/src/test/mock_l2_block_source.ts +37 -2
  67. package/src/test/noop_l1_archiver.ts +0 -1
@@ -26,9 +26,16 @@ export class L2TipsCache {
26
26
  }
27
27
 
28
28
  private async loadFromStore(): Promise<L2Tips> {
29
- const [latestBlockNumber, provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber] = await Promise.all([
30
- this.blockStore.getLatestBlockNumber(),
29
+ const [
30
+ latestBlockNumber,
31
+ provenBlockNumber,
32
+ proposedCheckpointBlockNumber,
33
+ checkpointedBlockNumber,
34
+ finalizedBlockNumber,
35
+ ] = await Promise.all([
36
+ this.blockStore.getLatestL2BlockNumber(),
31
37
  this.blockStore.getProvenBlockNumber(),
38
+ this.blockStore.getProposedCheckpointL2BlockNumber(),
32
39
  this.blockStore.getCheckpointedL2BlockNumber(),
33
40
  this.blockStore.getFinalizedL2BlockNumber(),
34
41
  ]);
@@ -42,19 +49,34 @@ export class L2TipsCache {
42
49
  const getBlockData = (blockNumber: BlockNumber) =>
43
50
  blockNumber > beforeInitialBlockNumber ? this.blockStore.getBlockData(blockNumber) : genesisBlockHeader;
44
51
 
45
- const [latestBlockData, provenBlockData, checkpointedBlockData, finalizedBlockData] = await Promise.all(
46
- [latestBlockNumber, provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber].map(getBlockData),
47
- );
52
+ const [latestBlockData, provenBlockData, proposedCheckpointBlockData, checkpointedBlockData, finalizedBlockData] =
53
+ await Promise.all(
54
+ [
55
+ latestBlockNumber,
56
+ provenBlockNumber,
57
+ proposedCheckpointBlockNumber,
58
+ checkpointedBlockNumber,
59
+ finalizedBlockNumber,
60
+ ].map(getBlockData),
61
+ );
48
62
 
49
- if (!latestBlockData || !provenBlockData || !finalizedBlockData || !checkpointedBlockData) {
63
+ if (
64
+ !latestBlockData ||
65
+ !provenBlockData ||
66
+ !finalizedBlockData ||
67
+ !checkpointedBlockData ||
68
+ !proposedCheckpointBlockData
69
+ ) {
50
70
  throw new Error('Failed to load block data for L2 tips');
51
71
  }
52
72
 
53
- const [provenCheckpointId, finalizedCheckpointId, checkpointedCheckpointId] = await Promise.all([
54
- this.getCheckpointIdForBlock(provenBlockData),
55
- this.getCheckpointIdForBlock(finalizedBlockData),
56
- this.getCheckpointIdForBlock(checkpointedBlockData),
57
- ]);
73
+ const [provenCheckpointId, finalizedCheckpointId, proposedCheckpointId, checkpointedCheckpointId] =
74
+ await Promise.all([
75
+ this.getCheckpointIdForBlock(provenBlockData),
76
+ this.getCheckpointIdForBlock(finalizedBlockData),
77
+ this.getCheckpointIdForProposedCheckpoint(checkpointedBlockData),
78
+ this.getCheckpointIdForBlock(checkpointedBlockData),
79
+ ]);
58
80
 
59
81
  return {
60
82
  proposed: { number: latestBlockNumber, hash: latestBlockData.blockHash.toString() },
@@ -62,6 +84,10 @@ export class L2TipsCache {
62
84
  block: { number: provenBlockNumber, hash: provenBlockData.blockHash.toString() },
63
85
  checkpoint: provenCheckpointId,
64
86
  },
87
+ proposedCheckpoint: {
88
+ block: { number: proposedCheckpointBlockNumber, hash: proposedCheckpointBlockData.blockHash.toString() },
89
+ checkpoint: proposedCheckpointId,
90
+ },
65
91
  finalized: {
66
92
  block: { number: finalizedBlockNumber, hash: finalizedBlockData.blockHash.toString() },
67
93
  checkpoint: finalizedCheckpointId,
@@ -73,6 +99,19 @@ export class L2TipsCache {
73
99
  };
74
100
  }
75
101
 
102
+ private async getCheckpointIdForProposedCheckpoint(
103
+ checkpointedBlockData: Pick<BlockData, 'checkpointNumber'>,
104
+ ): Promise<CheckpointId> {
105
+ const checkpointData = await this.blockStore.getProposedCheckpointOnly();
106
+ if (!checkpointData) {
107
+ return this.getCheckpointIdForBlock(checkpointedBlockData);
108
+ }
109
+ return {
110
+ number: checkpointData.checkpointNumber,
111
+ hash: checkpointData.header.hash().toString(),
112
+ };
113
+ }
114
+
76
115
  private async getCheckpointIdForBlock(blockData: Pick<BlockData, 'checkpointNumber'>): Promise<CheckpointId> {
77
116
  const checkpointData = await this.blockStore.getCheckpointData(blockData.checkpointNumber);
78
117
  if (!checkpointData) {
@@ -161,15 +161,6 @@ export class MessageStore {
161
161
  lastMessage = message;
162
162
  }
163
163
 
164
- // Update the L1 sync point to that of the last message added.
165
- const currentSyncPoint = await this.getSynchedL1Block();
166
- if (!currentSyncPoint || currentSyncPoint.l1BlockNumber < lastMessage!.l1BlockNumber) {
167
- await this.setSynchedL1Block({
168
- l1BlockNumber: lastMessage!.l1BlockNumber,
169
- l1BlockHash: lastMessage!.l1BlockHash,
170
- });
171
- }
172
-
173
164
  // Update total message count with the number of inserted messages.
174
165
  await this.increaseTotalMessageCount(messageCount);
175
166
  });
@@ -194,9 +185,16 @@ export class MessageStore {
194
185
  return this.#inboxTreeInProgress.getAsync();
195
186
  }
196
187
 
197
- /** Persists the inbox tree-in-progress checkpoint number from L1 state. */
198
- public async setInboxTreeInProgress(value: bigint): Promise<void> {
199
- await this.#inboxTreeInProgress.set(value);
188
+ /** Atomically updates the message sync state: the L1 sync point and the inbox tree-in-progress marker. */
189
+ public setMessageSyncState(l1Block: L1BlockId, treeInProgress: bigint | undefined): Promise<void> {
190
+ return this.db.transactionAsync(async () => {
191
+ await this.setSynchedL1Block(l1Block);
192
+ if (treeInProgress !== undefined) {
193
+ await this.#inboxTreeInProgress.set(treeInProgress);
194
+ } else {
195
+ await this.#inboxTreeInProgress.delete();
196
+ }
197
+ });
200
198
  }
201
199
 
202
200
  public async getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise<Fr[]> {
@@ -8,7 +8,7 @@ export type InboxMessage = {
8
8
  index: bigint;
9
9
  leaf: Fr;
10
10
  checkpointNumber: CheckpointNumber;
11
- l1BlockNumber: bigint; // L1 block number - NOT Aztec L2
11
+ l1BlockNumber: bigint;
12
12
  l1BlockHash: Buffer32;
13
13
  rollingHash: Buffer16;
14
14
  };
@@ -332,6 +332,21 @@ export class FakeL1State {
332
332
  this.updatePendingCheckpointNumber();
333
333
  }
334
334
 
335
+ /**
336
+ * Moves a checkpoint to a different L1 block number (simulates L1 reorg that
337
+ * re-includes the same checkpoint transaction in a different block).
338
+ * The checkpoint content stays the same — only the L1 metadata changes.
339
+ * Auto-updates pending status.
340
+ */
341
+ moveCheckpointToL1Block(checkpointNumber: CheckpointNumber, newL1BlockNumber: bigint): void {
342
+ for (const cpData of this.checkpoints) {
343
+ if (cpData.checkpointNumber === checkpointNumber) {
344
+ cpData.l1BlockNumber = newL1BlockNumber;
345
+ }
346
+ }
347
+ this.updatePendingCheckpointNumber();
348
+ }
349
+
335
350
  /**
336
351
  * Removes messages after a given total index (simulates L1 reorg).
337
352
  * Auto-updates rolling hash.
@@ -451,19 +466,33 @@ export class FakeL1State {
451
466
  createMockInboxContract(_publicClient: MockProxy<ViemPublicClient>): MockProxy<InboxContract> {
452
467
  const mockInbox = mock<InboxContract>();
453
468
 
454
- mockInbox.getState.mockImplementation(() => {
469
+ mockInbox.getState.mockImplementation((opts: { blockTag?: string; blockNumber?: bigint } = {}) => {
470
+ // Filter messages visible at the given block number (or all if not specified)
471
+ const blockNumber = opts.blockNumber ?? this.l1BlockNumber;
472
+ const visibleMessages = this.messages.filter(m => m.l1BlockNumber <= blockNumber);
473
+
455
474
  // treeInProgress must be > any sealed checkpoint. On L1, a checkpoint can only be proposed
456
475
  // after its messages are sealed, so treeInProgress > checkpointNumber for all published checkpoints.
457
476
  const maxFromMessages =
458
- this.messages.length > 0 ? Math.max(...this.messages.map(m => Number(m.checkpointNumber))) + 1 : 0;
477
+ visibleMessages.length > 0 ? Math.max(...visibleMessages.map(m => Number(m.checkpointNumber))) + 1 : 0;
459
478
  const maxFromCheckpoints =
460
479
  this.checkpoints.length > 0
461
- ? Math.max(...this.checkpoints.filter(cp => !cp.pruned).map(cp => Number(cp.checkpointNumber))) + 1
480
+ ? Math.max(
481
+ ...this.checkpoints
482
+ .filter(cp => !cp.pruned && cp.l1BlockNumber <= blockNumber)
483
+ .map(cp => Number(cp.checkpointNumber)),
484
+ 0,
485
+ ) + 1
462
486
  : 0;
463
487
  const treeInProgress = Math.max(maxFromMessages, maxFromCheckpoints, INITIAL_CHECKPOINT_NUMBER);
488
+
489
+ // Compute rolling hash only for visible messages
490
+ const rollingHash =
491
+ visibleMessages.length > 0 ? visibleMessages[visibleMessages.length - 1].rollingHash : Buffer16.ZERO;
492
+
464
493
  return Promise.resolve({
465
- messagesRollingHash: this.messagesRollingHash,
466
- totalMessagesInserted: BigInt(this.messages.length),
494
+ messagesRollingHash: rollingHash,
495
+ totalMessagesInserted: BigInt(visibleMessages.length),
467
496
  treeInProgress: BigInt(treeInProgress),
468
497
  });
469
498
  });
@@ -473,8 +502,8 @@ export class FakeL1State {
473
502
  Promise.resolve(this.getMessageSentLogs(fromBlock, toBlock)),
474
503
  );
475
504
 
476
- mockInbox.getMessageSentEventByHash.mockImplementation((hash: string, fromBlock: bigint, toBlock: bigint) =>
477
- Promise.resolve(this.getMessageSentLogs(fromBlock, toBlock, hash)),
505
+ mockInbox.getMessageSentEventByHash.mockImplementation((msgHash: string, l1BlockHash: string) =>
506
+ Promise.resolve(this.getMessageSentLogByHash(msgHash, l1BlockHash) as MessageSentLog),
478
507
  );
479
508
 
480
509
  return mockInbox;
@@ -594,6 +623,26 @@ export class FakeL1State {
594
623
  }));
595
624
  }
596
625
 
626
+ private getMessageSentLogByHash(msgHash: string, l1BlockHash: string): MessageSentLog | undefined {
627
+ const msg = this.messages.find(
628
+ msg => msg.leaf.toString() === msgHash && Buffer32.fromBigInt(msg.l1BlockNumber).toString() === l1BlockHash,
629
+ );
630
+ if (!msg) {
631
+ return undefined;
632
+ }
633
+ return {
634
+ l1BlockNumber: msg.l1BlockNumber,
635
+ l1BlockHash: Buffer32.fromBigInt(msg.l1BlockNumber),
636
+ l1TransactionHash: `0x${msg.l1BlockNumber.toString(16)}` as `0x${string}`,
637
+ args: {
638
+ checkpointNumber: msg.checkpointNumber,
639
+ index: msg.index,
640
+ leaf: msg.leaf,
641
+ rollingHash: msg.rollingHash,
642
+ },
643
+ };
644
+ }
645
+
597
646
  private async makeRollupTx(
598
647
  checkpoint: Checkpoint,
599
648
  signers: Secp256k1Signer[],
@@ -44,6 +44,7 @@ export class MockL1ToL2MessageSource implements L1ToL2MessageSource {
44
44
  checkpointed: tip,
45
45
  proven: tip,
46
46
  finalized: tip,
47
+ proposedCheckpoint: tip,
47
48
  });
48
49
  }
49
50
  }
@@ -16,7 +16,13 @@ import {
16
16
  type L2Tips,
17
17
  type ValidateCheckpointResult,
18
18
  } from '@aztec/stdlib/block';
19
- import { Checkpoint, type CheckpointData, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
19
+ import {
20
+ Checkpoint,
21
+ type CheckpointData,
22
+ L1PublishedData,
23
+ type ProposedCheckpointData,
24
+ PublishedCheckpoint,
25
+ } from '@aztec/stdlib/checkpoint';
20
26
  import type { ContractClassPublic, ContractDataSource, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
21
27
  import {
22
28
  EmptyL1RollupConstants,
@@ -39,6 +45,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
39
45
  private provenBlockNumber: number = 0;
40
46
  private finalizedBlockNumber: number = 0;
41
47
  private checkpointedBlockNumber: number = 0;
48
+ private proposedCheckpointBlockNumber: number = 0;
42
49
 
43
50
  private log = createLogger('archiver:mock_l2_block_source');
44
51
 
@@ -89,6 +96,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
89
96
  });
90
97
  // Keep tip numbers consistent with remaining blocks.
91
98
  this.checkpointedBlockNumber = Math.min(this.checkpointedBlockNumber, maxBlockNum);
99
+ this.proposedCheckpointBlockNumber = Math.min(this.proposedCheckpointBlockNumber, maxBlockNum);
92
100
  this.provenBlockNumber = Math.min(this.provenBlockNumber, maxBlockNum);
93
101
  this.finalizedBlockNumber = Math.min(this.finalizedBlockNumber, maxBlockNum);
94
102
  this.log.verbose(`Removed ${numBlocks} blocks from the mock L2 block source`);
@@ -105,9 +113,17 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
105
113
  this.finalizedBlockNumber = finalizedBlockNumber;
106
114
  }
107
115
 
116
+ public setProposedCheckpointBlockNumber(blockNumber: number) {
117
+ this.proposedCheckpointBlockNumber = blockNumber;
118
+ }
119
+
108
120
  public setCheckpointedBlockNumber(checkpointedBlockNumber: number) {
109
121
  const prevCheckpointed = this.checkpointedBlockNumber;
110
122
  this.checkpointedBlockNumber = checkpointedBlockNumber;
123
+ // Proposed checkpoint is always at least as advanced as checkpointed
124
+ if (this.proposedCheckpointBlockNumber < checkpointedBlockNumber) {
125
+ this.proposedCheckpointBlockNumber = checkpointedBlockNumber;
126
+ }
111
127
  // Auto-create single-block checkpoints for newly checkpointed blocks that don't have one yet.
112
128
  // This handles blocks added via addProposedBlocks that are now being marked as checkpointed.
113
129
  const newCheckpoints: Checkpoint[] = [];
@@ -171,6 +187,10 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
171
187
  return Promise.resolve(BlockNumber(this.finalizedBlockNumber));
172
188
  }
173
189
 
190
+ public getProposedCheckpointL2BlockNumber() {
191
+ return Promise.resolve(BlockNumber(this.proposedCheckpointBlockNumber));
192
+ }
193
+
174
194
  public getCheckpointedBlock(number: BlockNumber): Promise<CheckpointedL2Block | undefined> {
175
195
  if (number > this.checkpointedBlockNumber) {
176
196
  return Promise.resolve(undefined);
@@ -408,17 +428,19 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
408
428
  }
409
429
 
410
430
  async getL2Tips(): Promise<L2Tips> {
411
- const [latest, proven, finalized, checkpointed] = [
431
+ const [latest, proven, finalized, checkpointed, proposedCheckpoint] = [
412
432
  await this.getBlockNumber(),
413
433
  await this.getProvenBlockNumber(),
414
434
  this.finalizedBlockNumber,
415
435
  this.checkpointedBlockNumber,
436
+ await this.getProposedCheckpointL2BlockNumber(),
416
437
  ] as const;
417
438
 
418
439
  const latestBlock = this.l2Blocks[latest - 1];
419
440
  const provenBlock = this.l2Blocks[proven - 1];
420
441
  const finalizedBlock = this.l2Blocks[finalized - 1];
421
442
  const checkpointedBlock = this.l2Blocks[checkpointed - 1];
443
+ const proposedCheckpointBlock = this.l2Blocks[proposedCheckpoint - 1];
422
444
 
423
445
  const latestBlockId = {
424
446
  number: BlockNumber(latest),
@@ -436,6 +458,10 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
436
458
  number: BlockNumber(checkpointed),
437
459
  hash: (await checkpointedBlock?.hash())?.toString(),
438
460
  };
461
+ const proposedCheckpointBlockId = {
462
+ number: BlockNumber(proposedCheckpoint),
463
+ hash: (await proposedCheckpointBlock?.hash())?.toString(),
464
+ };
439
465
 
440
466
  const makeTipId = (blockId: typeof latestBlockId) => ({
441
467
  block: blockId,
@@ -450,6 +476,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
450
476
  checkpointed: makeTipId(checkpointedBlockId),
451
477
  proven: makeTipId(provenBlockId),
452
478
  finalized: makeTipId(finalizedBlockId),
479
+ proposedCheckpoint: makeTipId(proposedCheckpointBlockId),
453
480
  };
454
481
  }
455
482
 
@@ -531,6 +558,14 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
531
558
  return Promise.resolve({ valid: true });
532
559
  }
533
560
 
561
+ getProposedCheckpoint(): Promise<ProposedCheckpointData | undefined> {
562
+ return Promise.resolve(undefined);
563
+ }
564
+
565
+ getProposedCheckpointOnly(): Promise<ProposedCheckpointData | undefined> {
566
+ return Promise.resolve(undefined);
567
+ }
568
+
534
569
  /** Returns checkpoints whose slot falls within the given epoch. */
535
570
  private getCheckpointsInEpoch(epochNumber: EpochNumber): Checkpoint[] {
536
571
  const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
@@ -72,7 +72,6 @@ export class NoopL1Archiver extends Archiver {
72
72
  {
73
73
  registryAddress: EthAddress.ZERO,
74
74
  governanceProposerAddress: EthAddress.ZERO,
75
- slashFactoryAddress: EthAddress.ZERO,
76
75
  slashingProposerAddress: EthAddress.ZERO,
77
76
  },
78
77
  dataStore,