@aztec/archiver 0.0.1-commit.e558bd1c → 0.0.1-commit.e5a3663dd

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 (126) hide show
  1. package/README.md +12 -6
  2. package/dest/archiver.d.ts +26 -15
  3. package/dest/archiver.d.ts.map +1 -1
  4. package/dest/archiver.js +161 -153
  5. package/dest/config.d.ts +5 -3
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +16 -4
  8. package/dest/errors.d.ts +61 -10
  9. package/dest/errors.d.ts.map +1 -1
  10. package/dest/errors.js +88 -14
  11. package/dest/factory.d.ts +6 -7
  12. package/dest/factory.d.ts.map +1 -1
  13. package/dest/factory.js +40 -32
  14. package/dest/index.d.ts +11 -3
  15. package/dest/index.d.ts.map +1 -1
  16. package/dest/index.js +10 -2
  17. package/dest/l1/bin/retrieve-calldata.js +32 -28
  18. package/dest/l1/calldata_retriever.d.ts +74 -50
  19. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  20. package/dest/l1/calldata_retriever.js +197 -260
  21. package/dest/l1/data_retrieval.d.ts +26 -17
  22. package/dest/l1/data_retrieval.d.ts.map +1 -1
  23. package/dest/l1/data_retrieval.js +42 -47
  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/l1/validate_historical_logs.d.ts +23 -0
  28. package/dest/l1/validate_historical_logs.d.ts.map +1 -0
  29. package/dest/l1/validate_historical_logs.js +108 -0
  30. package/dest/modules/contract_data_source_adapter.d.ts +25 -0
  31. package/dest/modules/contract_data_source_adapter.d.ts.map +1 -0
  32. package/dest/modules/contract_data_source_adapter.js +42 -0
  33. package/dest/modules/data_source_base.d.ts +27 -14
  34. package/dest/modules/data_source_base.d.ts.map +1 -1
  35. package/dest/modules/data_source_base.js +98 -125
  36. package/dest/modules/data_store_updater.d.ts +37 -17
  37. package/dest/modules/data_store_updater.d.ts.map +1 -1
  38. package/dest/modules/data_store_updater.js +155 -112
  39. package/dest/modules/instrumentation.d.ts +21 -3
  40. package/dest/modules/instrumentation.d.ts.map +1 -1
  41. package/dest/modules/instrumentation.js +41 -8
  42. package/dest/modules/l1_synchronizer.d.ts +13 -11
  43. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  44. package/dest/modules/l1_synchronizer.js +355 -182
  45. package/dest/modules/validation.d.ts +4 -3
  46. package/dest/modules/validation.d.ts.map +1 -1
  47. package/dest/modules/validation.js +6 -6
  48. package/dest/store/block_store.d.ts +107 -31
  49. package/dest/store/block_store.d.ts.map +1 -1
  50. package/dest/store/block_store.js +477 -141
  51. package/dest/store/contract_class_store.d.ts +17 -4
  52. package/dest/store/contract_class_store.d.ts.map +1 -1
  53. package/dest/store/contract_class_store.js +24 -68
  54. package/dest/store/contract_instance_store.d.ts +28 -1
  55. package/dest/store/contract_instance_store.d.ts.map +1 -1
  56. package/dest/store/contract_instance_store.js +37 -2
  57. package/dest/store/data_stores.d.ts +68 -0
  58. package/dest/store/data_stores.d.ts.map +1 -0
  59. package/dest/store/data_stores.js +50 -0
  60. package/dest/store/function_names_cache.d.ts +17 -0
  61. package/dest/store/function_names_cache.d.ts.map +1 -0
  62. package/dest/store/function_names_cache.js +30 -0
  63. package/dest/store/l2_tips_cache.d.ts +20 -0
  64. package/dest/store/l2_tips_cache.d.ts.map +1 -0
  65. package/dest/store/l2_tips_cache.js +109 -0
  66. package/dest/store/log_store.d.ts +6 -3
  67. package/dest/store/log_store.d.ts.map +1 -1
  68. package/dest/store/log_store.js +95 -20
  69. package/dest/store/message_store.d.ts +11 -1
  70. package/dest/store/message_store.d.ts.map +1 -1
  71. package/dest/store/message_store.js +51 -9
  72. package/dest/test/fake_l1_state.d.ts +25 -1
  73. package/dest/test/fake_l1_state.d.ts.map +1 -1
  74. package/dest/test/fake_l1_state.js +166 -32
  75. package/dest/test/mock_archiver.d.ts +1 -1
  76. package/dest/test/mock_archiver.d.ts.map +1 -1
  77. package/dest/test/mock_archiver.js +3 -2
  78. package/dest/test/mock_l1_to_l2_message_source.d.ts +1 -1
  79. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  80. package/dest/test/mock_l1_to_l2_message_source.js +2 -1
  81. package/dest/test/mock_l2_block_source.d.ts +35 -5
  82. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  83. package/dest/test/mock_l2_block_source.js +182 -89
  84. package/dest/test/mock_structs.d.ts +4 -1
  85. package/dest/test/mock_structs.d.ts.map +1 -1
  86. package/dest/test/mock_structs.js +13 -1
  87. package/dest/test/noop_l1_archiver.d.ts +7 -4
  88. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  89. package/dest/test/noop_l1_archiver.js +14 -8
  90. package/package.json +13 -13
  91. package/src/archiver.ts +199 -174
  92. package/src/config.ts +23 -2
  93. package/src/errors.ts +133 -22
  94. package/src/factory.ts +53 -30
  95. package/src/index.ts +18 -2
  96. package/src/l1/README.md +25 -68
  97. package/src/l1/bin/retrieve-calldata.ts +40 -27
  98. package/src/l1/calldata_retriever.ts +261 -379
  99. package/src/l1/data_retrieval.ts +58 -69
  100. package/src/l1/spire_proposer.ts +7 -15
  101. package/src/l1/validate_historical_logs.ts +140 -0
  102. package/src/modules/contract_data_source_adapter.ts +59 -0
  103. package/src/modules/data_source_base.ts +142 -144
  104. package/src/modules/data_store_updater.ts +187 -141
  105. package/src/modules/instrumentation.ts +56 -9
  106. package/src/modules/l1_synchronizer.ts +463 -218
  107. package/src/modules/validation.ts +10 -9
  108. package/src/store/block_store.ts +587 -177
  109. package/src/store/contract_class_store.ts +31 -103
  110. package/src/store/contract_instance_store.ts +51 -5
  111. package/src/store/data_stores.ts +108 -0
  112. package/src/store/function_names_cache.ts +37 -0
  113. package/src/store/l2_tips_cache.ts +134 -0
  114. package/src/store/log_store.ts +128 -32
  115. package/src/store/message_store.ts +60 -10
  116. package/src/structs/inbox_message.ts +1 -1
  117. package/src/test/fake_l1_state.ts +213 -42
  118. package/src/test/mock_archiver.ts +3 -2
  119. package/src/test/mock_l1_to_l2_message_source.ts +1 -0
  120. package/src/test/mock_l2_block_source.ts +230 -82
  121. package/src/test/mock_structs.ts +20 -6
  122. package/src/test/noop_l1_archiver.ts +16 -8
  123. package/dest/store/kv_archiver_store.d.ts +0 -340
  124. package/dest/store/kv_archiver_store.d.ts.map +0 -1
  125. package/dest/store/kv_archiver_store.js +0 -446
  126. package/src/store/kv_archiver_store.ts +0 -639
