@aztec/archiver 0.0.1-commit.f295ac2 → 0.0.1-commit.f504929

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 (95) hide show
  1. package/README.md +9 -0
  2. package/dest/archiver.d.ts +10 -6
  3. package/dest/archiver.d.ts.map +1 -1
  4. package/dest/archiver.js +50 -111
  5. package/dest/errors.d.ts +6 -1
  6. package/dest/errors.d.ts.map +1 -1
  7. package/dest/errors.js +8 -0
  8. package/dest/factory.d.ts +5 -2
  9. package/dest/factory.d.ts.map +1 -1
  10. package/dest/factory.js +16 -13
  11. package/dest/index.d.ts +2 -1
  12. package/dest/index.d.ts.map +1 -1
  13. package/dest/index.js +1 -0
  14. package/dest/l1/bin/retrieve-calldata.js +35 -32
  15. package/dest/l1/calldata_retriever.d.ts +73 -50
  16. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  17. package/dest/l1/calldata_retriever.js +190 -259
  18. package/dest/l1/data_retrieval.d.ts +9 -9
  19. package/dest/l1/data_retrieval.d.ts.map +1 -1
  20. package/dest/l1/data_retrieval.js +24 -22
  21. package/dest/l1/spire_proposer.d.ts +5 -5
  22. package/dest/l1/spire_proposer.d.ts.map +1 -1
  23. package/dest/l1/spire_proposer.js +9 -17
  24. package/dest/l1/validate_trace.d.ts +6 -3
  25. package/dest/l1/validate_trace.d.ts.map +1 -1
  26. package/dest/l1/validate_trace.js +13 -9
  27. package/dest/modules/data_source_base.d.ts +25 -21
  28. package/dest/modules/data_source_base.d.ts.map +1 -1
  29. package/dest/modules/data_source_base.js +48 -123
  30. package/dest/modules/data_store_updater.d.ts +31 -20
  31. package/dest/modules/data_store_updater.d.ts.map +1 -1
  32. package/dest/modules/data_store_updater.js +79 -60
  33. package/dest/modules/instrumentation.d.ts +17 -4
  34. package/dest/modules/instrumentation.d.ts.map +1 -1
  35. package/dest/modules/instrumentation.js +36 -12
  36. package/dest/modules/l1_synchronizer.d.ts +4 -8
  37. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  38. package/dest/modules/l1_synchronizer.js +23 -19
  39. package/dest/store/block_store.d.ts +50 -32
  40. package/dest/store/block_store.d.ts.map +1 -1
  41. package/dest/store/block_store.js +147 -54
  42. package/dest/store/contract_class_store.d.ts +1 -1
  43. package/dest/store/contract_class_store.d.ts.map +1 -1
  44. package/dest/store/contract_class_store.js +11 -7
  45. package/dest/store/kv_archiver_store.d.ts +52 -29
  46. package/dest/store/kv_archiver_store.d.ts.map +1 -1
  47. package/dest/store/kv_archiver_store.js +49 -23
  48. package/dest/store/l2_tips_cache.d.ts +19 -0
  49. package/dest/store/l2_tips_cache.d.ts.map +1 -0
  50. package/dest/store/l2_tips_cache.js +89 -0
  51. package/dest/store/log_store.d.ts +17 -8
  52. package/dest/store/log_store.d.ts.map +1 -1
  53. package/dest/store/log_store.js +77 -43
  54. package/dest/test/fake_l1_state.d.ts +9 -4
  55. package/dest/test/fake_l1_state.d.ts.map +1 -1
  56. package/dest/test/fake_l1_state.js +56 -18
  57. package/dest/test/index.js +3 -1
  58. package/dest/test/mock_archiver.d.ts +1 -1
  59. package/dest/test/mock_archiver.d.ts.map +1 -1
  60. package/dest/test/mock_archiver.js +3 -2
  61. package/dest/test/mock_l2_block_source.d.ts +36 -21
  62. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  63. package/dest/test/mock_l2_block_source.js +151 -109
  64. package/dest/test/mock_structs.d.ts +3 -2
  65. package/dest/test/mock_structs.d.ts.map +1 -1
  66. package/dest/test/mock_structs.js +11 -9
  67. package/dest/test/noop_l1_archiver.d.ts +23 -0
  68. package/dest/test/noop_l1_archiver.d.ts.map +1 -0
  69. package/dest/test/noop_l1_archiver.js +68 -0
  70. package/package.json +14 -13
  71. package/src/archiver.ts +71 -136
  72. package/src/errors.ts +12 -0
  73. package/src/factory.ts +30 -14
  74. package/src/index.ts +1 -0
  75. package/src/l1/README.md +25 -68
  76. package/src/l1/bin/retrieve-calldata.ts +45 -33
  77. package/src/l1/calldata_retriever.ts +249 -379
  78. package/src/l1/data_retrieval.ts +27 -29
  79. package/src/l1/spire_proposer.ts +7 -15
  80. package/src/l1/validate_trace.ts +24 -6
  81. package/src/modules/data_source_base.ts +81 -167
  82. package/src/modules/data_store_updater.ts +92 -63
  83. package/src/modules/instrumentation.ts +46 -14
  84. package/src/modules/l1_synchronizer.ts +26 -24
  85. package/src/store/block_store.ts +188 -92
  86. package/src/store/contract_class_store.ts +11 -7
  87. package/src/store/kv_archiver_store.ts +85 -36
  88. package/src/store/l2_tips_cache.ts +89 -0
  89. package/src/store/log_store.ts +134 -49
  90. package/src/test/fake_l1_state.ts +77 -19
  91. package/src/test/index.ts +3 -0
  92. package/src/test/mock_archiver.ts +3 -2
  93. package/src/test/mock_l2_block_source.ts +196 -126
  94. package/src/test/mock_structs.ts +26 -10
  95. package/src/test/noop_l1_archiver.ts +109 -0
