@aztec/archiver 5.0.0-rc.1 → 5.0.0

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 (82) hide show
  1. package/dest/archiver.d.ts +1 -1
  2. package/dest/archiver.d.ts.map +1 -1
  3. package/dest/archiver.js +39 -10
  4. package/dest/config.d.ts +1 -1
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +5 -0
  7. package/dest/factory.d.ts +8 -1
  8. package/dest/factory.d.ts.map +1 -1
  9. package/dest/factory.js +31 -0
  10. package/dest/l1/calldata_retriever.d.ts +8 -1
  11. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  12. package/dest/l1/calldata_retriever.js +7 -6
  13. package/dest/l1/data_retrieval.d.ts +7 -2
  14. package/dest/l1/data_retrieval.d.ts.map +1 -1
  15. package/dest/modules/contract_data_source_adapter.d.ts +2 -2
  16. package/dest/modules/contract_data_source_adapter.d.ts.map +1 -1
  17. package/dest/modules/contract_data_source_adapter.js +1 -9
  18. package/dest/modules/data_source_base.d.ts +2 -3
  19. package/dest/modules/data_source_base.d.ts.map +1 -1
  20. package/dest/modules/data_source_base.js +9 -35
  21. package/dest/modules/data_store_updater.d.ts +1 -1
  22. package/dest/modules/data_store_updater.d.ts.map +1 -1
  23. package/dest/modules/data_store_updater.js +4 -3
  24. package/dest/modules/instrumentation.d.ts +9 -1
  25. package/dest/modules/instrumentation.d.ts.map +1 -1
  26. package/dest/modules/instrumentation.js +23 -2
  27. package/dest/modules/l1_synchronizer.d.ts +3 -3
  28. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  29. package/dest/modules/l1_synchronizer.js +78 -41
  30. package/dest/modules/validation.d.ts +29 -7
  31. package/dest/modules/validation.d.ts.map +1 -1
  32. package/dest/modules/validation.js +31 -13
  33. package/dest/store/block_store.d.ts +20 -11
  34. package/dest/store/block_store.d.ts.map +1 -1
  35. package/dest/store/block_store.js +43 -25
  36. package/dest/store/contract_class_store.d.ts +1 -1
  37. package/dest/store/contract_class_store.d.ts.map +1 -1
  38. package/dest/store/contract_class_store.js +19 -1
  39. package/dest/store/contract_instance_store.d.ts +1 -1
  40. package/dest/store/contract_instance_store.d.ts.map +1 -1
  41. package/dest/store/contract_instance_store.js +18 -1
  42. package/dest/store/log_store.d.ts +1 -1
  43. package/dest/store/log_store.d.ts.map +1 -1
  44. package/dest/store/log_store.js +3 -3
  45. package/dest/store/log_store_codec.d.ts +9 -1
  46. package/dest/store/log_store_codec.d.ts.map +1 -1
  47. package/dest/store/log_store_codec.js +9 -0
  48. package/dest/test/fake_l1_state.js +3 -3
  49. package/dest/test/mock_l1_to_l2_message_source.d.ts +1 -1
  50. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  51. package/dest/test/mock_l1_to_l2_message_source.js +1 -2
  52. package/dest/test/mock_l2_block_source.d.ts +1 -4
  53. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  54. package/dest/test/mock_l2_block_source.js +8 -22
  55. package/dest/test/mock_structs.d.ts +1 -1
  56. package/dest/test/mock_structs.d.ts.map +1 -1
  57. package/dest/test/mock_structs.js +2 -2
  58. package/dest/test/noop_l1_archiver.d.ts +1 -1
  59. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  60. package/dest/test/noop_l1_archiver.js +1 -1
  61. package/package.json +14 -13
  62. package/src/archiver.ts +44 -14
  63. package/src/config.ts +8 -0
  64. package/src/factory.ts +34 -0
  65. package/src/l1/calldata_retriever.ts +13 -6
  66. package/src/l1/data_retrieval.ts +8 -1
  67. package/src/modules/contract_data_source_adapter.ts +1 -10
  68. package/src/modules/data_source_base.ts +9 -38
  69. package/src/modules/data_store_updater.ts +4 -3
  70. package/src/modules/instrumentation.ts +15 -2
  71. package/src/modules/l1_synchronizer.ts +120 -57
  72. package/src/modules/validation.ts +71 -15
  73. package/src/store/block_store.ts +42 -31
  74. package/src/store/contract_class_store.ts +19 -1
  75. package/src/store/contract_instance_store.ts +18 -1
  76. package/src/store/log_store.ts +3 -2
  77. package/src/store/log_store_codec.ts +11 -0
  78. package/src/test/fake_l1_state.ts +3 -3
  79. package/src/test/mock_l1_to_l2_message_source.ts +0 -1
  80. package/src/test/mock_l2_block_source.ts +8 -22
  81. package/src/test/mock_structs.ts +5 -2
  82. package/src/test/noop_l1_archiver.ts +3 -3