@@ -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,90 @@ 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 getCheckpointData(_n: CheckpointNumber): Promise<CheckpointData | undefined> {
325
+ return Promise.resolve(undefined);
326
+ }
327
+
328
+ public getCheckpointDataRange(_from: CheckpointNumber, _limit: number): Promise<CheckpointData[]> {
329
+ return Promise.resolve([]);
330
+ }
331
+
332
+ public getCheckpointNumberBySlot(_slot: SlotNumber): Promise<CheckpointNumber | undefined> {
333
+ return Promise.resolve(undefined);
334
+ }
335
+
336
+ public async getBlockDataWithCheckpointContext(number: BlockNumber) {
337
+ const data = await this.getBlockData(number);
338
+ if (!data) {
339
+ return undefined;
340
+ }
341
+ return { data, checkpoint: undefined, l1: undefined, attestations: [] };
342
+ }
343
+
344
+ public async getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
345
+ const block = this.l2Blocks.find(b => b.archive.root.equals(archive));
346
+ if (!block) {
347
+ return undefined;
348
+ }
349
+ return {
350
+ header: block.header,
351
+ archive: block.archive,
352
+ blockHash: await block.hash(),
353
+ checkpointNumber: block.checkpointNumber,
354
+ indexWithinCheckpoint: block.indexWithinCheckpoint,
355
+ };
356
+ }
357
+
258
358
  getBlockHeader(number: number | 'latest'): Promise<BlockHeader | undefined> {
259
359
  return Promise.resolve(this.l2Blocks.at(typeof number === 'number' ? number - 1 : -1)?.header);
260
360
  }