@@ -8,24 +8,28 @@ 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,
12
+ BlockHash,
11
13
  CheckpointedL2Block,
12
- L2BlockHash,
13
- L2BlockNew,
14
+ L2Block,
14
15
  type L2BlockSource,
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 { Checkpoint, type CheckpointData, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
19
20
  import type { ContractClassPublic, ContractDataSource, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
20
21
  import { EmptyL1RollupConstants, type L1RollupConstants, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
21
- import { type BlockHeader, TxHash, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
22
+ import { computeCheckpointOutHash } from '@aztec/stdlib/messaging';
23
+ import { CheckpointHeader } from '@aztec/stdlib/rollup';
24
+ import { type BlockHeader, TxExecutionResult, TxHash, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
22
25
  import type { UInt64 } from '@aztec/stdlib/types';
23
26
 
24
27
  /**
25
28
  * A mocked implementation of L2BlockSource to be used in tests.
26
29
  */
27
30
  export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
28
- protected l2Blocks: L2BlockNew[] = [];
31
+ protected l2Blocks: L2Block[] = [];
32
+ protected checkpointList: Checkpoint[] = [];
29
33
 
30
34
  private provenBlockNumber: number = 0;
31
35
  private finalizedBlockNumber: number = 0;
@@ -33,23 +37,49 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
33
37
 
34
38
  private log = createLogger('archiver:mock_l2_block_source');
35
39
 
40
+ /** Creates blocks grouped into single-block checkpoints. */
36
41
  public async createBlocks(numBlocks: number) {
37
- for (let i = 0; i < numBlocks; i++) {
38
- const blockNum = this.l2Blocks.length + 1;
39
- const block = await L2BlockNew.random(BlockNumber(blockNum), { slotNumber: SlotNumber(blockNum) });
40
- this.l2Blocks.push(block);
42
+ await this.createCheckpoints(numBlocks, 1);
43
+ }
44
+
45
+ /** Creates checkpoints, each containing `blocksPerCheckpoint` blocks. */
46
+ public async createCheckpoints(numCheckpoints: number, blocksPerCheckpoint: number = 1) {
47
+ for (let c = 0; c < numCheckpoints; c++) {
48
+ const checkpointNum = CheckpointNumber(this.checkpointList.length + 1);
49
+ const startBlockNum = this.l2Blocks.length + 1;
50
+ const slotNumber = SlotNumber(Number(checkpointNum));
51
+ const checkpoint = await Checkpoint.random(checkpointNum, {
52
+ numBlocks: blocksPerCheckpoint,
53
+ startBlockNumber: startBlockNum,
54
+ slotNumber,
55
+ checkpointNumber: checkpointNum,
56
+ });
57
+ this.checkpointList.push(checkpoint);
58
+ this.l2Blocks.push(...checkpoint.blocks);
41
59
  }
42
60
 
43
- this.log.verbose(`Created ${numBlocks} blocks in the mock L2 block source`);
61
+ this.log.verbose(
62
+ `Created ${numCheckpoints} checkpoints with ${blocksPerCheckpoint} blocks each in the mock L2 block source`,
63
+ );
44
64
  }
45
65
 
46
- public addBlocks(blocks: L2BlockNew[]) {
66
+ public addProposedBlocks(blocks: L2Block[]) {
47
67
  this.l2Blocks.push(...blocks);
48
- this.log.verbose(`Added ${blocks.length} blocks to the mock L2 block source`);
68
+ this.log.verbose(`Added ${blocks.length} proposed blocks to the mock L2 block source`);
49
69
  }
50
70
 
51
71
  public removeBlocks(numBlocks: number) {
52
72
  this.l2Blocks = this.l2Blocks.slice(0, -numBlocks);
73
+ const maxBlockNum = this.l2Blocks.length;
74
+ // Remove any checkpoint whose last block is beyond the remaining blocks.
75
+ this.checkpointList = this.checkpointList.filter(c => {
76
+ const lastBlockNum = c.blocks[0].number + c.blocks.length - 1;
77
+ return lastBlockNum <= maxBlockNum;
78
+ });
79
+ // Keep tip numbers consistent with remaining blocks.
80
+ this.checkpointedBlockNumber = Math.min(this.checkpointedBlockNumber, maxBlockNum);
81
+ this.provenBlockNumber = Math.min(this.provenBlockNumber, maxBlockNum);
82
+ this.finalizedBlockNumber = Math.min(this.finalizedBlockNumber, maxBlockNum);
53
83
  this.log.verbose(`Removed ${numBlocks} blocks from the mock L2 block source`);
54
84
  }
55
85
 
@@ -65,7 +95,33 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
65
95
  }
66
96
 
67
97
  public setCheckpointedBlockNumber(checkpointedBlockNumber: number) {
98
+ const prevCheckpointed = this.checkpointedBlockNumber;
68
99
  this.checkpointedBlockNumber = checkpointedBlockNumber;
100
+ // Auto-create single-block checkpoints for newly checkpointed blocks that don't have one yet.
101
+ // This handles blocks added via addProposedBlocks that are now being marked as checkpointed.
102
+ const newCheckpoints: Checkpoint[] = [];
103
+ for (let blockNum = prevCheckpointed + 1; blockNum <= checkpointedBlockNumber; blockNum++) {
104
+ const block = this.l2Blocks[blockNum - 1];
105
+ if (!block) {
106
+ continue;
107
+ }
108
+ if (this.checkpointList.some(c => c.blocks.some(b => b.number === block.number))) {
109
+ continue;
110
+ }
111
+ const checkpointNum = CheckpointNumber(this.checkpointList.length + newCheckpoints.length + 1);
112
+ const checkpoint = new Checkpoint(
113
+ block.archive,
114
+ CheckpointHeader.random({ slotNumber: block.header.globalVariables.slotNumber }),
115
+ [block],
116
+ checkpointNum,
117
+ );
118
+ newCheckpoints.push(checkpoint);
119
+ }
120
+ // Insert new checkpoints in order by number.
121
+ if (newCheckpoints.length > 0) {
122
+ this.checkpointList.push(...newCheckpoints);
123
+ this.checkpointList.sort((a, b) => a.number - b.number);
124
+ }
69
125
  }
70
126
 
71
127
  /**
@@ -96,6 +152,14 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
96
152
  return Promise.resolve(BlockNumber(this.provenBlockNumber));
97
153
  }
98
154
 
155
+ public getCheckpointedL2BlockNumber() {
156
+ return Promise.resolve(BlockNumber(this.checkpointedBlockNumber));
157
+ }
158
+
159
+ public getFinalizedL2BlockNumber() {
160
+ return Promise.resolve(BlockNumber(this.finalizedBlockNumber));
161
+ }
162
+
99
163
  public getCheckpointedBlock(number: BlockNumber): Promise<CheckpointedL2Block | undefined> {
100
164
  if (number > this.checkpointedBlockNumber) {
101
165
  return Promise.resolve(undefined);
@@ -104,20 +168,10 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
104
168
  if (!block) {
105
169
  return Promise.resolve(undefined);
106
170
  }
107
- const checkpointedBlock = new CheckpointedL2Block(
108
- CheckpointNumber(number),
109
- block,
110
- new L1PublishedData(BigInt(number), BigInt(number), `0x${number.toString(16).padStart(64, '0')}`),
111
- [],
112
- );
113
- return Promise.resolve(checkpointedBlock);
171
+ return Promise.resolve(this.toCheckpointedBlock(block));
114
172
  }
115
173
 
116
- public async getCheckpointedBlocks(
117
- from: BlockNumber,
118
- limit: number,
119
- _proven?: boolean,
120
- ): Promise<CheckpointedL2Block[]> {
174
+ public async getCheckpointedBlocks(from: BlockNumber, limit: number): Promise<CheckpointedL2Block[]> {
121
175
  const result: CheckpointedL2Block[] = [];
122
176
  for (let i = 0; i < limit; i++) {
123
177
  const blockNum = from + i;
@@ -137,7 +191,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
137
191
  * @param number - The block number to return (inclusive).
138
192
  * @returns The requested L2 block.
139
193
  */
140
- public getBlock(number: number): Promise<L2BlockNew | undefined> {
194
+ public getBlock(number: number): Promise<L2Block | undefined> {
141
195
  const block = this.l2Blocks[number - 1];
142
196
  return Promise.resolve(block);
143
197
  }
@@ -147,7 +201,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
147
201
  * @param number - The block number to return.
148
202
  * @returns The requested L2 block.
149
203
  */
150
- public getL2BlockNew(number: BlockNumber): Promise<L2BlockNew | undefined> {
204
+ public getL2Block(number: BlockNumber): Promise<L2Block | undefined> {
151
205
  const block = this.l2Blocks[number - 1];
152
206
  return Promise.resolve(block);
153
207
  }
@@ -158,95 +212,41 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
158
212
  * @param limit - The maximum number of blocks to return.
159
213
  * @returns The requested mocked L2 blocks.
160
214
  */
161
- public getBlocks(from: number, limit: number, proven?: boolean): Promise<L2BlockNew[]> {
162
- return Promise.resolve(
163
- this.l2Blocks
164
- .slice(from - 1, from - 1 + limit)
165
- .filter(b => !proven || this.provenBlockNumber === undefined || b.number <= this.provenBlockNumber),
166
- );
167
- }
168
-
169
- public getPublishedCheckpoints(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 L2BlockNew 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
- }),
183
- );
184
- }
185
-
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 L2BlockNew doesn't have toCheckpoint()
193
- const checkpoint = await Checkpoint.random(block.checkpointNumber, { numBlocks: 1 });
194
- checkpoint.blocks = [block];
195
- return checkpoint;
215
+ public getBlocks(from: number, limit: number): Promise<L2Block[]> {
216
+ return Promise.resolve(this.l2Blocks.slice(from - 1, from - 1 + limit));
196
217
  }
197
218
 
198
- public getPublishedBlocks(from: number, limit: number, proven?: boolean): Promise<CheckpointedL2Block[]> {
199
- const blocks = this.l2Blocks
200
- .slice(from - 1, from - 1 + limit)
201
- .filter(b => !proven || this.provenBlockNumber === undefined || b.number <= this.provenBlockNumber);
219
+ public getCheckpoints(from: CheckpointNumber, limit: number) {
220
+ const checkpoints = this.checkpointList.slice(from - 1, from - 1 + limit);
202
221
  return Promise.resolve(
203
- blocks.map(block =>
204
- CheckpointedL2Block.fromFields({
205
- checkpointNumber: CheckpointNumber(block.number),
206
- block,
207
- l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
208
- attestations: [],
209
- }),
210
- ),
222
+ checkpoints.map(checkpoint => new PublishedCheckpoint(checkpoint, this.mockL1DataForCheckpoint(checkpoint), [])),
211
223
  );
212
224
  }
213
225
 
214
- getL2BlocksNew(from: BlockNumber, limit: number, proven?: boolean): Promise<L2BlockNew[]> {
215
- // getBlocks already returns L2BlockNew[], so just return directly
216
- return this.getBlocks(from, limit, proven);
226
+ public getCheckpointByArchive(archive: Fr): Promise<Checkpoint | undefined> {
227
+ const checkpoint = this.checkpointList.find(c => c.archive.root.equals(archive));
228
+ return Promise.resolve(checkpoint);
217
229
  }
218
230
 
219
- public async getPublishedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
231
+ public async getCheckpointedBlockByHash(blockHash: BlockHash): Promise<CheckpointedL2Block | undefined> {
220
232
  for (const block of this.l2Blocks) {
221
233
  const hash = await block.hash();
222
234
  if (hash.equals(blockHash)) {
223
- return CheckpointedL2Block.fromFields({
224
- checkpointNumber: CheckpointNumber(block.number),
225
- block,
226
- l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
227
- attestations: [],
228
- });
235
+ return this.toCheckpointedBlock(block);
229
236
  }
230
237
  }
231
238
  return undefined;
232
239
  }
233
240
 
234
- public getPublishedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
241
+ public getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
235
242
  const block = this.l2Blocks.find(b => b.archive.root.equals(archive));
236
243
  if (!block) {
237
244
  return Promise.resolve(undefined);
238
245
  }
239
- return Promise.resolve(
240
- CheckpointedL2Block.fromFields({
241
- checkpointNumber: CheckpointNumber(block.number),
242
- block,
243
- l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
244
- attestations: [],
245
- }),
246
- );
246
+ return Promise.resolve(this.toCheckpointedBlock(block));
247
247
  }
248
248
 
249
- public async getL2BlockNewByHash(blockHash: Fr): Promise<L2BlockNew | undefined> {
249
+ public async getL2BlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
250
250
  for (const block of this.l2Blocks) {
251
251
  const hash = await block.hash();
252
252
  if (hash.equals(blockHash)) {
@@ -256,12 +256,12 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
256
256
  return undefined;
257
257
  }
258
258
 
259
- public getL2BlockNewByArchive(archive: Fr): Promise<L2BlockNew | undefined> {
259
+ public getL2BlockByArchive(archive: Fr): Promise<L2Block | undefined> {
260
260
  const block = this.l2Blocks.find(b => b.archive.root.equals(archive));
261
261
  return Promise.resolve(block);
262
262
  }
263
263
 
264
- public async getBlockHeaderByHash(blockHash: Fr): Promise<BlockHeader | undefined> {
264
+ public async getBlockHeaderByHash(blockHash: BlockHash): Promise<BlockHeader | undefined> {
265
265
  for (const block of this.l2Blocks) {
266
266
  const hash = await block.hash();
267
267
  if (hash.equals(blockHash)) {
@@ -276,46 +276,77 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
276
276
  return Promise.resolve(block?.header);
277
277
  }
278
278
 
279
+ public async getBlockData(number: BlockNumber): Promise<BlockData | undefined> {
280
+ const block = this.l2Blocks[number - 1];
281
+ if (!block) {
282
+ return undefined;
283
+ }
284
+ return {
285
+ header: block.header,
286
+ archive: block.archive,
287
+ blockHash: await block.hash(),
288
+ checkpointNumber: block.checkpointNumber,
289
+ indexWithinCheckpoint: block.indexWithinCheckpoint,
290
+ };
291
+ }
292
+
293
+ public async getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
294
+ const block = this.l2Blocks.find(b => b.archive.root.equals(archive));
295
+ if (!block) {
296
+ return undefined;
297
+ }
298
+ return {
299
+ header: block.header,
300
+ archive: block.archive,
301
+ blockHash: await block.hash(),
302
+ checkpointNumber: block.checkpointNumber,
303
+ indexWithinCheckpoint: block.indexWithinCheckpoint,
304
+ };
305
+ }
306
+
279
307
  getBlockHeader(number: number | 'latest'): Promise<BlockHeader | undefined> {
280
308
  return Promise.resolve(this.l2Blocks.at(typeof number === 'number' ? number - 1 : -1)?.header);
281
309
  }
282
310
 
283
311
  getCheckpointsForEpoch(epochNumber: EpochNumber): Promise<Checkpoint[]> {
284
- // TODO(mbps): Implement this properly. This only works when we have one block per checkpoint.
285
- const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
286
- const [start, end] = getSlotRangeForEpoch(epochNumber, { epochDuration });
287
- const blocks = this.l2Blocks.filter(b => {
288
- const slot = b.header.globalVariables.slotNumber;
289
- return slot >= start && slot <= end;
290
- });
291
- // Create checkpoints from blocks - manually construct since L2BlockNew doesn't have toCheckpoint()
292
- return Promise.all(
293
- blocks.map(async block => {
294
- const checkpoint = await Checkpoint.random(block.checkpointNumber, { numBlocks: 1 });
295
- checkpoint.blocks = [block];
296
- return checkpoint;
297
- }),
312
+ return Promise.resolve(this.getCheckpointsInEpoch(epochNumber));
313
+ }
314
+
315
+ getCheckpointsDataForEpoch(epochNumber: EpochNumber): Promise<CheckpointData[]> {
316
+ const checkpoints = this.getCheckpointsInEpoch(epochNumber);
317
+ return Promise.resolve(
318
+ checkpoints.map(
319
+ (checkpoint): CheckpointData => ({
320
+ checkpointNumber: checkpoint.number,
321
+ header: checkpoint.header,
322
+ archive: checkpoint.archive,
323
+ checkpointOutHash: computeCheckpointOutHash(
324
+ checkpoint.blocks.map(b => b.body.txEffects.map(tx => tx.l2ToL1Msgs)),
325
+ ),
326
+ startBlock: checkpoint.blocks[0].number,
327
+ blockCount: checkpoint.blocks.length,
328
+ attestations: [],
329
+ l1: this.mockL1DataForCheckpoint(checkpoint),
330
+ }),
331
+ ),
298
332
  );
299
333
  }
300
334
 
301
- getBlocksForEpoch(epochNumber: EpochNumber): Promise<L2BlockNew[]> {
302
- const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
303
- const [start, end] = getSlotRangeForEpoch(epochNumber, { epochDuration });
304
- const blocks = this.l2Blocks.filter(b => {
305
- const slot = b.header.globalVariables.slotNumber;
306
- return slot >= start && slot <= end;
307
- });
308
- return Promise.resolve(blocks);
335
+ getCheckpointedBlocksForEpoch(epochNumber: EpochNumber): Promise<CheckpointedL2Block[]> {
336
+ const checkpoints = this.getCheckpointsInEpoch(epochNumber);
337
+ return Promise.resolve(
338
+ checkpoints.flatMap(checkpoint => checkpoint.blocks.map(block => this.toCheckpointedBlock(block))),
339
+ );
309
340
  }
310
341
 
311
- getBlocksForSlot(slotNumber: SlotNumber): Promise<L2BlockNew[]> {
342
+ getBlocksForSlot(slotNumber: SlotNumber): Promise<L2Block[]> {
312
343
  const blocks = this.l2Blocks.filter(b => b.header.globalVariables.slotNumber === slotNumber);
313
344
  return Promise.resolve(blocks);
314
345
  }
315
346
 
316
- async getBlockHeadersForEpoch(epochNumber: EpochNumber): Promise<BlockHeader[]> {
317
- const blocks = await this.getBlocksForEpoch(epochNumber);
318
- return blocks.map(b => b.header);
347
+ async getCheckpointedBlockHeadersForEpoch(epochNumber: EpochNumber): Promise<BlockHeader[]> {
348
+ const checkpointedBlocks = await this.getCheckpointedBlocksForEpoch(epochNumber);
349
+ return checkpointedBlocks.map(b => b.block.header);
319
350
  }
320
351
 
321
352
  /**
@@ -334,7 +365,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
334
365
  return {
335
366
  data: txEffect,
336
367
  l2BlockNumber: block.number,
337
- l2BlockHash: L2BlockHash.fromField(await block.hash()),
368
+ l2BlockHash: await block.hash(),
338
369
  txIndexInBlock: block.body.txEffects.indexOf(txEffect),
339
370
  };
340
371
  }
@@ -348,12 +379,14 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
348
379
  for (const block of this.l2Blocks) {
349
380
  for (const txEffect of block.body.txEffects) {
350
381
  if (txEffect.txHash.equals(txHash)) {
382
+ // In mock, assume all txs are checkpointed with successful execution
351
383
  return new TxReceipt(
352
384
  txHash,
353
- TxStatus.SUCCESS,
354
- '',
385
+ TxStatus.CHECKPOINTED,
386
+ TxExecutionResult.SUCCESS,
387
+ undefined,
355
388
  txEffect.transactionFee.toBigInt(),
356
- L2BlockHash.fromField(await block.hash()),
389
+ await block.hash(),
357
390
  block.number,
358
391
  );
359
392
  }
@@ -394,7 +427,10 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
394
427
 
395
428
  const makeTipId = (blockId: typeof latestBlockId) => ({
396
429
  block: blockId,
397
- checkpoint: { number: CheckpointNumber(blockId.number), hash: blockId.hash },
430
+ checkpoint: {
431
+ number: this.findCheckpointNumberForBlock(blockId.number) ?? CheckpointNumber(0),
432
+ hash: blockId.hash,
433
+ },
398
434
  });
399
435
 
400
436
  return {
@@ -482,4 +518,38 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
482
518
  getPendingChainValidationStatus(): Promise<ValidateCheckpointResult> {
483
519
  return Promise.resolve({ valid: true });
484
520
  }
521
+
522
+ /** Returns checkpoints whose slot falls within the given epoch. */
523
+ private getCheckpointsInEpoch(epochNumber: EpochNumber): Checkpoint[] {
524
+ const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
525
+ const [start, end] = getSlotRangeForEpoch(epochNumber, { epochDuration });
526
+ return this.checkpointList.filter(c => c.header.slotNumber >= start && c.header.slotNumber <= end);
527
+ }
528
+
529
+ /** Creates a mock L1PublishedData for a checkpoint. */
530
+ private mockL1DataForCheckpoint(checkpoint: Checkpoint): L1PublishedData {
531
+ return new L1PublishedData(BigInt(checkpoint.number), BigInt(checkpoint.number), Buffer32.random().toString());
532
+ }
533
+
534
+ /** Creates a CheckpointedL2Block from a block using stored checkpoint info. */
535
+ private toCheckpointedBlock(block: L2Block): CheckpointedL2Block {
536
+ const checkpoint = this.checkpointList.find(c => c.blocks.some(b => b.number === block.number));
537
+ const checkpointNumber = checkpoint?.number ?? block.checkpointNumber;
538
+ return new CheckpointedL2Block(
539
+ checkpointNumber,
540
+ block,
541
+ new L1PublishedData(
542
+ BigInt(block.number),
543
+ BigInt(block.number),
544
+ `0x${block.number.toString(16).padStart(64, '0')}`,
545
+ ),
546
+ [],
547
+ );
548
+ }
549
+
550
+ /** Finds the checkpoint number for a block, or undefined if the block is not in any checkpoint. */
551
+ private findCheckpointNumberForBlock(blockNumber: BlockNumber): CheckpointNumber | undefined {
552
+ const checkpoint = this.checkpointList.find(c => c.blocks.some(b => b.number === blockNumber));
553
+ return checkpoint?.number;
554
+ }
485
555
  }
@@ -12,7 +12,7 @@ import type { Secp256k1Signer } from '@aztec/foundation/crypto/secp256k1-signer'
12
12
  import { Fr } from '@aztec/foundation/curves/bn254';
13
13
  import { EthAddress } from '@aztec/foundation/eth-address';
14
14
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
15
- import { CommitteeAttestation, L2BlockNew } from '@aztec/stdlib/block';
15
+ import { CommitteeAttestation, L2Block } from '@aztec/stdlib/block';
16
16
  import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
17
17
  import { PrivateLog, PublicLog, SiloedTag, Tag } from '@aztec/stdlib/logs';
18
18
  import { InboxLeaf } from '@aztec/stdlib/messaging';
@@ -46,24 +46,40 @@ export function makeInboxMessage(
46
46
  }
47
47
 
48
48
  export function makeInboxMessages(
49
- count: number,
49
+ totalCount: number,
50
50
  opts: {
51
51
  initialHash?: Buffer16;
52
52
  initialCheckpointNumber?: CheckpointNumber;
53
+ messagesPerCheckpoint?: number;
53
54
  overrideFn?: (msg: InboxMessage, index: number) => InboxMessage;
54
55
  } = {},
55
56
  ): InboxMessage[] {
56
- const { initialHash = Buffer16.ZERO, overrideFn = msg => msg, initialCheckpointNumber = 1 } = opts;
57
+ const {
58
+ initialHash = Buffer16.ZERO,
59
+ overrideFn = msg => msg,
60
+ initialCheckpointNumber = CheckpointNumber(1),
61
+ messagesPerCheckpoint = 1,
62
+ } = opts;
63
+
57
64
  const messages: InboxMessage[] = [];
58
65
  let rollingHash = initialHash;
59
- for (let i = 0; i < count; i++) {
66
+ for (let i = 0; i < totalCount; i++) {
67
+ const msgIndex = i % messagesPerCheckpoint;
68
+ const checkpointNumber = CheckpointNumber.fromBigInt(
69
+ BigInt(initialCheckpointNumber) + BigInt(i) / BigInt(messagesPerCheckpoint),
70
+ );
60
71
  const leaf = Fr.random();
61
- const checkpointNumber = CheckpointNumber(i + initialCheckpointNumber);
62
- const message = overrideFn(makeInboxMessage(rollingHash, { leaf, checkpointNumber }), i);
72
+ const message = overrideFn(
73
+ makeInboxMessage(rollingHash, {
74
+ leaf,
75
+ checkpointNumber,
76
+ index: InboxLeaf.smallestIndexForCheckpoint(checkpointNumber) + BigInt(msgIndex),
77
+ }),
78
+ i,
79
+ );
63
80
  rollingHash = message.rollingHash;
64
81
  messages.push(message);
65
82
  }
66
-
67
83
  return messages;
68
84
  }
69
85
 
@@ -268,8 +284,8 @@ export async function makeCheckpointWithLogs(
268
284
  ): Promise<PublishedCheckpoint> {
269
285
  const { previousArchive, numTxsPerBlock = 4, privateLogs, publicLogs } = options;
270
286
 
271
- const block = await L2BlockNew.random(BlockNumber(blockNumber), {
272
- checkpointNumber: CheckpointNumber(blockNumber),
287
+ const block = await L2Block.random(BlockNumber(blockNumber), {
288
+ checkpointNumber: CheckpointNumber.fromBlockNumber(BlockNumber(blockNumber)),
273
289
  indexWithinCheckpoint: IndexWithinCheckpoint(0),
274
290
  state: makeStateForBlock(blockNumber, numTxsPerBlock),
275
291
  ...(previousArchive ? { lastArchive: previousArchive } : {}),
@@ -289,7 +305,7 @@ export async function makeCheckpointWithLogs(
289
305
  AppendOnlyTreeSnapshot.random(),
290
306
  CheckpointHeader.random(),
291
307
  [block],
292
- CheckpointNumber(blockNumber),
308
+ CheckpointNumber.fromBlockNumber(BlockNumber(blockNumber)),
293
309
  );
294
310
  return makePublishedCheckpoint(checkpoint, blockNumber);
295
311
  }
@@ -0,0 +1,109 @@
1
+ import type { BlobClientInterface } from '@aztec/blob-client/client';
2
+ import type { RollupContract } from '@aztec/ethereum/contracts';
3
+ import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
4
+ import { Buffer32 } from '@aztec/foundation/buffer';
5
+ import { Fr } from '@aztec/foundation/curves/bn254';
6
+ import { EthAddress } from '@aztec/foundation/eth-address';
7
+ import type { FunctionsOf } from '@aztec/foundation/types';
8
+ import type { ArchiverEmitter } from '@aztec/stdlib/block';
9
+ import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
10
+ import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/telemetry-client';
11
+
12
+ import { mock } from 'jest-mock-extended';
13
+ import { EventEmitter } from 'node:events';
14
+
15
+ import { Archiver } from '../archiver.js';
16
+ import { ArchiverInstrumentation } from '../modules/instrumentation.js';
17
+ import type { ArchiverL1Synchronizer } from '../modules/l1_synchronizer.js';
18
+ import type { KVArchiverDataStore } from '../store/kv_archiver_store.js';
19
+
20
+ /** Noop L1 synchronizer for testing without L1 connectivity. */
21
+ class NoopL1Synchronizer implements FunctionsOf<ArchiverL1Synchronizer> {
22
+ public readonly tracer: Tracer;
23
+
24
+ constructor(tracer: Tracer) {
25
+ this.tracer = tracer;
26
+ }
27
+
28
+ setConfig(_config: unknown) {}
29
+ getL1BlockNumber(): bigint | undefined {
30
+ return 0n;
31
+ }
32
+ getL1Timestamp(): bigint | undefined {
33
+ return 0n;
34
+ }
35
+ testEthereumNodeSynced(): Promise<void> {
36
+ return Promise.resolve();
37
+ }
38
+ syncFromL1(_initialSyncComplete: boolean): Promise<void> {
39
+ return Promise.resolve();
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Archiver with mocked L1 connectivity for testing.
45
+ * Uses mock L1 clients and a noop synchronizer, enabling tests that
46
+ * don't require real Ethereum connectivity.
47
+ */
48
+ export class NoopL1Archiver extends Archiver {
49
+ constructor(
50
+ dataStore: KVArchiverDataStore,
51
+ l1Constants: L1RollupConstants & { genesisArchiveRoot: Fr },
52
+ instrumentation: ArchiverInstrumentation,
53
+ ) {
54
+ // Create mocks for L1 clients
55
+ const publicClient = mock<ViemPublicClient>();
56
+ const debugClient = mock<ViemPublicDebugClient>();
57
+ const rollup = mock<RollupContract>();
58
+ const blobClient = mock<BlobClientInterface>();
59
+
60
+ // Mock methods called during start()
61
+ blobClient.testSources.mockResolvedValue();
62
+ publicClient.getBlockNumber.mockResolvedValue(1n);
63
+
64
+ const events = new EventEmitter() as ArchiverEmitter;
65
+ const synchronizer = new NoopL1Synchronizer(instrumentation.tracer);
66
+
67
+ super(
68
+ publicClient,
69
+ debugClient,
70
+ rollup,
71
+ {
72
+ registryAddress: EthAddress.ZERO,
73
+ governanceProposerAddress: EthAddress.ZERO,
74
+ slashFactoryAddress: EthAddress.ZERO,
75
+ slashingProposerAddress: EthAddress.ZERO,
76
+ },
77
+ dataStore,
78
+ {
79
+ pollingIntervalMs: 1000,
80
+ batchSize: 100,
81
+ skipValidateCheckpointAttestations: true,
82
+ maxAllowedEthClientDriftSeconds: 300,
83
+ ethereumAllowNoDebugHosts: true, // Skip trace validation
84
+ },
85
+ blobClient,
86
+ instrumentation,
87
+ { ...l1Constants, l1StartBlockHash: Buffer32.random() },
88
+ synchronizer as ArchiverL1Synchronizer,
89
+ events,
90
+ );
91
+ }
92
+
93
+ /** Override start to skip L1 validation checks. */
94
+ public override start(_blockUntilSynced?: boolean): Promise<void> {
95
+ // Just start the running promise without L1 checks
96
+ this.runningPromise.start();
97
+ return Promise.resolve();
98
+ }
99
+ }
100
+
101
+ /** Creates an archiver with mocked L1 connectivity for testing. */
102
+ export async function createNoopL1Archiver(
103
+ dataStore: KVArchiverDataStore,
104
+ l1Constants: L1RollupConstants & { genesisArchiveRoot: Fr },
105
+ telemetry: TelemetryClient = getTelemetryClient(),
106
+ ): Promise<NoopL1Archiver> {
107
+ const instrumentation = await ArchiverInstrumentation.new(telemetry, () => dataStore.estimateSize());
108
+ return new NoopL1Archiver(dataStore, l1Constants, instrumentation);
109
+ }