@@ -306,12 +306,18 @@ export class BlockStore {
306
306
 
307
307
  /**
308
308
  * Append new checkpoints to the store's list.
309
+ * Checkpoints at the start of the batch that are already stored (e.g. re-included by an L1 reorg)
310
+ * are accepted if their archive root matches: their L1 metadata is updated but they are not
311
+ * re-inserted, and they are excluded from the returned array.
309
312
  * @param checkpoints - The L2 checkpoints to be added to the store.
310
- * @returns True if the operation is successful.
313
+ * @returns The checkpoints that were actually inserted (excluding already-stored ones).
311
314
  */
312
- async addCheckpoints(checkpoints: PublishedCheckpoint[], opts: { force?: boolean } = {}): Promise<boolean> {
315
+ async addCheckpoints(
316
+ checkpoints: PublishedCheckpoint[],
317
+ opts: { force?: boolean } = {},
318
+ ): Promise<PublishedCheckpoint[]> {
313
319
  if (checkpoints.length === 0) {
314
- return true;
320
+ return [];
315
321
  }
316
322
 
317
323
  return await this.db.transactionAsync(async () => {
@@ -324,7 +330,7 @@ export class BlockStore {
324
330
  if (!opts.force && firstCheckpointNumber <= previousCheckpointNumber) {
325
331
  checkpoints = await this.skipOrUpdateAlreadyStoredCheckpoints(checkpoints, previousCheckpointNumber);
326
332
  if (checkpoints.length === 0) {
327
- return true;
333
+ return [];
328
334
  }
329
335
  // Re-check sequentiality after skipping
330
336
  const newFirstNumber = checkpoints[0].checkpoint.number;
@@ -387,7 +393,7 @@ export class BlockStore {
387
393
  }
388
394
 
389
395
  await this.advanceSynchedL1BlockNumber(checkpoints[checkpoints.length - 1].l1.blockNumber);
390
- return true;
396
+ return checkpoints;
391
397
  });
392
398
  }
393
399
 
@@ -621,6 +627,26 @@ export class BlockStore {
621
627
  return checkpoints;
622
628
  }
623
629
 
630
+ /**
631
+ * Returns up to `limit` checkpoints anchored at `fromSlot`, ordered nearest-first, walking the slot index.
632
+ * With `reverse`, takes the checkpoints at or before `fromSlot` (descending by slot); otherwise the
633
+ * checkpoints at or after it (ascending). `limit: 1, reverse: true` yields the latest checkpoint at or
634
+ * before the slot in a single range scan.
635
+ */
636
+ async getCheckpointsBySlot(fromSlot: SlotNumber, limit: number, reverse: boolean): Promise<CheckpointData[]> {
637
+ // The KV range bounds are direction-dependent: forward is [start, end), reverse is (start, end], so a
638
+ // reverse scan uses `end: fromSlot` (inclusive) with no +1 to include the checkpoint at fromSlot itself.
639
+ const range = reverse ? { end: fromSlot, reverse: true, limit } : { start: fromSlot, limit };
640
+ const result: CheckpointData[] = [];
641
+ for await (const [, checkpointNumber] of this.#slotToCheckpoint.entriesAsync(range)) {
642
+ const checkpointStorage = await this.#checkpoints.getAsync(checkpointNumber);
643
+ if (checkpointStorage) {
644
+ result.push(this.checkpointDataFromCheckpointStorage(checkpointStorage));
645
+ }
646
+ }
647
+ return result;
648
+ }
649
+
624
650
  /** Returns checkpoint data for all checkpoints whose slot falls within the given range (inclusive). */
625
651
  async getCheckpointDataForSlotRange(startSlot: SlotNumber, endSlot: SlotNumber): Promise<CheckpointData[]> {
626
652
  const result: CheckpointData[] = [];
@@ -1167,14 +1193,13 @@ export class BlockStore {
1167
1193
  }
1168
1194
 
1169
1195
  /**
1170
- * Resolves all five L2 chain tips (proposed, proposedCheckpoint, checkpointed, proven, finalized)
1171
- * in a single read-only transaction so the snapshot is internally consistent. Each underlying
1172
- * record is read at most once: latest block, latest confirmed checkpoint, and latest pending
1173
- * checkpoint are each loaded directly (no separate "find the number, then look up data" hop),
1174
- * the proven/finalized checkpoint singletons are read once and their storage entries are
1175
- * reused if they coincide with the latest checkpoint, and per-tip block hashes are deduped
1176
- * when two tips land on the same block (e.g. finalized == proven, or proposedCheckpoint falls
1177
- * back to checkpointed when no pending checkpoint exists).
1196
+ * Resolves all four L2 chain tips (proposed, checkpointed, proven, finalized) in a single
1197
+ * read-only transaction so the snapshot is internally consistent. Each underlying record is
1198
+ * read at most once: latest block and latest confirmed checkpoint are loaded directly (no
1199
+ * separate "find the number, then look up data" hop), the proven/finalized checkpoint
1200
+ * singletons are read once and their storage entries are reused if they coincide with the
1201
+ * latest checkpoint, and per-tip block hashes are deduped when two tips land on the same block
1202
+ * (e.g. finalized == proven).
1178
1203
  *
1179
1204
  * The result is guaranteed to satisfy `finalized <= proven <= checkpointed <= proposed` (by
1180
1205
  * block number). Genesis is represented by `(INITIAL_L2_BLOCK_NUM - 1)` and the supplied
@@ -1197,9 +1222,6 @@ export class BlockStore {
1197
1222
 
1198
1223
  // Load latest block and checkpoint entries
1199
1224
  const [latestBlockEntry] = await toArray(this.#blocks.entriesAsync({ reverse: true, limit: 1 }));
1200
- const [proposedCheckpointEntry] = await toArray(
1201
- this.#proposedCheckpoints.entriesAsync({ reverse: true, limit: 1 }),
1202
- );
1203
1225
  const [latestCheckpointEntry] = await toArray(this.#checkpoints.entriesAsync({ reverse: true, limit: 1 }));
1204
1226
  const latestCheckpointNumber = latestCheckpointEntry
1205
1227
  ? CheckpointNumber(latestCheckpointEntry[0])
@@ -1285,14 +1307,6 @@ export class BlockStore {
1285
1307
  const provenTip = await buildTipFromCheckpoint(provenCheckpoint);
1286
1308
  const finalizedTip = await buildTipFromCheckpoint(finalizedCheckpoint);
1287
1309
 
1288
- // Proposed checkpoint falls back to the checkpoint tip if it's not set. And if local storage is
1289
- // inconsistent and the proposed checkpoint is behind the checkpointed tip, we patch that and
1290
- // report the checkpointed tip as the proposed checkpoint to maintain the invariant.
1291
- const proposedCheckpointTip =
1292
- proposedCheckpointEntry === undefined || proposedCheckpointEntry[0] <= latestCheckpointNumber
1293
- ? checkpointedTip
1294
- : await buildTipFromCheckpoint(proposedCheckpointEntry[1]);
1295
-
1296
1310
  // A checkpointed block past the latest stored block would mean a checkpoint
1297
1311
  // references blocks that aren't in blocks.
1298
1312
  if (proposedBlockId.number < checkpointedTip.block.number) {
@@ -1304,11 +1318,10 @@ export class BlockStore {
1304
1318
  // Assert that checkpoint numbers are increasing
1305
1319
  if (
1306
1320
  finalizedTip.checkpoint.number > provenTip.checkpoint.number ||
1307
- provenTip.checkpoint.number > checkpointedTip.checkpoint.number ||
1308
- checkpointedTip.checkpoint.number > proposedCheckpointTip.checkpoint.number
1321
+ provenTip.checkpoint.number > checkpointedTip.checkpoint.number
1309
1322
  ) {
1310
1323
  throw new Error(
1311
- `Inconsistent checkpoint numbers in chain tips: finalized=${finalizedTip.checkpoint.number} proven=${provenTip.checkpoint.number} checkpointed=${checkpointedTip.checkpoint.number} proposed=${proposedCheckpointTip.checkpoint.number}`,
1324
+ `Inconsistent checkpoint numbers in chain tips: finalized=${finalizedTip.checkpoint.number} proven=${provenTip.checkpoint.number} checkpointed=${checkpointedTip.checkpoint.number}`,
1312
1325
  );
1313
1326
  }
1314
1327
 
@@ -1316,17 +1329,15 @@ export class BlockStore {
1316
1329
  if (
1317
1330
  finalizedTip.block.number > provenTip.block.number ||
1318
1331
  provenTip.block.number > checkpointedTip.block.number ||
1319
- checkpointedTip.block.number > proposedCheckpointTip.block.number ||
1320
- proposedCheckpointTip.block.number > proposedBlockId.number
1332
+ checkpointedTip.block.number > proposedBlockId.number
1321
1333
  ) {
1322
1334
  throw new Error(
1323
- `Inconsistent block numbers in chain tips: finalized=${finalizedTip.block.number} proven=${provenTip.block.number} checkpointed=${checkpointedTip.block.number} proposedCheckpoint=${proposedCheckpointTip.block.number} proposed=${proposedBlockId.number}`,
1335
+ `Inconsistent block numbers in chain tips: finalized=${finalizedTip.block.number} proven=${provenTip.block.number} checkpointed=${checkpointedTip.block.number} proposed=${proposedBlockId.number}`,
1324
1336
  );
1325
1337
  }
1326
1338
 
1327
1339
  return {
1328
1340
  proposed: proposedBlockId,
1329
- proposedCheckpoint: proposedCheckpointTip,
1330
1341
  checkpointed: checkpointedTip,
1331
1342
  proven: provenTip,
1332
1343
  finalized: finalizedTip,
@@ -2,6 +2,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
2
2
  import { toArray } from '@aztec/foundation/iterable';
3
3
  import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize';
4
4
  import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
5
+ import { isProtocolContractClass } from '@aztec/protocol-contracts';
5
6
  import type {
6
7
  ContractClassPublic,
7
8
  ContractClassPublicWithBlockNumber,
@@ -49,7 +50,19 @@ export class ContractClassStore {
49
50
  ): Promise<void> {
50
51
  await this.db.transactionAsync(async () => {
51
52
  const key = contractClass.id.toString();
52
- if (await this.#contractClasses.hasAsync(key)) {
53
+ const existing = await this.#contractClasses.getAsync(key);
54
+ if (existing !== undefined) {
55
+ // Protocol contracts are preloaded at block 0, so a later on-chain (re-)publish of a bundled
56
+ // protocol class id is valid and must be a no-op. Keep the existing block-0 entry untouched
57
+ // (do not bump its block number) so it survives reorgs of the publishing block.
58
+ if (isProtocolContractClass(contractClass.id)) {
59
+ return;
60
+ }
61
+ // An L1 reorg can re-present an already-stored checkpoint, replaying this class at the same
62
+ // block; treat that as a no-op. A duplicate at a different block still signals double-processing.
63
+ if (deserializeContractClassPublic(existing).l2BlockNumber === blockNumber) {
64
+ return;
65
+ }
53
66
  throw new Error(`Contract class ${key} already exists, cannot add again at block ${blockNumber}`);
54
67
  }
55
68
  await this.#contractClasses.set(
@@ -61,6 +74,11 @@ export class ContractClassStore {
61
74
  }
62
75
 
63
76
  async deleteContractClass(contractClass: ContractClassPublic, blockNumber: number): Promise<void> {
77
+ // Protocol contracts are preloaded at block 0 and must never be deleted, even when the block that
78
+ // (re-)published them on-chain is unwound by a reorg.
79
+ if (isProtocolContractClass(contractClass.id)) {
80
+ return;
81
+ }
64
82
  const restoredContractClass = await this.#contractClasses.getAsync(contractClass.id.toString());
65
83
  if (restoredContractClass && deserializeContractClassPublic(restoredContractClass).l2BlockNumber >= blockNumber) {
66
84
  await this.db.transactionAsync(async () => {
@@ -1,5 +1,6 @@
1
1
  import type { Fr } from '@aztec/foundation/curves/bn254';
2
2
  import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
3
+ import { isProtocolContract } from '@aztec/protocol-contracts';
3
4
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
4
5
  import {
5
6
  type ContractInstanceUpdateWithAddress,
@@ -72,8 +73,19 @@ export class ContractInstanceStore {
72
73
  return this.db.transactionAsync(async () => {
73
74
  const key = contractInstance.address.toString();
74
75
  if (await this.#contractInstances.hasAsync(key)) {
76
+ // Protocol contracts are preloaded at block 0, so a later on-chain (re-)publish of a bundled
77
+ // protocol instance is valid and must be a no-op. Keep the existing block-0 entry untouched.
78
+ if (isProtocolContract(contractInstance.address)) {
79
+ return;
80
+ }
81
+ const existingBlockNumber = await this.#contractInstancePublishedAt.getAsync(key);
82
+ // An L1 reorg can re-present an already-stored checkpoint, replaying this instance at the same
83
+ // block; treat that as a no-op. A duplicate at a different block still signals double-processing.
84
+ if (existingBlockNumber === blockNumber) {
85
+ return;
86
+ }
75
87
  throw new Error(
76
- `Contract instance at ${key} already exists (deployed at block ${await this.#contractInstancePublishedAt.getAsync(key)}), cannot add again at block ${blockNumber}`,
88
+ `Contract instance at ${key} already exists (deployed at block ${existingBlockNumber}), cannot add again at block ${blockNumber}`,
77
89
  );
78
90
  }
79
91
  await this.#contractInstances.set(key, new SerializableContractInstance(contractInstance).toBuffer());
@@ -82,6 +94,11 @@ export class ContractInstanceStore {
82
94
  }
83
95
 
84
96
  deleteContractInstance(contractInstance: ContractInstanceWithAddress): Promise<void> {
97
+ // Protocol contracts are preloaded at block 0 and must never be deleted, even when the block that
98
+ // (re-)published them on-chain is unwound by a reorg.
99
+ if (isProtocolContract(contractInstance.address)) {
100
+ return Promise.resolve();
101
+ }
85
102
  return this.db.transactionAsync(async () => {
86
103
  await this.#contractInstances.delete(contractInstance.address.toString());
87
104
  await this.#contractInstancePublishedAt.delete(contractInstance.address.toString());
@@ -26,6 +26,7 @@ import {
26
26
  endOfTxRange,
27
27
  fieldHex,
28
28
  incKey,
29
+ tagHexForLog,
29
30
  } from './log_store_codec.js';
30
31
 
31
32
  /**
@@ -100,7 +101,7 @@ export class LogStore {
100
101
  let publicLogIndexWithinTx = 0;
101
102
 
102
103
  for (const log of txEffect.privateLogs) {
103
- const tagHex = fieldHex(log.fields[0]);
104
+ const tagHex = tagHexForLog(log.fields);
104
105
  const key = encodeKey(tagHex, blockNumber, txIndexWithinBlock, privateLogIndexWithinTx);
105
106
  const value = encodeValue({
106
107
  txHash,
@@ -115,7 +116,7 @@ export class LogStore {
115
116
 
116
117
  for (const log of txEffect.publicLogs) {
117
118
  const contractHex = fieldHex(log.contractAddress);
118
- const tagHex = fieldHex(log.fields[0]);
119
+ const tagHex = tagHexForLog(log.fields);
119
120
  const key = encodeKey(
120
121
  encodePublicPrefix(contractHex, tagHex),
121
122
  blockNumber,
@@ -37,6 +37,17 @@ export function fieldHex(value: Fr | { toString: () => string }): string {
37
37
  return value.toString().slice(2);
38
38
  }
39
39
 
40
+ /**
41
+ * Tag prefix for a log: the hex of its first field, or the empty string when the log carries no fields.
42
+ * A protocol-valid public log can have zero fields (e.g. a raw AVM `EMITPUBLICLOG` with `logSize = 0`),
43
+ * which has no tag to index by. Encoding it under the empty tag keeps it retrievable via the per-block
44
+ * read (and droppable on reorg) while never matching a real 64-hex-char tag query — instead of reading
45
+ * `fields[0]` off an empty array and aborting the whole block-ingestion transaction.
46
+ */
47
+ export function tagHexForLog(fields: Fr[]): string {
48
+ return fields.length > 0 ? fieldHex(fields[0]) : '';
49
+ }
50
+
40
51
  /** Encodes a number as 8-char zero-padded lowercase hex (matches a u32 big-endian byte buffer's lex order). */
41
52
  export function u32Hex(n: number): string {
42
53
  return n.toString(16).padStart(NUMERIC_HEX_LEN, '0');
@@ -682,7 +682,7 @@ export class FakeL1State {
682
682
  getHashedSignaturePayloadTypedData(attestationsAndSigners),
683
683
  );
684
684
 
685
- const packedAttestations = attestationsAndSigners.getPackedAttestations();
685
+ const verbatimAttestations = attestationsAndSigners.getPackedAttestations();
686
686
 
687
687
  const rollupInput = encodeFunctionData({
688
688
  abi: RollupAbi,
@@ -693,7 +693,7 @@ export class FakeL1State {
693
693
  archive,
694
694
  oracleInput: { feeAssetPriceModifier: 0n },
695
695
  },
696
- packedAttestations,
696
+ verbatimAttestations,
697
697
  attestationsAndSigners.getSigners().map(signer => signer.toString()),
698
698
  attestationsAndSignersSignature.toViemSignature(),
699
699
  blobInput,
@@ -716,7 +716,7 @@ export class FakeL1State {
716
716
 
717
717
  // Compute attestationsHash (same logic as CalldataRetriever)
718
718
  const attestationsHash = Buffer32.fromString(
719
- keccak256(encodeAbiParameters([this.getCommitteeAttestationsStructDef()], [packedAttestations])),
719
+ keccak256(encodeAbiParameters([this.getCommitteeAttestationsStructDef()], [verbatimAttestations])),
720
720
  );
721
721
 
722
722
  // Compute payloadDigest (same logic as CalldataRetriever)
@@ -44,7 +44,6 @@ export class MockL1ToL2MessageSource implements L1ToL2MessageSource {
44
44
  checkpointed: tip,
45
45
  proven: tip,
46
46
  finalized: tip,
47
- proposedCheckpoint: tip,
48
47
  });
49
48
  }
50
49
  }
@@ -55,7 +55,6 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
55
55
  private provenBlockNumber: number = 0;
56
56
  private finalizedBlockNumber: number = 0;
57
57
  private checkpointedBlockNumber: number = 0;
58
- private proposedCheckpointBlockNumber: number = 0;
59
58
 
60
59
  private initialHeader: BlockHeader = BlockHeader.empty();
61
60
  private initialHeaderHash: BlockHash = GENESIS_BLOCK_HEADER_HASH;
@@ -164,7 +163,6 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
164
163
  });
165
164
  // Keep tip numbers consistent with remaining blocks.
166
165
  this.checkpointedBlockNumber = Math.min(this.checkpointedBlockNumber, maxBlockNum);
167
- this.proposedCheckpointBlockNumber = Math.min(this.proposedCheckpointBlockNumber, maxBlockNum);
168
166
  this.provenBlockNumber = Math.min(this.provenBlockNumber, maxBlockNum);
169
167
  this.finalizedBlockNumber = Math.min(this.finalizedBlockNumber, maxBlockNum);
170
168
  this.log.verbose(`Removed ${numBlocks} blocks from the mock L2 block source`);
@@ -181,17 +179,9 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
181
179
  this.finalizedBlockNumber = finalizedBlockNumber;
182
180
  }
183
181
 
184
- public setProposedCheckpointBlockNumber(blockNumber: number) {
185
- this.proposedCheckpointBlockNumber = blockNumber;
186
- }
187
-
188
182
  public setCheckpointedBlockNumber(checkpointedBlockNumber: number) {
189
183
  const prevCheckpointed = this.checkpointedBlockNumber;
190
184
  this.checkpointedBlockNumber = checkpointedBlockNumber;
191
- // Proposed checkpoint is always at least as advanced as checkpointed
192
- if (this.proposedCheckpointBlockNumber < checkpointedBlockNumber) {
193
- this.proposedCheckpointBlockNumber = checkpointedBlockNumber;
194
- }
195
185
  // Auto-create single-block checkpoints for newly checkpointed blocks that don't have one yet.
196
186
  // This handles blocks added via addProposedBlocks that are now being marked as checkpointed.
197
187
  const newCheckpoints: Checkpoint[] = [];
@@ -255,10 +245,6 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
255
245
  return block ? block.header.globalVariables.blockNumber : undefined;
256
246
  }
257
247
 
258
- public getProposedCheckpointL2BlockNumber() {
259
- return Promise.resolve(BlockNumber(this.proposedCheckpointBlockNumber));
260
- }
261
-
262
248
  public getCheckpoint(query: CheckpointQuery): Promise<PublishedCheckpoint | undefined> {
263
249
  const checkpoint = this.resolveCheckpointQuery(query);
264
250
  if (!checkpoint) {
@@ -337,6 +323,13 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
337
323
  if ('from' in query) {
338
324
  return this.checkpointList.slice(query.from - 1, query.from - 1 + query.limit);
339
325
  }
326
+ if ('fromSlot' in query) {
327
+ const matching = this.checkpointList.filter(c =>
328
+ query.reverse ? c.header.slotNumber <= query.fromSlot : c.header.slotNumber >= query.fromSlot,
329
+ );
330
+ const nearestFirst = query.reverse ? matching.reverse() : matching;
331
+ return nearestFirst.slice(0, query.limit);
332
+ }
340
333
  return this.getCheckpointsInEpoch(query.epoch);
341
334
  }
342
335
 
@@ -373,19 +366,17 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
373
366
  }
374
367
 
375
368
  async getL2Tips(): Promise<L2Tips> {
376
- const [latest, proven, finalized, checkpointed, proposedCheckpoint] = [
369
+ const [latest, proven, finalized, checkpointed] = [
377
370
  await this.getBlockNumber(),
378
371
  this.provenBlockNumber,
379
372
  this.finalizedBlockNumber,
380
373
  this.checkpointedBlockNumber,
381
- await this.getProposedCheckpointL2BlockNumber(),
382
374
  ] as const;
383
375
 
384
376
  const latestBlock = this.l2Blocks[latest - 1];
385
377
  const provenBlock = this.l2Blocks[proven - 1];
386
378
  const finalizedBlock = this.l2Blocks[finalized - 1];
387
379
  const checkpointedBlock = this.l2Blocks[checkpointed - 1];
388
- const proposedCheckpointBlock = this.l2Blocks[proposedCheckpoint - 1];
389
380
 
390
381
  // For genesis tips (block number 0) report the dynamic initial header hash so consumers
391
382
  // running L2BlockStream against this mock agree at block 0 with their local tip store.
@@ -413,10 +404,6 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
413
404
  number: BlockNumber(checkpointed),
414
405
  hash: await tipHash(checkpointedBlock, checkpointed),
415
406
  };
416
- const proposedCheckpointBlockId = {
417
- number: BlockNumber(proposedCheckpoint),
418
- hash: await tipHash(proposedCheckpointBlock, proposedCheckpoint),
419
- };
420
407
 
421
408
  const makeTipId = (blockId: typeof latestBlockId) => {
422
409
  const checkpointNumber = this.findCheckpointNumberForBlock(blockId.number) ?? CheckpointNumber(0);
@@ -435,7 +422,6 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
435
422
  checkpointed: makeTipId(checkpointedBlockId),
436
423
  proven: makeTipId(provenBlockId),
437
424
  finalized: makeTipId(finalizedBlockId),
438
- proposedCheckpoint: makeTipId(proposedCheckpointBlockId),
439
425
  };
440
426
  }
441
427
 
@@ -269,7 +269,10 @@ export function makePublicLogTag(blockNumber: number, txIndex: number, logIndex:
269
269
  }
270
270
 
271
271
  /** Creates a PublicLog with fields derived from the tag. */
272
- export function makePublicLog(tag: Tag, contractAddress: AztecAddress = AztecAddress.fromNumber(543254)): PublicLog {
272
+ export function makePublicLog(
273
+ tag: Tag,
274
+ contractAddress: AztecAddress = AztecAddress.fromNumberUnsafe(543254),
275
+ ): PublicLog {
273
276
  return PublicLog.from({
274
277
  contractAddress,
275
278
  fields: new Array(10).fill(null).map((_, i) => (!i ? tag.value : new Fr(tag.value.toBigInt() + BigInt(i)))),
@@ -281,7 +284,7 @@ export function makePublicLogs(
281
284
  blockNumber: number,
282
285
  txIndex: number,
283
286
  numLogsPerTx: number,
284
- contractAddress: AztecAddress = AztecAddress.fromNumber(543254),
287
+ contractAddress: AztecAddress = AztecAddress.fromNumberUnsafe(543254),
285
288
  ): PublicLog[] {
286
289
  return times(numLogsPerTx, logIndex => {
287
290
  const tag = makePublicLogTag(blockNumber, txIndex, logIndex);
@@ -7,7 +7,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
7
7
  import { EthAddress } from '@aztec/foundation/eth-address';
8
8
  import { DateProvider } from '@aztec/foundation/timer';
9
9
  import type { FunctionsOf } from '@aztec/foundation/types';
10
- import type { ArchiverEmitter, BlockHash } from '@aztec/stdlib/block';
10
+ import type { ArchiverEmitter, BlockHash, L2Block } from '@aztec/stdlib/block';
11
11
  import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
12
12
  import type { BlockHeader } from '@aztec/stdlib/tx';
13
13
  import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/telemetry-client';
@@ -43,8 +43,8 @@ class NoopL1Synchronizer implements FunctionsOf<ArchiverL1Synchronizer> {
43
43
  testEthereumNodeSynced(): Promise<void> {
44
44
  return Promise.resolve();
45
45
  }
46
- syncFromL1(_initialSyncComplete: boolean): Promise<void> {
47
- return Promise.resolve();
46
+ syncFromL1(_initialSyncComplete: boolean): Promise<L2Block[]> {
47
+ return Promise.resolve([]);
48
48
  }
49
49
  }
50
50