261
361
 
262
362
  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
- );
363
+ return Promise.resolve(this.getCheckpointsInEpoch(epochNumber));
278
364
  }
279
365
 
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
- });
366
+ getCheckpointsDataForEpoch(epochNumber: EpochNumber): Promise<CheckpointData[]> {
367
+ const checkpoints = this.getCheckpointsInEpoch(epochNumber);
287
368
  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()),
369
+ checkpoints.map(
370
+ (checkpoint): CheckpointData => ({
371
+ checkpointNumber: checkpoint.number,
372
+ header: checkpoint.header,
373
+ archive: checkpoint.archive,
374
+ checkpointOutHash: computeCheckpointOutHash(
375
+ checkpoint.blocks.map(b => b.body.txEffects.map(tx => tx.l2ToL1Msgs)),
376
+ ),
377
+ startBlock: checkpoint.blocks[0].number,
378
+ blockCount: checkpoint.blocks.length,
379
+ feeAssetPriceModifier: checkpoint.feeAssetPriceModifier,
293
380
  attestations: [],
381
+ l1: this.mockL1DataForCheckpoint(checkpoint),
294
382
  }),
295
383
  ),
296
384
  );
297
385
  }
298
386
 
387
+ getCheckpointedBlocksForEpoch(epochNumber: EpochNumber): Promise<CheckpointedL2Block[]> {
388
+ const checkpoints = this.getCheckpointsInEpoch(epochNumber);
389
+ return Promise.resolve(
390
+ checkpoints.flatMap(checkpoint => checkpoint.blocks.map(block => this.toCheckpointedBlock(block))),
391
+ );
392
+ }
393
+
299
394
  getBlocksForSlot(slotNumber: SlotNumber): Promise<L2Block[]> {
300
395
  const blocks = this.l2Blocks.filter(b => b.header.globalVariables.slotNumber === slotNumber);
301
396
  return Promise.resolve(blocks);
@@ -345,6 +440,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
345
440
  txEffect.transactionFee.toBigInt(),
346
441
  await block.hash(),
347
442
  block.number,
443
+ getEpochAtSlot(block.slot, EmptyL1RollupConstants),
348
444
  );
349
445
  }
350
446
  }
@@ -353,17 +449,19 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
353
449
  }
354
450
 
