@aztec/archiver 0.0.1-commit.e3c1de76 → 0.0.1-commit.e588bc7e5

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 (110) hide show
  1. package/README.md +12 -6
  2. package/dest/archiver.d.ts +9 -6
  3. package/dest/archiver.d.ts.map +1 -1
  4. package/dest/archiver.js +78 -113
  5. package/dest/config.d.ts +3 -3
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +2 -1
  8. package/dest/errors.d.ts +34 -10
  9. package/dest/errors.d.ts.map +1 -1
  10. package/dest/errors.js +45 -16
  11. package/dest/factory.d.ts +4 -5
  12. package/dest/factory.d.ts.map +1 -1
  13. package/dest/factory.js +31 -26
  14. package/dest/index.d.ts +2 -1
  15. package/dest/index.d.ts.map +1 -1
  16. package/dest/index.js +1 -0
  17. package/dest/l1/bin/retrieve-calldata.js +36 -33
  18. package/dest/l1/calldata_retriever.d.ts +73 -50
  19. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  20. package/dest/l1/calldata_retriever.js +191 -259
  21. package/dest/l1/data_retrieval.d.ts +10 -10
  22. package/dest/l1/data_retrieval.d.ts.map +1 -1
  23. package/dest/l1/data_retrieval.js +35 -34
  24. package/dest/l1/spire_proposer.d.ts +5 -5
  25. package/dest/l1/spire_proposer.d.ts.map +1 -1
  26. package/dest/l1/spire_proposer.js +9 -17
  27. package/dest/modules/data_source_base.d.ts +14 -7
  28. package/dest/modules/data_source_base.d.ts.map +1 -1
  29. package/dest/modules/data_source_base.js +39 -77
  30. package/dest/modules/data_store_updater.d.ts +25 -12
  31. package/dest/modules/data_store_updater.d.ts.map +1 -1
  32. package/dest/modules/data_store_updater.js +125 -94
  33. package/dest/modules/instrumentation.d.ts +15 -2
  34. package/dest/modules/instrumentation.d.ts.map +1 -1
  35. package/dest/modules/instrumentation.js +19 -2
  36. package/dest/modules/l1_synchronizer.d.ts +7 -9
  37. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  38. package/dest/modules/l1_synchronizer.js +176 -136
  39. package/dest/modules/validation.d.ts +1 -1
  40. package/dest/modules/validation.d.ts.map +1 -1
  41. package/dest/modules/validation.js +2 -2
  42. package/dest/store/block_store.d.ts +65 -28
  43. package/dest/store/block_store.d.ts.map +1 -1
  44. package/dest/store/block_store.js +311 -134
  45. package/dest/store/contract_class_store.d.ts +2 -3
  46. package/dest/store/contract_class_store.d.ts.map +1 -1
  47. package/dest/store/contract_class_store.js +7 -67
  48. package/dest/store/contract_instance_store.d.ts +1 -1
  49. package/dest/store/contract_instance_store.d.ts.map +1 -1
  50. package/dest/store/contract_instance_store.js +6 -2
  51. package/dest/store/kv_archiver_store.d.ts +61 -24
  52. package/dest/store/kv_archiver_store.d.ts.map +1 -1
  53. package/dest/store/kv_archiver_store.js +75 -27
  54. package/dest/store/l2_tips_cache.d.ts +20 -0
  55. package/dest/store/l2_tips_cache.d.ts.map +1 -0
  56. package/dest/store/l2_tips_cache.js +109 -0
  57. package/dest/store/log_store.d.ts +6 -3
  58. package/dest/store/log_store.d.ts.map +1 -1
  59. package/dest/store/log_store.js +148 -51
  60. package/dest/store/message_store.d.ts +5 -1
  61. package/dest/store/message_store.d.ts.map +1 -1
  62. package/dest/store/message_store.js +21 -9
  63. package/dest/test/fake_l1_state.d.ts +14 -1
  64. package/dest/test/fake_l1_state.d.ts.map +1 -1
  65. package/dest/test/fake_l1_state.js +120 -26
  66. package/dest/test/mock_archiver.d.ts +1 -1
  67. package/dest/test/mock_archiver.d.ts.map +1 -1
  68. package/dest/test/mock_archiver.js +3 -2
  69. package/dest/test/mock_l1_to_l2_message_source.d.ts +1 -1
  70. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  71. package/dest/test/mock_l1_to_l2_message_source.js +2 -1
  72. package/dest/test/mock_l2_block_source.d.ts +26 -5
  73. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  74. package/dest/test/mock_l2_block_source.js +160 -89
  75. package/dest/test/mock_structs.d.ts +4 -1
  76. package/dest/test/mock_structs.d.ts.map +1 -1
  77. package/dest/test/mock_structs.js +13 -1
  78. package/dest/test/noop_l1_archiver.d.ts +4 -1
  79. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  80. package/dest/test/noop_l1_archiver.js +5 -1
  81. package/package.json +13 -13
  82. package/src/archiver.ts +97 -133
  83. package/src/config.ts +8 -1
  84. package/src/errors.ts +70 -26
  85. package/src/factory.ts +46 -24
  86. package/src/index.ts +1 -0
  87. package/src/l1/README.md +25 -68
  88. package/src/l1/bin/retrieve-calldata.ts +46 -39
  89. package/src/l1/calldata_retriever.ts +250 -379
  90. package/src/l1/data_retrieval.ts +31 -37
  91. package/src/l1/spire_proposer.ts +7 -15
  92. package/src/modules/data_source_base.ts +78 -98
  93. package/src/modules/data_store_updater.ts +138 -124
  94. package/src/modules/instrumentation.ts +29 -2
  95. package/src/modules/l1_synchronizer.ts +195 -168
  96. package/src/modules/validation.ts +2 -2
  97. package/src/store/block_store.ts +393 -170
  98. package/src/store/contract_class_store.ts +8 -106
  99. package/src/store/contract_instance_store.ts +8 -5
  100. package/src/store/kv_archiver_store.ts +115 -41
  101. package/src/store/l2_tips_cache.ts +128 -0
  102. package/src/store/log_store.ts +219 -58
  103. package/src/store/message_store.ts +27 -10
  104. package/src/structs/inbox_message.ts +1 -1
  105. package/src/test/fake_l1_state.ts +163 -30
  106. package/src/test/mock_archiver.ts +3 -2
  107. package/src/test/mock_l1_to_l2_message_source.ts +1 -0
  108. package/src/test/mock_l2_block_source.ts +209 -82
  109. package/src/test/mock_structs.ts +20 -6
  110. package/src/test/noop_l1_archiver.ts +7 -1
