@aztec/archiver 0.0.1-commit.cd76b27 → 0.0.1-commit.ce4f8c4f2

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 (88) hide show
  1. package/dest/archiver.d.ts +3 -4
  2. package/dest/archiver.d.ts.map +1 -1
  3. package/dest/archiver.js +67 -23
  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 +21 -9
  8. package/dest/errors.d.ts.map +1 -1
  9. package/dest/errors.js +27 -14
  10. package/dest/factory.d.ts +4 -5
  11. package/dest/factory.d.ts.map +1 -1
  12. package/dest/factory.js +25 -25
  13. package/dest/l1/bin/retrieve-calldata.js +32 -28
  14. package/dest/l1/calldata_retriever.d.ts +70 -53
  15. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  16. package/dest/l1/calldata_retriever.js +178 -260
  17. package/dest/l1/data_retrieval.d.ts +7 -8
  18. package/dest/l1/data_retrieval.d.ts.map +1 -1
  19. package/dest/l1/data_retrieval.js +18 -17
  20. package/dest/l1/spire_proposer.d.ts +5 -5
  21. package/dest/l1/spire_proposer.d.ts.map +1 -1
  22. package/dest/l1/spire_proposer.js +9 -17
  23. package/dest/modules/data_source_base.d.ts +5 -5
  24. package/dest/modules/data_source_base.d.ts.map +1 -1
  25. package/dest/modules/data_source_base.js +5 -5
  26. package/dest/modules/data_store_updater.d.ts +17 -12
  27. package/dest/modules/data_store_updater.d.ts.map +1 -1
  28. package/dest/modules/data_store_updater.js +78 -77
  29. package/dest/modules/instrumentation.d.ts +12 -1
  30. package/dest/modules/instrumentation.d.ts.map +1 -1
  31. package/dest/modules/instrumentation.js +10 -0
  32. package/dest/modules/l1_synchronizer.d.ts +3 -7
  33. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  34. package/dest/modules/l1_synchronizer.js +46 -11
  35. package/dest/store/block_store.d.ts +12 -13
  36. package/dest/store/block_store.d.ts.map +1 -1
  37. package/dest/store/block_store.js +61 -61
  38. package/dest/store/contract_class_store.d.ts +2 -3
  39. package/dest/store/contract_class_store.d.ts.map +1 -1
  40. package/dest/store/contract_class_store.js +7 -67
  41. package/dest/store/contract_instance_store.d.ts +1 -1
  42. package/dest/store/contract_instance_store.d.ts.map +1 -1
  43. package/dest/store/contract_instance_store.js +6 -2
  44. package/dest/store/kv_archiver_store.d.ts +28 -18
  45. package/dest/store/kv_archiver_store.d.ts.map +1 -1
  46. package/dest/store/kv_archiver_store.js +34 -21
  47. package/dest/store/log_store.d.ts +6 -3
  48. package/dest/store/log_store.d.ts.map +1 -1
  49. package/dest/store/log_store.js +93 -16
  50. package/dest/store/message_store.d.ts +5 -1
  51. package/dest/store/message_store.d.ts.map +1 -1
  52. package/dest/store/message_store.js +14 -1
  53. package/dest/test/fake_l1_state.d.ts +10 -1
  54. package/dest/test/fake_l1_state.d.ts.map +1 -1
  55. package/dest/test/fake_l1_state.js +81 -15
  56. package/dest/test/mock_l2_block_source.d.ts +4 -3
  57. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  58. package/dest/test/mock_l2_block_source.js +7 -4
  59. package/dest/test/mock_structs.d.ts +4 -1
  60. package/dest/test/mock_structs.d.ts.map +1 -1
  61. package/dest/test/mock_structs.js +13 -1
  62. package/dest/test/noop_l1_archiver.d.ts +4 -1
  63. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  64. package/dest/test/noop_l1_archiver.js +5 -1
  65. package/package.json +13 -13
  66. package/src/archiver.ts +80 -23
  67. package/src/config.ts +8 -1
  68. package/src/errors.ts +40 -24
  69. package/src/factory.ts +23 -16
  70. package/src/l1/README.md +25 -68
  71. package/src/l1/bin/retrieve-calldata.ts +40 -27
  72. package/src/l1/calldata_retriever.ts +231 -383
  73. package/src/l1/data_retrieval.ts +20 -25
  74. package/src/l1/spire_proposer.ts +7 -15
  75. package/src/modules/data_source_base.ts +11 -6
  76. package/src/modules/data_store_updater.ts +83 -107
  77. package/src/modules/instrumentation.ts +20 -0
  78. package/src/modules/l1_synchronizer.ts +55 -20
  79. package/src/store/block_store.ts +72 -69
  80. package/src/store/contract_class_store.ts +8 -106
  81. package/src/store/contract_instance_store.ts +8 -5
  82. package/src/store/kv_archiver_store.ts +43 -32
  83. package/src/store/log_store.ts +126 -27
  84. package/src/store/message_store.ts +20 -1
  85. package/src/test/fake_l1_state.ts +110 -19
  86. package/src/test/mock_l2_block_source.ts +15 -3
  87. package/src/test/mock_structs.ts +20 -6
  88. package/src/test/noop_l1_archiver.ts +7 -1