355
451
  async getL2Tips(): Promise<L2Tips> {
356
- const [latest, proven, finalized, checkpointed] = [
452
+ const [latest, proven, finalized, checkpointed, proposedCheckpoint] = [
357
453
  await this.getBlockNumber(),
358
454
  await this.getProvenBlockNumber(),
359
455
  this.finalizedBlockNumber,
360
456
  this.checkpointedBlockNumber,
457
+ await this.getProposedCheckpointL2BlockNumber(),
361
458
  ] as const;
362
459
 
363
460
  const latestBlock = this.l2Blocks[latest - 1];
364
461
  const provenBlock = this.l2Blocks[proven - 1];
365
462
  const finalizedBlock = this.l2Blocks[finalized - 1];
366
463
  const checkpointedBlock = this.l2Blocks[checkpointed - 1];
464
+ const proposedCheckpointBlock = this.l2Blocks[proposedCheckpoint - 1];
367
465
 
368
466
  const latestBlockId = {
369
467
  number: BlockNumber(latest),
@@ -381,10 +479,17 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
381
479
  number: BlockNumber(checkpointed),
382
480
  hash: (await checkpointedBlock?.hash())?.toString(),
383
481
  };
482
+ const proposedCheckpointBlockId = {
483
+ number: BlockNumber(proposedCheckpoint),
484
+ hash: (await proposedCheckpointBlock?.hash())?.toString(),
485
+ };
384
486
 
385
487
  const makeTipId = (blockId: typeof latestBlockId) => ({
386
488
  block: blockId,
387
- checkpoint: { number: CheckpointNumber.fromBlockNumber(blockId.number), hash: blockId.hash },
489
+ checkpoint: {
490
+ number: this.findCheckpointNumberForBlock(blockId.number) ?? CheckpointNumber(0),
491
+ hash: blockId.hash,
492
+ },
388
493
  });
389
494
 
390
495
  return {
@@ -392,14 +497,15 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
392
497
  checkpointed: makeTipId(checkpointedBlockId),
393
498
  proven: makeTipId(provenBlockId),
394
499
  finalized: makeTipId(finalizedBlockId),
500
+ proposedCheckpoint: makeTipId(proposedCheckpointBlockId),
395
501
  };
396
502
  }
397
503
 
398
- getL2EpochNumber(): Promise<EpochNumber> {
504
+ getSyncedL2EpochNumber(): Promise<EpochNumber> {
399
505
  throw new Error('Method not implemented.');
400
506
  }
401
507
 
402
- getL2SlotNumber(): Promise<SlotNumber> {
508
+ getSyncedL2SlotNumber(): Promise<SlotNumber> {
403
509
  throw new Error('Method not implemented.');
404
510
  }
405
511
 
@@ -472,4 +578,46 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
472
578
  getPendingChainValidationStatus(): Promise<ValidateCheckpointResult> {
473
579
  return Promise.resolve({ valid: true });
474
580
  }
581
+
582
+ getLastCheckpoint(): Promise<ProposedCheckpointData | undefined> {
583
+ return Promise.resolve(undefined);
584
+ }
585
+
586
+ getLastProposedCheckpoint(): Promise<ProposedCheckpointData | undefined> {
587
+ return Promise.resolve(undefined);
588
+ }
589
+
590
+ /** Returns checkpoints whose slot falls within the given epoch. */
591
+ private getCheckpointsInEpoch(epochNumber: EpochNumber): Checkpoint[] {
592
+ const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
593
+ const [start, end] = getSlotRangeForEpoch(epochNumber, { epochDuration });
594
+ return this.checkpointList.filter(c => c.header.slotNumber >= start && c.header.slotNumber <= end);
595
+ }
596
+
597
+ /** Creates a mock L1PublishedData for a checkpoint. */
598
+ private mockL1DataForCheckpoint(checkpoint: Checkpoint): L1PublishedData {
599
+ return new L1PublishedData(BigInt(checkpoint.number), BigInt(checkpoint.number), Buffer32.random().toString());
600
+ }
601
+
602
+ /** Creates a CheckpointedL2Block from a block using stored checkpoint info. */
603
+ private toCheckpointedBlock(block: L2Block): CheckpointedL2Block {
604
+ const checkpoint = this.checkpointList.find(c => c.blocks.some(b => b.number === block.number));
605
+ const checkpointNumber = checkpoint?.number ?? block.checkpointNumber;
606
+ return new CheckpointedL2Block(
607
+ checkpointNumber,
608
+ block,
609
+ new L1PublishedData(
610
+ BigInt(block.number),
611
+ BigInt(block.number),
612
+ `0x${block.number.toString(16).padStart(64, '0')}`,
613
+ ),
614
+ [],
615
+ );
616
+ }
617
+
618
+ /** Finds the checkpoint number for a block, or undefined if the block is not in any checkpoint. */
619
+ private findCheckpointNumberForBlock(blockNumber: BlockNumber): CheckpointNumber | undefined {
620
+ const checkpoint = this.checkpointList.find(c => c.blocks.some(b => b.number === blockNumber));
621
+ return checkpoint?.number;
622
+ }
475
623
  }
@@ -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';
@@ -15,7 +16,7 @@ import { EventEmitter } from 'node:events';
15
16
  import { Archiver } from '../archiver.js';
16
17
  import { ArchiverInstrumentation } from '../modules/instrumentation.js';
17
18
  import type { ArchiverL1Synchronizer } from '../modules/l1_synchronizer.js';
18
- import type { KVArchiverDataStore } from '../store/kv_archiver_store.js';
19
+ import type { ArchiverDataStores } from '../store/data_stores.js';
19
20
 
20
21
  /** Noop L1 synchronizer for testing without L1 connectivity. */
21
22
  class NoopL1Synchronizer implements FunctionsOf<ArchiverL1Synchronizer> {
@@ -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();
@@ -47,7 +48,7 @@ class NoopL1Synchronizer implements FunctionsOf<ArchiverL1Synchronizer> {
47
48
  */
48
49
  export class NoopL1Archiver extends Archiver {
49
50
  constructor(
50
- dataStore: KVArchiverDataStore,
51
+ dataStores: ArchiverDataStores,
51
52
  l1Constants: L1RollupConstants & { genesisArchiveRoot: Fr },
52
53
  instrumentation: ArchiverInstrumentation,
53
54
  ) {
@@ -69,18 +70,20 @@ export class NoopL1Archiver extends Archiver {
69
70
  debugClient,
70
71
  rollup,
71
72
  {
73
+ rollupAddress: EthAddress.ZERO,
72
74
  registryAddress: EthAddress.ZERO,
75
+ inboxAddress: EthAddress.ZERO,
73
76
  governanceProposerAddress: EthAddress.ZERO,
74
- slashFactoryAddress: EthAddress.ZERO,
75
77
  slashingProposerAddress: EthAddress.ZERO,
76
78
  },
77
- dataStore,
79
+ dataStores,
78
80
  {
79
81
  pollingIntervalMs: 1000,
80
82
  batchSize: 100,
81
83
  skipValidateCheckpointAttestations: true,
82
84
  maxAllowedEthClientDriftSeconds: 300,
83
85
  ethereumAllowNoDebugHosts: true, // Skip trace validation
86
+ skipHistoricalLogsCheck: true, // Skip historical logs validation
84
87
  },
85
88
  blobClient,
86
89
  instrumentation,
@@ -96,14 +99,19 @@ export class NoopL1Archiver extends Archiver {
96
99
  this.runningPromise.start();
97
100
  return Promise.resolve();
98
101
  }
102
+
103
+ /** Always reports as fully synced since there is no real L1 to sync from. */
104
+ public override getSyncedL2SlotNumber(): Promise<SlotNumber | undefined> {
105
+ return Promise.resolve(SlotNumber(Number.MAX_SAFE_INTEGER));
106
+ }
99
107
  }
100
108
 
101
109
  /** Creates an archiver with mocked L1 connectivity for testing. */
102
110
  export async function createNoopL1Archiver(
103
- dataStore: KVArchiverDataStore,
111
+ dataStores: ArchiverDataStores,
104
112
  l1Constants: L1RollupConstants & { genesisArchiveRoot: Fr },
105
113
  telemetry: TelemetryClient = getTelemetryClient(),
106
114
  ): Promise<NoopL1Archiver> {
107
- const instrumentation = await ArchiverInstrumentation.new(telemetry, () => dataStore.estimateSize());
108
- return new NoopL1Archiver(dataStore, l1Constants, instrumentation);
115
+ const instrumentation = await ArchiverInstrumentation.new(telemetry, () => dataStores.db.estimateSize());
116
+ return new NoopL1Archiver(dataStores, l1Constants, instrumentation);
109
117
  }