@@ -8,6 +8,7 @@ import { createLogger } from '@aztec/foundation/log';
8
8
  import type { FunctionSelector } from '@aztec/stdlib/abi';
9
9
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
10
10
  import {
11
+ type BlockData,
11
12
  BlockHash,
12
13
  CheckpointedL2Block,
13
14
  L2Block,
@@ -15,9 +16,22 @@ import {
15
16
  type L2Tips,
16
17
  type ValidateCheckpointResult,
17
18
  } from '@aztec/stdlib/block';
18
- import { Checkpoint, 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';
19
26
  import type { ContractClassPublic, ContractDataSource, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
20
- import { EmptyL1RollupConstants, type L1RollupConstants, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
27
+ import {
28
+ EmptyL1RollupConstants,
29
+ type L1RollupConstants,
30
+ getEpochAtSlot,
31
+ getSlotRangeForEpoch,
32
+ } from '@aztec/stdlib/epoch-helpers';
33
+ import { computeCheckpointOutHash } from '@aztec/stdlib/messaging';
34
+ import { CheckpointHeader } from '@aztec/stdlib/rollup';
21
35
  import { type BlockHeader, TxExecutionResult, TxHash, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
22
36
  import type { UInt64 } from '@aztec/stdlib/types';
23
37
 
@@ -26,21 +40,45 @@ import type { UInt64 } from '@aztec/stdlib/types';
26
40
  */
27
41
  export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
28
42
  protected l2Blocks: L2Block[] = [];
43
+ protected checkpointList: Checkpoint[] = [];
29
44
 
30
45
  private provenBlockNumber: number = 0;
31
46
  private finalizedBlockNumber: number = 0;
32
47
  private checkpointedBlockNumber: number = 0;
48
+ private proposedCheckpointBlockNumber: number = 0;
33
49
 
34
50
  private log = createLogger('archiver:mock_l2_block_source');
35
51
 
52
+ /** Creates blocks grouped into single-block checkpoints. */
36
53
  public async createBlocks(numBlocks: number) {
37
- for (let i = 0; i < numBlocks; i++) {
38
- const blockNum = this.l2Blocks.length + 1;
39
- const block = await L2Block.random(BlockNumber(blockNum), { slotNumber: SlotNumber(blockNum) });
40
- this.l2Blocks.push(block);
54
+ await this.createCheckpoints(numBlocks, 1);
55
+ }
56
+
57
+ public getCheckpointNumber(): Promise<CheckpointNumber> {
58
+ return Promise.resolve(
59
+ this.checkpointList.length === 0 ? CheckpointNumber.ZERO : CheckpointNumber(this.checkpointList.length),
60
+ );
61
+ }
62
+
63
+ /** Creates checkpoints, each containing `blocksPerCheckpoint` blocks. */
64
+ public async createCheckpoints(numCheckpoints: number, blocksPerCheckpoint: number = 1) {
65
+ for (let c = 0; c < numCheckpoints; c++) {
66
+ const checkpointNum = CheckpointNumber(this.checkpointList.length + 1);
67
+ const startBlockNum = this.l2Blocks.length + 1;
68
+ const slotNumber = SlotNumber(Number(checkpointNum));
69
+ const checkpoint = await Checkpoint.random(checkpointNum, {
70
+ numBlocks: blocksPerCheckpoint,
71
+ startBlockNumber: startBlockNum,
72
+ slotNumber,
73
+ checkpointNumber: checkpointNum,
74
+ });
75
+ this.checkpointList.push(checkpoint);
76
+ this.l2Blocks.push(...checkpoint.blocks);
41
77
  }
42
78
 
43
- this.log.verbose(`Created ${numBlocks} blocks in the mock L2 block source`);
79
+ this.log.verbose(
80
+ `Created ${numCheckpoints} checkpoints with ${blocksPerCheckpoint} blocks each in the mock L2 block source`,
81
+ );
44
82
  }
45
83
 
46
84
  public addProposedBlocks(blocks: L2Block[]) {
@@ -50,6 +88,17 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
50
88
 
51
89
  public removeBlocks(numBlocks: number) {
52
90
  this.l2Blocks = this.l2Blocks.slice(0, -numBlocks);
91
+ const maxBlockNum = this.l2Blocks.length;
92
+ // Remove any checkpoint whose last block is beyond the remaining blocks.
93
+ this.checkpointList = this.checkpointList.filter(c => {
94
+ const lastBlockNum = c.blocks[0].number + c.blocks.length - 1;
95
+ return lastBlockNum <= maxBlockNum;
96
+ });
97
+ // Keep tip numbers consistent with remaining blocks.
98
+ this.checkpointedBlockNumber = Math.min(this.checkpointedBlockNumber, maxBlockNum);
99
+ this.proposedCheckpointBlockNumber = Math.min(this.proposedCheckpointBlockNumber, maxBlockNum);
100
+ this.provenBlockNumber = Math.min(this.provenBlockNumber, maxBlockNum);
101
+ this.finalizedBlockNumber = Math.min(this.finalizedBlockNumber, maxBlockNum);
53
102
  this.log.verbose(`Removed ${numBlocks} blocks from the mock L2 block source`);
54
103
  }
55
104
 
@@ -64,8 +113,42 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
64
113
  this.finalizedBlockNumber = finalizedBlockNumber;
65
114
  }
66
115
 
116
+ public setProposedCheckpointBlockNumber(blockNumber: number) {
117
+ this.proposedCheckpointBlockNumber = blockNumber;
118
+ }
119
+
67
120
  public setCheckpointedBlockNumber(checkpointedBlockNumber: number) {
121
+ const prevCheckpointed = this.checkpointedBlockNumber;
68
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
+ }
127
+ // Auto-create single-block checkpoints for newly checkpointed blocks that don't have one yet.
128
+ // This handles blocks added via addProposedBlocks that are now being marked as checkpointed.
129
+ const newCheckpoints: Checkpoint[] = [];
130
+ for (let blockNum = prevCheckpointed + 1; blockNum <= checkpointedBlockNumber; blockNum++) {
131
+ const block = this.l2Blocks[blockNum - 1];
132
+ if (!block) {
133
+ continue;
134
+ }
135
+ if (this.checkpointList.some(c => c.blocks.some(b => b.number === block.number))) {
136
+ continue;
137
+ }
138
+ const checkpointNum = CheckpointNumber(this.checkpointList.length + newCheckpoints.length + 1);
139
+ const checkpoint = new Checkpoint(
140
+ block.archive,
141
+ CheckpointHeader.random({ slotNumber: block.header.globalVariables.slotNumber }),
142
+ [block],
143
+ checkpointNum,
144
+ );
145
+ newCheckpoints.push(checkpoint);
146
+ }
147
+ // Insert new checkpoints in order by number.
148
+ if (newCheckpoints.length > 0) {
149
+ this.checkpointList.push(...newCheckpoints);
150
+ this.checkpointList.sort((a, b) => a.number - b.number);
151
+ }
69
152
  }
70
153
 
71
154
  /**
@@ -104,6 +187,10 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
104
187
  return Promise.resolve(BlockNumber(this.finalizedBlockNumber));
105
188
  }
106
189
 
190
+ public getProposedCheckpointL2BlockNumber() {
191
+ return Promise.resolve(BlockNumber(this.proposedCheckpointBlockNumber));
192
+ }
193
+
107
194
  public getCheckpointedBlock(number: BlockNumber): Promise<CheckpointedL2Block | undefined> {
108
195
  if (number > this.checkpointedBlockNumber) {
109
196
  return Promise.resolve(undefined);
@@ -112,13 +199,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
112
199
  if (!block) {
113
200
  return Promise.resolve(undefined);
114
201
  }
115
- const checkpointedBlock = new CheckpointedL2Block(
116
- CheckpointNumber.fromBlockNumber(number),
117
- block,
118
- new L1PublishedData(BigInt(number), BigInt(number), `0x${number.toString(16).padStart(64, '0')}`),
119
- [],
120
- );
121
- return Promise.resolve(checkpointedBlock);
202
+ return Promise.resolve(this.toCheckpointedBlock(block));
122
203
  }
123
204
 
124
205
  public async getCheckpointedBlocks(from: BlockNumber, limit: number): Promise<CheckpointedL2Block[]> {
@@ -167,44 +248,22 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
167
248
  }
168
249
 
169
250
  public getCheckpoints(from: CheckpointNumber, limit: number) {
170
- // TODO(mbps): Implement this properly. This only works when we have one block per checkpoint.
171
- const blocks = this.l2Blocks.slice(from - 1, from - 1 + limit);
172
- return Promise.all(
173
- blocks.map(async block => {
174
- // Create a checkpoint from the block - manually construct since L2Block doesn't have toCheckpoint()
175
- const checkpoint = await Checkpoint.random(block.checkpointNumber, { numBlocks: 1 });
176
- checkpoint.blocks = [block];
177
- return new PublishedCheckpoint(
178
- checkpoint,
179
- new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
180
- [],
181
- );
182
- }),
251
+ const checkpoints = this.checkpointList.slice(from - 1, from - 1 + limit);
252
+ return Promise.resolve(
253
+ checkpoints.map(checkpoint => new PublishedCheckpoint(checkpoint, this.mockL1DataForCheckpoint(checkpoint), [])),
183
254
  );
184
255
  }
185
256
 
186
- public async getCheckpointByArchive(archive: Fr): Promise<Checkpoint | undefined> {
187
- // TODO(mbps): Implement this properly. This only works when we have one block per checkpoint.
188
- const block = this.l2Blocks.find(b => b.archive.root.equals(archive));
189
- if (!block) {
190
- return undefined;
191
- }
192
- // Create a checkpoint from the block - manually construct since L2Block doesn't have toCheckpoint()
193
- const checkpoint = await Checkpoint.random(block.checkpointNumber, { numBlocks: 1 });
194
- checkpoint.blocks = [block];
195
- return checkpoint;
257
+ public getCheckpointByArchive(archive: Fr): Promise<Checkpoint | undefined> {
258
+ const checkpoint = this.checkpointList.find(c => c.archive.root.equals(archive));
259
+ return Promise.resolve(checkpoint);
196
260
  }
197
261
 
198
262
  public async getCheckpointedBlockByHash(blockHash: BlockHash): Promise<CheckpointedL2Block | undefined> {
199
263
  for (const block of this.l2Blocks) {
200
264
  const hash = await block.hash();
201
265
  if (hash.equals(blockHash)) {
202
- return CheckpointedL2Block.fromFields({
203
- checkpointNumber: CheckpointNumber.fromBlockNumber(block.number),
204
- block,
205
- l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
206
- attestations: [],
207
- });
266
+ return this.toCheckpointedBlock(block);
208
267
  }
209
268
  }
210
269
  return undefined;
@@ -215,14 +274,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
215
274
  if (!block) {
216
275
  return Promise.resolve(undefined);
217
276
  }
218
- return Promise.resolve(
219
- CheckpointedL2Block.fromFields({
220
- checkpointNumber: CheckpointNumber.fromBlockNumber(block.number),
221
- block,
222
- l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
223
- attestations: [],
224
- }),
225
- );
277
+ return Promise.resolve(this.toCheckpointedBlock(block));
226
278
  }
227
279
 
228
280
  public async getL2BlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
@@ -255,47 +307,69 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
255
307
  return Promise.resolve(block?.header);
256
308
  }
257
309
 
310
+ public async getBlockData(number: BlockNumber): Promise<BlockData | undefined> {
311
+ const block = this.l2Blocks[number - 1];
312
+ if (!block) {
313
+ return undefined;
314
+ }
315
+ return {
316
+ header: block.header,
317
+ archive: block.archive,
318
+ blockHash: await block.hash(),
319
+ checkpointNumber: block.checkpointNumber,
320
+ indexWithinCheckpoint: block.indexWithinCheckpoint,
321
+ };
322
+ }
323
+
324
+ public async getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
325
+ const block = this.l2Blocks.find(b => b.archive.root.equals(archive));
326
+ if (!block) {
327
+ return undefined;
328
+ }
329
+ return {
330
+ header: block.header,
331
+ archive: block.archive,
332
+ blockHash: await block.hash(),
333
+ checkpointNumber: block.checkpointNumber,
334
+ indexWithinCheckpoint: block.indexWithinCheckpoint,
335
+ };
336
+ }
337
+
258
338
  getBlockHeader(number: number | 'latest'): Promise<BlockHeader | undefined> {
259
339
  return Promise.resolve(this.l2Blocks.at(typeof number === 'number' ? number - 1 : -1)?.header);
260
340
  }
261
341
 
262
342
  getCheckpointsForEpoch(epochNumber: EpochNumber): Promise<Checkpoint[]> {
263
- // TODO(mbps): Implement this properly. This only works when we have one block per checkpoint.
264
- const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
265
- const [start, end] = getSlotRangeForEpoch(epochNumber, { epochDuration });
266
- const blocks = this.l2Blocks.filter(b => {
267
- const slot = b.header.globalVariables.slotNumber;
268
- return slot >= start && slot <= end;
269
- });
270
- // Create checkpoints from blocks - manually construct since L2Block doesn't have toCheckpoint()
271
- return Promise.all(
272
- blocks.map(async block => {
273
- const checkpoint = await Checkpoint.random(block.checkpointNumber, { numBlocks: 1 });
274
- checkpoint.blocks = [block];
275
- return checkpoint;
276
- }),
277
- );
343
+ return Promise.resolve(this.getCheckpointsInEpoch(epochNumber));
278
344
  }
279
345
 
280
- getCheckpointedBlocksForEpoch(epochNumber: EpochNumber): Promise<CheckpointedL2Block[]> {
281
- const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
282
- const [start, end] = getSlotRangeForEpoch(epochNumber, { epochDuration });
283
- const blocks = this.l2Blocks.filter(b => {
284
- const slot = b.header.globalVariables.slotNumber;
285
- return slot >= start && slot <= end;
286
- });
346
+ getCheckpointsDataForEpoch(epochNumber: EpochNumber): Promise<CheckpointData[]> {
347
+ const checkpoints = this.getCheckpointsInEpoch(epochNumber);
287
348
  return Promise.resolve(
288
- blocks.map(block =>
289
- CheckpointedL2Block.fromFields({
290
- checkpointNumber: CheckpointNumber.fromBlockNumber(block.number),
291
- block,
292
- l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
349
+ checkpoints.map(
350
+ (checkpoint): CheckpointData => ({
351
+ checkpointNumber: checkpoint.number,
352
+ header: checkpoint.header,
353
+ archive: checkpoint.archive,
354
+ checkpointOutHash: computeCheckpointOutHash(
355
+ checkpoint.blocks.map(b => b.body.txEffects.map(tx => tx.l2ToL1Msgs)),
356
+ ),
357
+ startBlock: checkpoint.blocks[0].number,
358
+ blockCount: checkpoint.blocks.length,
293
359
  attestations: [],
360
+ l1: this.mockL1DataForCheckpoint(checkpoint),
294
361
  }),
295
362
  ),
296
363
  );
297
364
  }
298
365
 
366
+ getCheckpointedBlocksForEpoch(epochNumber: EpochNumber): Promise<CheckpointedL2Block[]> {
367
+ const checkpoints = this.getCheckpointsInEpoch(epochNumber);
368
+ return Promise.resolve(
369
+ checkpoints.flatMap(checkpoint => checkpoint.blocks.map(block => this.toCheckpointedBlock(block))),
370
+ );
371
+ }
372
+
299
373
  getBlocksForSlot(slotNumber: SlotNumber): Promise<L2Block[]> {
300
374
  const blocks = this.l2Blocks.filter(b => b.header.globalVariables.slotNumber === slotNumber);
301
375
  return Promise.resolve(blocks);
@@ -345,6 +419,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
345
419
  txEffect.transactionFee.toBigInt(),
346
420
  await block.hash(),
347
421
  block.number,
422
+ getEpochAtSlot(block.slot, EmptyL1RollupConstants),
348
423
  );
349
424
  }
350
425
  }
@@ -353,17 +428,19 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
353
428
  }
354
429
 
355
430
  async getL2Tips(): Promise<L2Tips> {
356
- const [latest, proven, finalized, checkpointed] = [
431
+ const [latest, proven, finalized, checkpointed, proposedCheckpoint] = [
357
432
  await this.getBlockNumber(),
358
433
  await this.getProvenBlockNumber(),
359
434
  this.finalizedBlockNumber,
360
435
  this.checkpointedBlockNumber,
436
+ await this.getProposedCheckpointL2BlockNumber(),
361
437
  ] as const;
362
438
 
363
439
  const latestBlock = this.l2Blocks[latest - 1];
364
440
  const provenBlock = this.l2Blocks[proven - 1];
365
441
  const finalizedBlock = this.l2Blocks[finalized - 1];
366
442
  const checkpointedBlock = this.l2Blocks[checkpointed - 1];
443
+ const proposedCheckpointBlock = this.l2Blocks[proposedCheckpoint - 1];
367
444
 
368
445
  const latestBlockId = {
369
446
  number: BlockNumber(latest),
@@ -381,10 +458,17 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
381
458
  number: BlockNumber(checkpointed),
382
459
  hash: (await checkpointedBlock?.hash())?.toString(),
383
460
  };
461
+ const proposedCheckpointBlockId = {
462
+ number: BlockNumber(proposedCheckpoint),
463
+ hash: (await proposedCheckpointBlock?.hash())?.toString(),
464
+ };
384
465
 
385
466
  const makeTipId = (blockId: typeof latestBlockId) => ({
386
467
  block: blockId,
387
- checkpoint: { number: CheckpointNumber.fromBlockNumber(blockId.number), hash: blockId.hash },
468
+ checkpoint: {
469
+ number: this.findCheckpointNumberForBlock(blockId.number) ?? CheckpointNumber(0),
470
+ hash: blockId.hash,
471
+ },
388
472
  });
389
473
 
390
474
  return {
@@ -392,14 +476,15 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
392
476
  checkpointed: makeTipId(checkpointedBlockId),
393
477
  proven: makeTipId(provenBlockId),
394
478
  finalized: makeTipId(finalizedBlockId),
479
+ proposedCheckpoint: makeTipId(proposedCheckpointBlockId),
395
480
  };
396
481
  }
397
482
 
398
- getL2EpochNumber(): Promise<EpochNumber> {
483
+ getSyncedL2EpochNumber(): Promise<EpochNumber> {
399
484
  throw new Error('Method not implemented.');
400
485
  }
401
486
 
402
- getL2SlotNumber(): Promise<SlotNumber> {
487
+ getSyncedL2SlotNumber(): Promise<SlotNumber> {
403
488
  throw new Error('Method not implemented.');
404
489
  }
405
490
 
@@ -472,4 +557,46 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
472
557
  getPendingChainValidationStatus(): Promise<ValidateCheckpointResult> {
473
558
  return Promise.resolve({ valid: true });
474
559
  }
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
+
569
+ /** Returns checkpoints whose slot falls within the given epoch. */
570
+ private getCheckpointsInEpoch(epochNumber: EpochNumber): Checkpoint[] {
571
+ const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
572
+ const [start, end] = getSlotRangeForEpoch(epochNumber, { epochDuration });
573
+ return this.checkpointList.filter(c => c.header.slotNumber >= start && c.header.slotNumber <= end);
574
+ }
575
+
576
+ /** Creates a mock L1PublishedData for a checkpoint. */
577
+ private mockL1DataForCheckpoint(checkpoint: Checkpoint): L1PublishedData {
578
+ return new L1PublishedData(BigInt(checkpoint.number), BigInt(checkpoint.number), Buffer32.random().toString());
579
+ }
580
+
581
+ /** Creates a CheckpointedL2Block from a block using stored checkpoint info. */
582
+ private toCheckpointedBlock(block: L2Block): CheckpointedL2Block {
583
+ const checkpoint = this.checkpointList.find(c => c.blocks.some(b => b.number === block.number));
584
+ const checkpointNumber = checkpoint?.number ?? block.checkpointNumber;
585
+ return new CheckpointedL2Block(
586
+ checkpointNumber,
587
+ block,
588
+ new L1PublishedData(
589
+ BigInt(block.number),
590
+ BigInt(block.number),
591
+ `0x${block.number.toString(16).padStart(64, '0')}`,
592
+ ),
593
+ [],
594
+ );
595
+ }
596
+
597
+ /** Finds the checkpoint number for a block, or undefined if the block is not in any checkpoint. */
598
+ private findCheckpointNumberForBlock(blockNumber: BlockNumber): CheckpointNumber | undefined {
599
+ const checkpoint = this.checkpointList.find(c => c.blocks.some(b => b.number === blockNumber));
600
+ return checkpoint?.number;
601
+ }
475
602
  }
@@ -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. */