@@ -1,5 +1,6 @@
1
1
  import type { BlobClientInterface } from '@aztec/blob-client/client';
2
2
  import { type Blob, getBlobsPerL1Block, getPrefixedEthBlobCommitments } from '@aztec/blob-lib';
3
+ import { INITIAL_CHECKPOINT_NUMBER } from '@aztec/constants';
3
4
  import type { CheckpointProposedLog, InboxContract, MessageSentLog, RollupContract } from '@aztec/ethereum/contracts';
4
5
  import { MULTI_CALL_3_ADDRESS } from '@aztec/ethereum/contracts';
5
6
  import type { ViemPublicClient } from '@aztec/ethereum/types';
@@ -14,6 +15,7 @@ import { CommitteeAttestation, CommitteeAttestationsAndSigners, L2Block } from '
14
15
  import { Checkpoint } from '@aztec/stdlib/checkpoint';
15
16
  import { getSlotAtTimestamp } from '@aztec/stdlib/epoch-helpers';
16
17
  import { InboxLeaf } from '@aztec/stdlib/messaging';
18
+ import { ConsensusPayload, SignatureDomainSeparator } from '@aztec/stdlib/p2p';
17
19
  import {
18
20
  makeAndSignCommitteeAttestationsAndSigners,
19
21
  makeCheckpointAttestationFromCheckpoint,
@@ -22,7 +24,16 @@ import {
22
24
  import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
23
25
 
24
26
  import { type MockProxy, mock } from 'jest-mock-extended';
25
- import { type FormattedBlock, type Transaction, encodeFunctionData, multicall3Abi, toHex } from 'viem';
27
+ import {
28
+ type AbiParameter,
29
+ type FormattedBlock,
30
+ type Transaction,
31
+ encodeAbiParameters,
32
+ encodeFunctionData,
33
+ keccak256,
34
+ multicall3Abi,
35
+ toHex,
36
+ } from 'viem';
26
37
 
27
38
  import { updateRollingHash } from '../structs/inbox_message.js';
28
39
 
@@ -87,6 +98,10 @@ type CheckpointData = {
87
98
  blobHashes: `0x${string}`[];
88
99
  blobs: Blob[];
89
100
  signers: Secp256k1Signer[];
101
+ /** Hash of the packed attestations, matching what the L1 event emits. */
102
+ attestationsHash: Buffer32;
103
+ /** Payload digest, matching what the L1 event emits. */
104
+ payloadDigest: Buffer32;
90
105
  /** If true, archiveAt will ignore it */
91
106
  pruned?: boolean;
92
107
  };
@@ -136,8 +151,12 @@ export class FakeL1State {
136
151
  // Computed from checkpoints based on L1 block visibility
137
152
  private pendingCheckpointNumber: CheckpointNumber = CheckpointNumber(0);
138
153
 
154
+ // The L1 block number reported as "finalized" (defaults to the start block)
155
+ private finalizedL1BlockNumber: bigint;
156
+
139
157
  constructor(private readonly config: FakeL1StateConfig) {
140
158
  this.l1BlockNumber = config.l1StartBlock;
159
+ this.finalizedL1BlockNumber = config.l1StartBlock;
141
160
  this.lastArchive = new AppendOnlyTreeSnapshot(config.genesisArchiveRoot, 1);
142
161
  }
143
162
 
@@ -194,8 +213,8 @@ export class FakeL1State {
194
213
  // Store the messages internally so they match the checkpoint's inHash
195
214
  this.addMessages(checkpointNumber, messagesL1BlockNumber, messages);
196
215
 
197
- // Create the transaction and blobs
198
- const tx = await this.makeRollupTx(checkpoint, signers);
216
+ // Create the transaction, blobs, and event hashes
217
+ const { tx, attestationsHash, payloadDigest } = await this.makeRollupTx(checkpoint, signers);
199
218
  const blobHashes = await this.makeVersionedBlobHashes(checkpoint);
200
219
  const blobs = await this.makeBlobsFromCheckpoint(checkpoint);
201
220
 
@@ -208,6 +227,8 @@ export class FakeL1State {
208
227
  blobHashes,
209
228
  blobs,
210
229
  signers,
230
+ attestationsHash,
231
+ payloadDigest,
211
232
  });
212
233
 
213
234
  // Update last archive for auto-chaining
@@ -267,11 +288,30 @@ export class FakeL1State {
267
288
  this.updatePendingCheckpointNumber();
268
289
  }
269
290
 
291
+ /** Sets the L1 block number that will be reported as "finalized". */
292
+ setFinalizedL1BlockNumber(blockNumber: bigint): void {
293
+ this.finalizedL1BlockNumber = blockNumber;
294
+ }
295
+
270
296
  /** Marks a checkpoint as proven. Updates provenCheckpointNumber. */
271
297
  markCheckpointAsProven(checkpointNumber: CheckpointNumber): void {
272
298
  this.provenCheckpointNumber = checkpointNumber;
273
299
  }
274
300
 
301
+ /**
302
+ * Simulates what `rollup.getProvenCheckpointNumber({ blockNumber: atL1Block })` would return.
303
+ */
304
+ getProvenCheckpointNumberAtL1Block(atL1Block: bigint): CheckpointNumber {
305
+ if (this.provenCheckpointNumber === 0) {
306
+ return CheckpointNumber(0);
307
+ }
308
+ const checkpoint = this.checkpoints.find(cp => cp.checkpointNumber === this.provenCheckpointNumber);
309
+ if (checkpoint && checkpoint.l1BlockNumber <= atL1Block) {
310
+ return this.provenCheckpointNumber;
311
+ }
312
+ return CheckpointNumber(0);
313
+ }
314
+
275
315
  /** Sets the target committee size for attestation validation. */
276
316
  setTargetCommitteeSize(size: number): void {
277
317
  this.targetCommitteeSize = size;
@@ -390,6 +430,11 @@ export class FakeL1State {
390
430
  });
391
431
  });
392
432
 
433
+ mockRollup.getProvenCheckpointNumber.mockImplementation((options?: { blockNumber?: bigint }) => {
434
+ const atBlock = options?.blockNumber ?? this.l1BlockNumber;
435
+ return Promise.resolve(this.getProvenCheckpointNumberAtL1Block(atBlock));
436
+ });
437
+
393
438
  mockRollup.canPruneAtTime.mockImplementation(() => Promise.resolve(this.canPruneResult));
394
439
 
395
440
  // Mock the wrapper method for fetching checkpoint events
@@ -406,13 +451,22 @@ export class FakeL1State {
406
451
  createMockInboxContract(_publicClient: MockProxy<ViemPublicClient>): MockProxy<InboxContract> {
407
452
  const mockInbox = mock<InboxContract>();
408
453
 
409
- mockInbox.getState.mockImplementation(() =>
410
- Promise.resolve({
454
+ mockInbox.getState.mockImplementation(() => {
455
+ // treeInProgress must be > any sealed checkpoint. On L1, a checkpoint can only be proposed
456
+ // after its messages are sealed, so treeInProgress > checkpointNumber for all published checkpoints.
457
+ const maxFromMessages =
458
+ this.messages.length > 0 ? Math.max(...this.messages.map(m => Number(m.checkpointNumber))) + 1 : 0;
459
+ const maxFromCheckpoints =
460
+ this.checkpoints.length > 0
461
+ ? Math.max(...this.checkpoints.filter(cp => !cp.pruned).map(cp => Number(cp.checkpointNumber))) + 1
462
+ : 0;
463
+ const treeInProgress = Math.max(maxFromMessages, maxFromCheckpoints, INITIAL_CHECKPOINT_NUMBER);
464
+ return Promise.resolve({
411
465
  messagesRollingHash: this.messagesRollingHash,
412
466
  totalMessagesInserted: BigInt(this.messages.length),
413
- treeInProgress: 0n,
414
- }),
415
- );
467
+ treeInProgress: BigInt(treeInProgress),
468
+ });
469
+ });
416
470
 
417
471
  // Mock the wrapper methods for fetching message events
418
472
  mockInbox.getMessageSentEvents.mockImplementation((fromBlock: bigint, toBlock: bigint) =>
@@ -433,10 +487,13 @@ export class FakeL1State {
433
487
  publicClient.getChainId.mockResolvedValue(1);
434
488
  publicClient.getBlockNumber.mockImplementation(() => Promise.resolve(this.l1BlockNumber));
435
489
 
436
- // Use async function pattern that existing test uses for getBlock
437
-
438
- publicClient.getBlock.mockImplementation((async (args: { blockNumber?: bigint } = {}) => {
439
- const blockNum = args.blockNumber ?? (await publicClient.getBlockNumber());
490
+ publicClient.getBlock.mockImplementation((async (args: { blockNumber?: bigint; blockTag?: string } = {}) => {
491
+ let blockNum: bigint;
492
+ if (args.blockTag === 'finalized') {
493
+ blockNum = this.finalizedL1BlockNumber;
494
+ } else {
495
+ blockNum = args.blockNumber ?? (await publicClient.getBlockNumber());
496
+ }
440
497
  return {
441
498
  number: blockNum,
442
499
  timestamp: BigInt(blockNum) * BigInt(this.config.ethereumSlotDuration) + this.config.l1GenesisTime,
@@ -510,10 +567,8 @@ export class FakeL1State {
510
567
  checkpointNumber: cpData.checkpointNumber,
511
568
  archive: cpData.checkpoint.archive.root,
512
569
  versionedBlobHashes: cpData.blobHashes.map(h => Buffer.from(h.slice(2), 'hex')),
513
- // These are intentionally undefined to skip hash validation in the archiver
514
- // (validation is skipped when these fields are falsy)
515
- payloadDigest: undefined,
516
- attestationsHash: undefined,
570
+ attestationsHash: cpData.attestationsHash,
571
+ payloadDigest: cpData.payloadDigest,
517
572
  },
518
573
  }));
519
574
  }
@@ -539,7 +594,10 @@ export class FakeL1State {
539
594
  }));
540
595
  }
541
596
 
542
- private async makeRollupTx(checkpoint: Checkpoint, signers: Secp256k1Signer[]): Promise<Transaction> {
597
+ private async makeRollupTx(
598
+ checkpoint: Checkpoint,
599
+ signers: Secp256k1Signer[],
600
+ ): Promise<{ tx: Transaction; attestationsHash: Buffer32; payloadDigest: Buffer32 }> {
543
601
  const attestations = signers
544
602
  .map(signer => makeCheckpointAttestationFromCheckpoint(checkpoint, signer))
545
603
  .map(attestation => CommitteeAttestation.fromSignature(attestation.signature))
@@ -557,6 +615,8 @@ export class FakeL1State {
557
615
  signers[0],
558
616
  );
559
617
 
618
+ const packedAttestations = attestationsAndSigners.getPackedAttestations();
619
+
560
620
  const rollupInput = encodeFunctionData({
561
621
  abi: RollupAbi,
562
622
  functionName: 'propose',
@@ -566,7 +626,7 @@ export class FakeL1State {
566
626
  archive,
567
627
  oracleInput: { feeAssetPriceModifier: 0n },
568
628
  },
569
- attestationsAndSigners.getPackedAttestations(),
629
+ packedAttestations,
570
630
  attestationsAndSigners.getSigners().map(signer => signer.toString()),
571
631
  attestationsAndSignersSignature.toViemSignature(),
572
632
  blobInput,
@@ -587,12 +647,43 @@ export class FakeL1State {
587
647
  ],
588
648
  });
589
649
 
590
- return {
650
+ // Compute attestationsHash (same logic as CalldataRetriever)
651
+ const attestationsHash = Buffer32.fromString(
652
+ keccak256(encodeAbiParameters([this.getCommitteeAttestationsStructDef()], [packedAttestations])),
653
+ );
654
+
655
+ // Compute payloadDigest (same logic as CalldataRetriever)
656
+ const consensusPayload = ConsensusPayload.fromCheckpoint(checkpoint);
657
+ const payloadToSign = consensusPayload.getPayloadToSign(SignatureDomainSeparator.checkpointAttestation);
658
+ const payloadDigest = Buffer32.fromString(keccak256(payloadToSign));
659
+
660
+ const tx = {
591
661
  input: multiCallInput,
592
662
  hash: archive,
593
663
  blockHash: archive,
594
664
  to: MULTI_CALL_3_ADDRESS as `0x${string}`,
595
665
  } as Transaction<bigint, number>;
666
+
667
+ return { tx, attestationsHash, payloadDigest };
668
+ }
669
+
670
+ /** Extracts the CommitteeAttestations struct definition from RollupAbi for hash computation. */
671
+ private getCommitteeAttestationsStructDef(): AbiParameter {
672
+ const proposeFunction = RollupAbi.find(item => item.type === 'function' && item.name === 'propose') as
673
+ | { type: 'function'; name: string; inputs: readonly AbiParameter[] }
674
+ | undefined;
675
+
676
+ if (!proposeFunction) {
677
+ throw new Error('propose function not found in RollupAbi');
678
+ }
679
+
680
+ const attestationsParam = proposeFunction.inputs.find(param => param.name === '_attestations');
681
+ if (!attestationsParam) {
682
+ throw new Error('_attestations parameter not found in propose function');
683
+ }
684
+
685
+ const tupleParam = attestationsParam as unknown as { type: 'tuple'; components?: readonly AbiParameter[] };
686
+ return { type: 'tuple', components: tupleParam.components || [] } as AbiParameter;
596
687
  }
597
688
 
598
689
  private async makeVersionedBlobHashes(checkpoint: Checkpoint): Promise<`0x${string}`[]> {
@@ -18,7 +18,12 @@ import {
18
18
  } from '@aztec/stdlib/block';
19
19
  import { Checkpoint, type CheckpointData, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
20
20
  import type { ContractClassPublic, ContractDataSource, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
21
- import { EmptyL1RollupConstants, type L1RollupConstants, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
21
+ import {
22
+ EmptyL1RollupConstants,
23
+ type L1RollupConstants,
24
+ getEpochAtSlot,
25
+ getSlotRangeForEpoch,
26
+ } from '@aztec/stdlib/epoch-helpers';
22
27
  import { computeCheckpointOutHash } from '@aztec/stdlib/messaging';
23
28
  import { CheckpointHeader } from '@aztec/stdlib/rollup';
24
29
  import { type BlockHeader, TxExecutionResult, TxHash, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
@@ -42,6 +47,12 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
42
47
  await this.createCheckpoints(numBlocks, 1);
43
48
  }
44
49
 
50
+ public getCheckpointNumber(): Promise<CheckpointNumber> {
51
+ return Promise.resolve(
52
+ this.checkpointList.length === 0 ? CheckpointNumber.ZERO : CheckpointNumber(this.checkpointList.length),
53
+ );
54
+ }
55
+
45
56
  /** Creates checkpoints, each containing `blocksPerCheckpoint` blocks. */
46
57
  public async createCheckpoints(numCheckpoints: number, blocksPerCheckpoint: number = 1) {
47
58
  for (let c = 0; c < numCheckpoints; c++) {
@@ -388,6 +399,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
388
399
  txEffect.transactionFee.toBigInt(),
389
400
  await block.hash(),
390
401
  block.number,
402
+ getEpochAtSlot(block.slot, EmptyL1RollupConstants),
391
403
  );
392
404
  }
393
405
  }
@@ -441,11 +453,11 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
441
453
  };
442
454
  }
443
455
 
444
- getL2EpochNumber(): Promise<EpochNumber> {
456
+ getSyncedL2EpochNumber(): Promise<EpochNumber> {
445
457
  throw new Error('Method not implemented.');
446
458
  }
447
459
 
448
- getL2SlotNumber(): Promise<SlotNumber> {
460
+ getSyncedL2SlotNumber(): Promise<SlotNumber> {
449
461
  throw new Error('Method not implemented.');
450
462
  }
451
463
 
@@ -127,6 +127,25 @@ export function makeL1PublishedData(l1BlockNumber: number): L1PublishedData {
127
127
  return new L1PublishedData(BigInt(l1BlockNumber), BigInt(l1BlockNumber * 1000), makeBlockHash(l1BlockNumber));
128
128
  }
129
129
 
130
+ /** Creates a Checkpoint from a list of blocks with a header that matches the blocks' structure. */
131
+ export function makeCheckpoint(blocks: L2Block[], checkpointNumber = CheckpointNumber(1)): Checkpoint {
132
+ const firstBlock = blocks[0];
133
+ const { slotNumber, timestamp, coinbase, feeRecipient, gasFees } = firstBlock.header.globalVariables;
134
+ return new Checkpoint(
135
+ blocks.at(-1)!.archive,
136
+ CheckpointHeader.random({
137
+ lastArchiveRoot: firstBlock.header.lastArchive.root,
138
+ slotNumber,
139
+ timestamp,
140
+ coinbase,
141
+ feeRecipient,
142
+ gasFees,
143
+ }),
144
+ blocks,
145
+ checkpointNumber,
146
+ );
147
+ }
148
+
130
149
  /** Wraps a Checkpoint with L1 published data and random attestations. */
131
150
  export function makePublishedCheckpoint(
132
151
  checkpoint: Checkpoint,
@@ -301,11 +320,6 @@ export async function makeCheckpointWithLogs(
301
320
  return txEffect;
302
321
  });
303
322
 
304
- const checkpoint = new Checkpoint(
305
- AppendOnlyTreeSnapshot.random(),
306
- CheckpointHeader.random(),
307
- [block],
308
- CheckpointNumber.fromBlockNumber(BlockNumber(blockNumber)),
309
- );
323
+ const checkpoint = makeCheckpoint([block], CheckpointNumber.fromBlockNumber(BlockNumber(blockNumber)));
310
324
  return makePublishedCheckpoint(checkpoint, blockNumber);
311
325
  }
@@ -1,6 +1,7 @@
1
1
  import type { BlobClientInterface } from '@aztec/blob-client/client';
2
2
  import type { RollupContract } from '@aztec/ethereum/contracts';
3
3
  import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
4
+ import { SlotNumber } from '@aztec/foundation/branded-types';
4
5
  import { Buffer32 } from '@aztec/foundation/buffer';
5
6
  import { Fr } from '@aztec/foundation/curves/bn254';
6
7
  import { EthAddress } from '@aztec/foundation/eth-address';
@@ -30,7 +31,7 @@ class NoopL1Synchronizer implements FunctionsOf<ArchiverL1Synchronizer> {
30
31
  return 0n;
31
32
  }
32
33
  getL1Timestamp(): bigint | undefined {
33
- return 0n;
34
+ return undefined;
34
35
  }
35
36
  testEthereumNodeSynced(): Promise<void> {
36
37
  return Promise.resolve();
@@ -96,6 +97,11 @@ export class NoopL1Archiver extends Archiver {
96
97
  this.runningPromise.start();
97
98
  return Promise.resolve();
98
99
  }
100
+
101
+ /** Always reports as fully synced since there is no real L1 to sync from. */
102
+ public override getSyncedL2SlotNumber(): Promise<SlotNumber | undefined> {
103
+ return Promise.resolve(SlotNumber(Number.MAX_SAFE_INTEGER));
104
+ }
99
105
  }
100
106
 
101
107
  /** Creates an archiver with mocked L1 connectivity for testing. */