@aztec/archiver 0.0.1-commit.b33fc05d0 → 0.0.1-commit.b3d3157a

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 (121) hide show
  1. package/README.md +19 -11
  2. package/dest/archiver.d.ts +53 -16
  3. package/dest/archiver.d.ts.map +1 -1
  4. package/dest/archiver.js +248 -76
  5. package/dest/config.d.ts +4 -1
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +21 -14
  8. package/dest/errors.d.ts +50 -2
  9. package/dest/errors.d.ts.map +1 -1
  10. package/dest/errors.js +74 -2
  11. package/dest/factory.d.ts +13 -7
  12. package/dest/factory.d.ts.map +1 -1
  13. package/dest/factory.js +46 -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/calldata_retriever.d.ts +2 -1
  18. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  19. package/dest/l1/calldata_retriever.js +15 -5
  20. package/dest/l1/data_retrieval.d.ts +19 -10
  21. package/dest/l1/data_retrieval.d.ts.map +1 -1
  22. package/dest/l1/data_retrieval.js +25 -32
  23. package/dest/l1/trace_tx.d.ts +12 -66
  24. package/dest/l1/trace_tx.d.ts.map +1 -1
  25. package/dest/l1/validate_historical_logs.d.ts +23 -0
  26. package/dest/l1/validate_historical_logs.d.ts.map +1 -0
  27. package/dest/l1/validate_historical_logs.js +108 -0
  28. package/dest/modules/contract_data_source_adapter.d.ts +25 -0
  29. package/dest/modules/contract_data_source_adapter.d.ts.map +1 -0
  30. package/dest/modules/contract_data_source_adapter.js +40 -0
  31. package/dest/modules/data_source_base.d.ts +71 -47
  32. package/dest/modules/data_source_base.d.ts.map +1 -1
  33. package/dest/modules/data_source_base.js +269 -137
  34. package/dest/modules/data_store_updater.d.ts +30 -13
  35. package/dest/modules/data_store_updater.d.ts.map +1 -1
  36. package/dest/modules/data_store_updater.js +166 -116
  37. package/dest/modules/instrumentation.d.ts +7 -2
  38. package/dest/modules/instrumentation.d.ts.map +1 -1
  39. package/dest/modules/instrumentation.js +25 -7
  40. package/dest/modules/l1_synchronizer.d.ts +11 -6
  41. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  42. package/dest/modules/l1_synchronizer.js +414 -213
  43. package/dest/modules/outbox_trees_resolver.d.ts +62 -0
  44. package/dest/modules/outbox_trees_resolver.d.ts.map +1 -0
  45. package/dest/modules/outbox_trees_resolver.js +162 -0
  46. package/dest/modules/validation.d.ts +4 -3
  47. package/dest/modules/validation.d.ts.map +1 -1
  48. package/dest/modules/validation.js +6 -6
  49. package/dest/store/block_store.d.ts +165 -66
  50. package/dest/store/block_store.d.ts.map +1 -1
  51. package/dest/store/block_store.js +648 -233
  52. package/dest/store/contract_class_store.d.ts +17 -4
  53. package/dest/store/contract_class_store.d.ts.map +1 -1
  54. package/dest/store/contract_class_store.js +24 -68
  55. package/dest/store/contract_instance_store.d.ts +28 -1
  56. package/dest/store/contract_instance_store.d.ts.map +1 -1
  57. package/dest/store/contract_instance_store.js +37 -2
  58. package/dest/store/data_stores.d.ts +68 -0
  59. package/dest/store/data_stores.d.ts.map +1 -0
  60. package/dest/store/data_stores.js +54 -0
  61. package/dest/store/function_names_cache.d.ts +17 -0
  62. package/dest/store/function_names_cache.d.ts.map +1 -0
  63. package/dest/store/function_names_cache.js +30 -0
  64. package/dest/store/l2_tips_cache.d.ts +13 -7
  65. package/dest/store/l2_tips_cache.d.ts.map +1 -1
  66. package/dest/store/l2_tips_cache.js +13 -76
  67. package/dest/store/log_store.d.ts +42 -37
  68. package/dest/store/log_store.d.ts.map +1 -1
  69. package/dest/store/log_store.js +262 -408
  70. package/dest/store/log_store_codec.d.ts +70 -0
  71. package/dest/store/log_store_codec.d.ts.map +1 -0
  72. package/dest/store/log_store_codec.js +101 -0
  73. package/dest/store/message_store.d.ts +11 -1
  74. package/dest/store/message_store.d.ts.map +1 -1
  75. package/dest/store/message_store.js +50 -8
  76. package/dest/test/fake_l1_state.d.ts +15 -3
  77. package/dest/test/fake_l1_state.d.ts.map +1 -1
  78. package/dest/test/fake_l1_state.js +88 -18
  79. package/dest/test/mock_l1_to_l2_message_source.d.ts +1 -1
  80. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  81. package/dest/test/mock_l1_to_l2_message_source.js +2 -1
  82. package/dest/test/mock_l2_block_source.d.ts +51 -50
  83. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  84. package/dest/test/mock_l2_block_source.js +244 -181
  85. package/dest/test/noop_l1_archiver.d.ts +10 -6
  86. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  87. package/dest/test/noop_l1_archiver.js +22 -9
  88. package/package.json +14 -14
  89. package/src/archiver.ts +315 -83
  90. package/src/config.ts +24 -11
  91. package/src/errors.ts +117 -2
  92. package/src/factory.ts +54 -28
  93. package/src/index.ts +18 -2
  94. package/src/l1/calldata_retriever.ts +16 -5
  95. package/src/l1/data_retrieval.ts +36 -45
  96. package/src/l1/validate_historical_logs.ts +140 -0
  97. package/src/modules/contract_data_source_adapter.ts +55 -0
  98. package/src/modules/data_source_base.ts +341 -174
  99. package/src/modules/data_store_updater.ts +201 -147
  100. package/src/modules/instrumentation.ts +28 -8
  101. package/src/modules/l1_synchronizer.ts +549 -254
  102. package/src/modules/outbox_trees_resolver.ts +199 -0
  103. package/src/modules/validation.ts +10 -9
  104. package/src/store/block_store.ts +807 -285
  105. package/src/store/contract_class_store.ts +31 -103
  106. package/src/store/contract_instance_store.ts +51 -5
  107. package/src/store/data_stores.ts +104 -0
  108. package/src/store/function_names_cache.ts +37 -0
  109. package/src/store/l2_tips_cache.ts +16 -70
  110. package/src/store/log_store.ts +301 -559
  111. package/src/store/log_store_codec.ts +132 -0
  112. package/src/store/message_store.ts +59 -9
  113. package/src/structs/inbox_message.ts +1 -1
  114. package/src/test/fake_l1_state.ts +111 -29
  115. package/src/test/mock_l1_to_l2_message_source.ts +1 -0
  116. package/src/test/mock_l2_block_source.ts +299 -232
  117. package/src/test/noop_l1_archiver.ts +42 -9
  118. package/dest/store/kv_archiver_store.d.ts +0 -363
  119. package/dest/store/kv_archiver_store.d.ts.map +0 -1
  120. package/dest/store/kv_archiver_store.js +0 -476
  121. package/src/store/kv_archiver_store.ts +0 -689
@@ -1,6 +1,12 @@
1
1
  import { GENESIS_ARCHIVE_ROOT } from '@aztec/constants';
2
2
  import { DefaultL1ContractsConfig } from '@aztec/ethereum/config';
3
- import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
3
+ import {
4
+ BlockNumber,
5
+ CheckpointNumber,
6
+ EpochNumber,
7
+ IndexWithinCheckpoint,
8
+ SlotNumber,
9
+ } from '@aztec/foundation/branded-types';
4
10
  import { Buffer32 } from '@aztec/foundation/buffer';
5
11
  import { Fr } from '@aztec/foundation/curves/bn254';
6
12
  import { EthAddress } from '@aztec/foundation/eth-address';
@@ -9,24 +15,34 @@ import type { FunctionSelector } from '@aztec/stdlib/abi';
9
15
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
10
16
  import {
11
17
  type BlockData,
12
- BlockHash,
13
- CheckpointedL2Block,
18
+ type BlockHash,
19
+ type BlockQuery,
20
+ type BlockTag,
21
+ type BlocksQuery,
22
+ Body,
23
+ type CheckpointQuery,
24
+ type CheckpointsQuery,
25
+ GENESIS_BLOCK_HEADER_HASH,
26
+ GENESIS_CHECKPOINT_HEADER_HASH,
14
27
  L2Block,
15
28
  type L2BlockSource,
16
29
  type L2Tips,
30
+ type ProposedCheckpointQuery,
17
31
  type ValidateCheckpointResult,
18
32
  } from '@aztec/stdlib/block';
19
- import { Checkpoint, type CheckpointData, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
20
- import type { ContractClassPublic, ContractDataSource, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
21
33
  import {
22
- EmptyL1RollupConstants,
23
- type L1RollupConstants,
24
- getEpochAtSlot,
25
- getSlotRangeForEpoch,
26
- } from '@aztec/stdlib/epoch-helpers';
34
+ Checkpoint,
35
+ type CheckpointData,
36
+ L1PublishedData,
37
+ type ProposedCheckpointData,
38
+ PublishedCheckpoint,
39
+ } from '@aztec/stdlib/checkpoint';
40
+ import type { ContractClassPublic, ContractDataSource, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
41
+ import { EmptyL1RollupConstants, type L1RollupConstants, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
27
42
  import { computeCheckpointOutHash } from '@aztec/stdlib/messaging';
28
43
  import { CheckpointHeader } from '@aztec/stdlib/rollup';
29
- import { type BlockHeader, TxExecutionResult, TxHash, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
44
+ import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
45
+ import { BlockHeader, TxHash } from '@aztec/stdlib/tx';
30
46
  import type { UInt64 } from '@aztec/stdlib/types';
31
47
 
32
48
  /**
@@ -39,9 +55,68 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
39
55
  private provenBlockNumber: number = 0;
40
56
  private finalizedBlockNumber: number = 0;
41
57
  private checkpointedBlockNumber: number = 0;
58
+ private proposedCheckpointBlockNumber: number = 0;
59
+
60
+ private initialHeader: BlockHeader = BlockHeader.empty();
61
+ private initialHeaderHash: BlockHash = GENESIS_BLOCK_HEADER_HASH;
62
+ private genesisArchiveRoot?: Fr;
63
+ private genesisBlock?: L2Block;
42
64
 
43
65
  private log = createLogger('archiver:mock_l2_block_source');
44
66
 
67
+ /** Returns the initial header used to synthesize block 0. */
68
+ public getInitialHeader(): BlockHeader {
69
+ return this.initialHeader;
70
+ }
71
+
72
+ /**
73
+ * Sets the initial header used to synthesize block 0. Tests that wire up a real
74
+ * world-state should call this with `worldState.getInitialHeader()` so the L2BlockStream
75
+ * agrees on the genesis hash on both sides. Precomputes and caches the header hash so
76
+ * `getGenesisBlockHash()` can return synchronously.
77
+ */
78
+ public async setInitialHeader(header: BlockHeader): Promise<void> {
79
+ this.initialHeader = header;
80
+ this.initialHeaderHash = await header.hash();
81
+ this.genesisBlock = undefined;
82
+ }
83
+
84
+ /**
85
+ * Returns the precomputed hash of the genesis block header. Defaults to the static
86
+ * {@link GENESIS_BLOCK_HEADER_HASH} unless {@link setInitialHeader} has been called with a
87
+ * custom header.
88
+ */
89
+ public getGenesisBlockHash(): BlockHash {
90
+ return this.initialHeaderHash;
91
+ }
92
+
93
+ /**
94
+ * Sets the post-genesis archive root used to synthesize block 0. Mirrors the real archiver,
95
+ * whose synthetic block 0 carries `new AppendOnlyTreeSnapshot(genesisArchiveRoot, 1)` rather
96
+ * than `AppendOnlyTreeSnapshot.empty()`. Tests wiring up a real world-state should set this so
97
+ * archive-based block lookups against the mock match production semantics.
98
+ */
99
+ public setGenesisArchiveRoot(root: Fr): void {
100
+ this.genesisArchiveRoot = root;
101
+ this.genesisBlock = undefined;
102
+ }
103
+
104
+ private getGenesisBlock(): L2Block {
105
+ if (this.genesisBlock) {
106
+ return this.genesisBlock;
107
+ }
108
+ const archive = this.genesisArchiveRoot
109
+ ? new AppendOnlyTreeSnapshot(this.genesisArchiveRoot, 1)
110
+ : AppendOnlyTreeSnapshot.empty();
111
+ return (this.genesisBlock = new L2Block(
112
+ archive,
113
+ this.initialHeader,
114
+ Body.empty(),
115
+ CheckpointNumber.ZERO,
116
+ IndexWithinCheckpoint(0),
117
+ ));
118
+ }
119
+
45
120
  /** Creates blocks grouped into single-block checkpoints. */
46
121
  public async createBlocks(numBlocks: number) {
47
122
  await this.createCheckpoints(numBlocks, 1);
@@ -89,6 +164,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
89
164
  });
90
165
  // Keep tip numbers consistent with remaining blocks.
91
166
  this.checkpointedBlockNumber = Math.min(this.checkpointedBlockNumber, maxBlockNum);
167
+ this.proposedCheckpointBlockNumber = Math.min(this.proposedCheckpointBlockNumber, maxBlockNum);
92
168
  this.provenBlockNumber = Math.min(this.provenBlockNumber, maxBlockNum);
93
169
  this.finalizedBlockNumber = Math.min(this.finalizedBlockNumber, maxBlockNum);
94
170
  this.log.verbose(`Removed ${numBlocks} blocks from the mock L2 block source`);
@@ -105,9 +181,17 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
105
181
  this.finalizedBlockNumber = finalizedBlockNumber;
106
182
  }
107
183
 
184
+ public setProposedCheckpointBlockNumber(blockNumber: number) {
185
+ this.proposedCheckpointBlockNumber = blockNumber;
186
+ }
187
+
108
188
  public setCheckpointedBlockNumber(checkpointedBlockNumber: number) {
109
189
  const prevCheckpointed = this.checkpointedBlockNumber;
110
190
  this.checkpointedBlockNumber = checkpointedBlockNumber;
191
+ // Proposed checkpoint is always at least as advanced as checkpointed
192
+ if (this.proposedCheckpointBlockNumber < checkpointedBlockNumber) {
193
+ this.proposedCheckpointBlockNumber = checkpointedBlockNumber;
194
+ }
111
195
  // Auto-create single-block checkpoints for newly checkpointed blocks that don't have one yet.
112
196
  // This handles blocks added via addProposedBlocks that are now being marked as checkpointed.
113
197
  const newCheckpoints: Checkpoint[] = [];
@@ -155,80 +239,36 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
155
239
  * Gets the number of the latest L2 block processed by the block source implementation.
156
240
  * @returns In this mock instance, returns the number of L2 blocks that we've mocked.
157
241
  */
158
- public getBlockNumber() {
159
- return Promise.resolve(BlockNumber(this.l2Blocks.length));
160
- }
161
-
162
- public getProvenBlockNumber() {
163
- return Promise.resolve(BlockNumber(this.provenBlockNumber));
164
- }
165
-
166
- public getCheckpointedL2BlockNumber() {
167
- return Promise.resolve(BlockNumber(this.checkpointedBlockNumber));
168
- }
169
-
170
- public getFinalizedL2BlockNumber() {
171
- return Promise.resolve(BlockNumber(this.finalizedBlockNumber));
172
- }
173
-
174
- public getCheckpointedBlock(number: BlockNumber): Promise<CheckpointedL2Block | undefined> {
175
- if (number > this.checkpointedBlockNumber) {
176
- return Promise.resolve(undefined);
242
+ public getBlockNumber(): Promise<BlockNumber>;
243
+ public getBlockNumber(query: BlockQuery): Promise<BlockNumber | undefined>;
244
+ public async getBlockNumber(query?: BlockQuery): Promise<BlockNumber | undefined> {
245
+ if (!query) {
246
+ return BlockNumber(this.l2Blocks.length);
177
247
  }
178
- const block = this.l2Blocks[number - 1];
179
- if (!block) {
180
- return Promise.resolve(undefined);
248
+ if ('number' in query) {
249
+ return query.number;
181
250
  }
182
- return Promise.resolve(this.toCheckpointedBlock(block));
183
- }
184
-
185
- public async getCheckpointedBlocks(from: BlockNumber, limit: number): Promise<CheckpointedL2Block[]> {
186
- const result: CheckpointedL2Block[] = [];
187
- for (let i = 0; i < limit; i++) {
188
- const blockNum = from + i;
189
- if (blockNum > this.checkpointedBlockNumber) {
190
- break;
191
- }
192
- const block = await this.getCheckpointedBlock(BlockNumber(blockNum));
193
- if (block) {
194
- result.push(block);
195
- }
251
+ if ('tag' in query) {
252
+ return BlockNumber(this.resolveBlockTag(query.tag));
196
253
  }
197
- return result;
254
+ const block = await this.getBlock(query);
255
+ return block ? block.header.globalVariables.blockNumber : undefined;
198
256
  }
199
257
 
200
- /**
201
- * Gets an l2 block.
202
- * @param number - The block number to return (inclusive).
203
- * @returns The requested L2 block.
204
- */
205
- public getBlock(number: number): Promise<L2Block | undefined> {
206
- const block = this.l2Blocks[number - 1];
207
- return Promise.resolve(block);
208
- }
209
-
210
- /**
211
- * Gets an L2 block (new format).
212
- * @param number - The block number to return.
213
- * @returns The requested L2 block.
214
- */
215
- public getL2Block(number: BlockNumber): Promise<L2Block | undefined> {
216
- const block = this.l2Blocks[number - 1];
217
- return Promise.resolve(block);
258
+ public getProposedCheckpointL2BlockNumber() {
259
+ return Promise.resolve(BlockNumber(this.proposedCheckpointBlockNumber));
218
260
  }
219
261
 
220
- /**
221
- * Gets up to `limit` amount of L2 blocks starting from `from`.
222
- * @param from - Number of the first block to return (inclusive).
223
- * @param limit - The maximum number of blocks to return.
224
- * @returns The requested mocked L2 blocks.
225
- */
226
- public getBlocks(from: number, limit: number): Promise<L2Block[]> {
227
- return Promise.resolve(this.l2Blocks.slice(from - 1, from - 1 + limit));
262
+ public getCheckpoint(query: CheckpointQuery): Promise<PublishedCheckpoint | undefined> {
263
+ const checkpoint = this.resolveCheckpointQuery(query);
264
+ if (!checkpoint) {
265
+ return Promise.resolve(undefined);
266
+ }
267
+ return Promise.resolve(new PublishedCheckpoint(checkpoint, this.mockL1DataForCheckpoint(checkpoint), []));
228
268
  }
229
269
 
230
- public getCheckpoints(from: CheckpointNumber, limit: number) {
231
- const checkpoints = this.checkpointList.slice(from - 1, from - 1 + limit);
270
+ public getCheckpoints(query: CheckpointsQuery): Promise<PublishedCheckpoint[]> {
271
+ const checkpoints = this.resolveCheckpointsQuery(query);
232
272
  return Promise.resolve(
233
273
  checkpoints.map(checkpoint => new PublishedCheckpoint(checkpoint, this.mockL1DataForCheckpoint(checkpoint), [])),
234
274
  );
@@ -239,115 +279,65 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
239
279
  return Promise.resolve(checkpoint);
240
280
  }
241
281
 
242
- public async getCheckpointedBlockByHash(blockHash: BlockHash): Promise<CheckpointedL2Block | undefined> {
243
- for (const block of this.l2Blocks) {
244
- const hash = await block.hash();
245
- if (hash.equals(blockHash)) {
246
- return this.toCheckpointedBlock(block);
247
- }
248
- }
249
- return undefined;
250
- }
251
-
252
- public getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
253
- const block = this.l2Blocks.find(b => b.archive.root.equals(archive));
254
- if (!block) {
282
+ public getCheckpointData(query: CheckpointQuery): Promise<CheckpointData | undefined> {
283
+ const checkpoint = this.resolveCheckpointQuery(query);
284
+ if (!checkpoint) {
255
285
  return Promise.resolve(undefined);
256
286
  }
257
- return Promise.resolve(this.toCheckpointedBlock(block));
287
+ return Promise.resolve(this.checkpointToData(checkpoint));
258
288
  }
259
289
 
260
- public async getL2BlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
261
- for (const block of this.l2Blocks) {
262
- const hash = await block.hash();
263
- if (hash.equals(blockHash)) {
264
- return block;
265
- }
266
- }
267
- return undefined;
290
+ public getCheckpointsData(query: CheckpointsQuery): Promise<CheckpointData[]> {
291
+ const checkpoints = this.resolveCheckpointsQuery(query);
292
+ return Promise.resolve(checkpoints.map(c => this.checkpointToData(c)));
268
293
  }
269
294
 
270
- public getL2BlockByArchive(archive: Fr): Promise<L2Block | undefined> {
271
- const block = this.l2Blocks.find(b => b.archive.root.equals(archive));
272
- return Promise.resolve(block);
273
- }
274
-
275
- public async getBlockHeaderByHash(blockHash: BlockHash): Promise<BlockHeader | undefined> {
276
- for (const block of this.l2Blocks) {
277
- const hash = await block.hash();
278
- if (hash.equals(blockHash)) {
279
- return block.header;
280
- }
281
- }
282
- return undefined;
283
- }
284
-
285
- public getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
286
- const block = this.l2Blocks.find(b => b.archive.root.equals(archive));
287
- return Promise.resolve(block?.header);
288
- }
289
-
290
- public async getBlockData(number: BlockNumber): Promise<BlockData | undefined> {
291
- const block = this.l2Blocks[number - 1];
292
- if (!block) {
293
- return undefined;
294
- }
295
+ private checkpointToData(checkpoint: Checkpoint): CheckpointData {
295
296
  return {
296
- header: block.header,
297
- archive: block.archive,
298
- blockHash: await block.hash(),
299
- checkpointNumber: block.checkpointNumber,
300
- indexWithinCheckpoint: block.indexWithinCheckpoint,
297
+ checkpointNumber: checkpoint.number,
298
+ header: checkpoint.header,
299
+ archive: checkpoint.archive,
300
+ checkpointOutHash: computeCheckpointOutHash(
301
+ checkpoint.blocks.map(b => b.body.txEffects.map(tx => tx.l2ToL1Msgs)),
302
+ ),
303
+ startBlock: checkpoint.blocks[0].number,
304
+ blockCount: checkpoint.blocks.length,
305
+ feeAssetPriceModifier: checkpoint.feeAssetPriceModifier,
306
+ attestations: [],
307
+ l1: this.mockL1DataForCheckpoint(checkpoint),
301
308
  };
302
309
  }
303
310
 
304
- public async getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
305
- const block = this.l2Blocks.find(b => b.archive.root.equals(archive));
306
- if (!block) {
307
- return undefined;
311
+ private resolveCheckpointQuery(query: CheckpointQuery): Checkpoint | undefined {
312
+ if ('number' in query) {
313
+ return this.checkpointList[query.number - 1];
314
+ }
315
+ if ('slot' in query) {
316
+ return this.checkpointList.find(c => c.header.slotNumber === query.slot);
317
+ }
318
+ switch (query.tag) {
319
+ case 'checkpointed':
320
+ return this.checkpointList[this.checkpointList.length - 1];
321
+ case 'proven': {
322
+ const provenCheckpoint = this.checkpointList.filter(c =>
323
+ c.blocks.some(b => b.number <= this.provenBlockNumber),
324
+ );
325
+ return provenCheckpoint.at(-1);
326
+ }
327
+ case 'finalized': {
328
+ const finalizedCheckpoint = this.checkpointList.filter(c =>
329
+ c.blocks.some(b => b.number <= this.finalizedBlockNumber),
330
+ );
331
+ return finalizedCheckpoint.at(-1);
332
+ }
308
333
  }
309
- return {
310
- header: block.header,
311
- archive: block.archive,
312
- blockHash: await block.hash(),
313
- checkpointNumber: block.checkpointNumber,
314
- indexWithinCheckpoint: block.indexWithinCheckpoint,
315
- };
316
- }
317
-
318
- getBlockHeader(number: number | 'latest'): Promise<BlockHeader | undefined> {
319
- return Promise.resolve(this.l2Blocks.at(typeof number === 'number' ? number - 1 : -1)?.header);
320
- }
321
-
322
- getCheckpointsForEpoch(epochNumber: EpochNumber): Promise<Checkpoint[]> {
323
- return Promise.resolve(this.getCheckpointsInEpoch(epochNumber));
324
- }
325
-
326
- getCheckpointsDataForEpoch(epochNumber: EpochNumber): Promise<CheckpointData[]> {
327
- const checkpoints = this.getCheckpointsInEpoch(epochNumber);
328
- return Promise.resolve(
329
- checkpoints.map(
330
- (checkpoint): CheckpointData => ({
331
- checkpointNumber: checkpoint.number,
332
- header: checkpoint.header,
333
- archive: checkpoint.archive,
334
- checkpointOutHash: computeCheckpointOutHash(
335
- checkpoint.blocks.map(b => b.body.txEffects.map(tx => tx.l2ToL1Msgs)),
336
- ),
337
- startBlock: checkpoint.blocks[0].number,
338
- blockCount: checkpoint.blocks.length,
339
- attestations: [],
340
- l1: this.mockL1DataForCheckpoint(checkpoint),
341
- }),
342
- ),
343
- );
344
334
  }
345
335
 
346
- getCheckpointedBlocksForEpoch(epochNumber: EpochNumber): Promise<CheckpointedL2Block[]> {
347
- const checkpoints = this.getCheckpointsInEpoch(epochNumber);
348
- return Promise.resolve(
349
- checkpoints.flatMap(checkpoint => checkpoint.blocks.map(block => this.toCheckpointedBlock(block))),
350
- );
336
+ private resolveCheckpointsQuery(query: CheckpointsQuery): Checkpoint[] {
337
+ if ('from' in query) {
338
+ return this.checkpointList.slice(query.from - 1, query.from - 1 + query.limit);
339
+ }
340
+ return this.getCheckpointsInEpoch(query.epoch);
351
341
  }
352
342
 
353
343
  getBlocksForSlot(slotNumber: SlotNumber): Promise<L2Block[]> {
@@ -355,11 +345,6 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
355
345
  return Promise.resolve(blocks);
356
346
  }
357
347
 
358
- async getCheckpointedBlockHeadersForEpoch(epochNumber: EpochNumber): Promise<BlockHeader[]> {
359
- const checkpointedBlocks = await this.getCheckpointedBlocksForEpoch(epochNumber);
360
- return checkpointedBlocks.map(b => b.block.header);
361
- }
362
-
363
348
  /**
364
349
  * Gets a tx effect.
365
350
  * @param txHash - The hash of the tx corresponding to the tx effect.
@@ -377,79 +362,80 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
377
362
  data: txEffect,
378
363
  l2BlockNumber: block.number,
379
364
  l2BlockHash: await block.hash(),
380
- txIndexInBlock: block.body.txEffects.indexOf(txEffect),
365
+ txIndexInBlock: block.body.txEffects.findIndex(t => t.txHash.equals(txHash)),
366
+ slotNumber: block.header.globalVariables.slotNumber,
381
367
  };
382
368
  }
383
369
 
384
- /**
385
- * Gets a receipt of a settled tx.
386
- * @param txHash - The hash of a tx we try to get the receipt for.
387
- * @returns The requested tx receipt (or undefined if not found).
388
- */
389
- public async getSettledTxReceipt(txHash: TxHash): Promise<TxReceipt | undefined> {
390
- for (const block of this.l2Blocks) {
391
- for (const txEffect of block.body.txEffects) {
392
- if (txEffect.txHash.equals(txHash)) {
393
- // In mock, assume all txs are checkpointed with successful execution
394
- return new TxReceipt(
395
- txHash,
396
- TxStatus.CHECKPOINTED,
397
- TxExecutionResult.SUCCESS,
398
- undefined,
399
- txEffect.transactionFee.toBigInt(),
400
- await block.hash(),
401
- block.number,
402
- getEpochAtSlot(block.slot, EmptyL1RollupConstants),
403
- );
404
- }
405
- }
406
- }
407
- return undefined;
370
+ public getL2ToL1MembershipWitness(): Promise<undefined> {
371
+ // Mock does not back the L2-to-L1 message witness flow.
372
+ return Promise.resolve(undefined);
408
373
  }
409
374
 
410
375
  async getL2Tips(): Promise<L2Tips> {
411
- const [latest, proven, finalized, checkpointed] = [
376
+ const [latest, proven, finalized, checkpointed, proposedCheckpoint] = [
412
377
  await this.getBlockNumber(),
413
- await this.getProvenBlockNumber(),
378
+ this.provenBlockNumber,
414
379
  this.finalizedBlockNumber,
415
380
  this.checkpointedBlockNumber,
381
+ await this.getProposedCheckpointL2BlockNumber(),
416
382
  ] as const;
417
383
 
418
384
  const latestBlock = this.l2Blocks[latest - 1];
419
385
  const provenBlock = this.l2Blocks[proven - 1];
420
386
  const finalizedBlock = this.l2Blocks[finalized - 1];
421
387
  const checkpointedBlock = this.l2Blocks[checkpointed - 1];
388
+ const proposedCheckpointBlock = this.l2Blocks[proposedCheckpoint - 1];
389
+
390
+ // For genesis tips (block number 0) report the dynamic initial header hash so consumers
391
+ // running L2BlockStream against this mock agree at block 0 with their local tip store.
392
+ const genesisHash = (await this.initialHeader.hash()).toString();
393
+ const tipHash = async (block: L2Block | undefined, number: number): Promise<string> => {
394
+ if (block) {
395
+ return (await block.hash()).toString();
396
+ }
397
+ return number === 0 ? genesisHash : '';
398
+ };
422
399
 
423
400
  const latestBlockId = {
424
401
  number: BlockNumber(latest),
425
- hash: (await latestBlock?.hash())?.toString(),
402
+ hash: await tipHash(latestBlock, latest),
426
403
  };
427
404
  const provenBlockId = {
428
405
  number: BlockNumber(proven),
429
- hash: (await provenBlock?.hash())?.toString(),
406
+ hash: await tipHash(provenBlock, proven),
430
407
  };
431
408
  const finalizedBlockId = {
432
409
  number: BlockNumber(finalized),
433
- hash: (await finalizedBlock?.hash())?.toString(),
410
+ hash: await tipHash(finalizedBlock, finalized),
434
411
  };
435
412
  const checkpointedBlockId = {
436
413
  number: BlockNumber(checkpointed),
437
- hash: (await checkpointedBlock?.hash())?.toString(),
414
+ hash: await tipHash(checkpointedBlock, checkpointed),
415
+ };
416
+ const proposedCheckpointBlockId = {
417
+ number: BlockNumber(proposedCheckpoint),
418
+ hash: await tipHash(proposedCheckpointBlock, proposedCheckpoint),
438
419
  };
439
420
 
440
- const makeTipId = (blockId: typeof latestBlockId) => ({
441
- block: blockId,
442
- checkpoint: {
443
- number: this.findCheckpointNumberForBlock(blockId.number) ?? CheckpointNumber(0),
444
- hash: blockId.hash,
445
- },
446
- });
421
+ const makeTipId = (blockId: typeof latestBlockId) => {
422
+ const checkpointNumber = this.findCheckpointNumberForBlock(blockId.number) ?? CheckpointNumber(0);
423
+ // Match production semantics: checkpoint 0 is fully synthetic (no real checkpoint header
424
+ // exists at 0), so its hash stays at the protocol constant `GENESIS_CHECKPOINT_HEADER_HASH`
425
+ // even though the block-0 hash is dynamic. See L2TipsCache for the production path.
426
+ const hash = checkpointNumber === 0 ? GENESIS_CHECKPOINT_HEADER_HASH.toString() : blockId.hash;
427
+ return {
428
+ block: blockId,
429
+ checkpoint: { number: checkpointNumber, hash },
430
+ };
431
+ };
447
432
 
448
433
  return {
449
434
  proposed: latestBlockId,
450
435
  checkpointed: makeTipId(checkpointedBlockId),
451
436
  proven: makeTipId(provenBlockId),
452
437
  finalized: makeTipId(finalizedBlockId),
438
+ proposedCheckpoint: makeTipId(proposedCheckpointBlockId),
453
439
  };
454
440
  }
455
441
 
@@ -469,8 +455,12 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
469
455
  return Promise.resolve(EmptyL1RollupConstants);
470
456
  }
471
457
 
458
+ isPruneDueAtSlot(_slot: SlotNumber): Promise<boolean> {
459
+ return Promise.resolve(false);
460
+ }
461
+
472
462
  getGenesisValues(): Promise<{ genesisArchiveRoot: Fr }> {
473
- return Promise.resolve({ genesisArchiveRoot: new Fr(GENESIS_ARCHIVE_ROOT) });
463
+ return Promise.resolve({ genesisArchiveRoot: this.genesisArchiveRoot ?? new Fr(GENESIS_ARCHIVE_ROOT) });
474
464
  }
475
465
 
476
466
  getL1Timestamp(): Promise<bigint> {
@@ -523,6 +513,95 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
523
513
  return Promise.resolve();
524
514
  }
525
515
 
516
+ async getBlock(query: BlockQuery): Promise<L2Block | undefined> {
517
+ if ('number' in query) {
518
+ if (query.number === 0) {
519
+ return this.getGenesisBlock();
520
+ }
521
+ return this.l2Blocks[query.number - 1];
522
+ }
523
+ if ('hash' in query) {
524
+ const genesis = this.getGenesisBlock();
525
+ if ((await genesis.hash()).equals(query.hash)) {
526
+ return genesis;
527
+ }
528
+ for (const b of this.l2Blocks) {
529
+ const hash = await b.hash();
530
+ if (hash.equals(query.hash)) {
531
+ return b;
532
+ }
533
+ }
534
+ return undefined;
535
+ }
536
+ if ('archive' in query) {
537
+ const genesis = this.getGenesisBlock();
538
+ if (genesis.archive.root.equals(query.archive)) {
539
+ return genesis;
540
+ }
541
+ return this.l2Blocks.find(b => b.archive.root.equals(query.archive));
542
+ }
543
+ const number = this.resolveBlockTag(query.tag);
544
+ if (number === 0) {
545
+ return this.getGenesisBlock();
546
+ }
547
+ return this.l2Blocks[number - 1];
548
+ }
549
+
550
+ private resolveBlockTag(tag: BlockTag): number {
551
+ switch (tag) {
552
+ case 'latest':
553
+ case 'proposed':
554
+ return this.l2Blocks.length;
555
+ case 'checkpointed':
556
+ return this.checkpointedBlockNumber;
557
+ case 'proven':
558
+ return this.provenBlockNumber;
559
+ case 'finalized':
560
+ return this.finalizedBlockNumber;
561
+ }
562
+ }
563
+
564
+ getBlocks(query: BlocksQuery): Promise<L2Block[]> {
565
+ let blocks: L2Block[];
566
+ if ('from' in query) {
567
+ blocks = this.l2Blocks.slice(query.from - 1, query.from - 1 + query.limit);
568
+ } else {
569
+ const epochCheckpoints = this.getCheckpointsInEpoch(query.epoch);
570
+ blocks = epochCheckpoints.flatMap(c => c.blocks);
571
+ }
572
+ if (query.onlyCheckpointed) {
573
+ blocks = blocks.filter(b => b.header.globalVariables.blockNumber <= this.checkpointedBlockNumber);
574
+ }
575
+ return Promise.resolve(blocks);
576
+ }
577
+
578
+ async getBlockData(query: BlockQuery): Promise<BlockData | undefined> {
579
+ const block = await this.getBlock(query);
580
+ if (!block) {
581
+ return undefined;
582
+ }
583
+ return {
584
+ header: block.header,
585
+ archive: block.archive,
586
+ blockHash: await block.hash(),
587
+ checkpointNumber: block.checkpointNumber,
588
+ indexWithinCheckpoint: block.indexWithinCheckpoint,
589
+ };
590
+ }
591
+
592
+ async getBlocksData(query: BlocksQuery): Promise<BlockData[]> {
593
+ const blocks = await this.getBlocks(query);
594
+ return Promise.all(
595
+ blocks.map(async block => ({
596
+ header: block.header,
597
+ archive: block.archive,
598
+ blockHash: await block.hash(),
599
+ checkpointNumber: block.checkpointNumber,
600
+ indexWithinCheckpoint: block.indexWithinCheckpoint,
601
+ })),
602
+ );
603
+ }
604
+
526
605
  isPendingChainInvalid(): Promise<boolean> {
527
606
  return Promise.resolve(false);
528
607
  }
@@ -531,6 +610,10 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
531
610
  return Promise.resolve({ valid: true });
532
611
  }
533
612
 
613
+ getProposedCheckpointData(_query?: ProposedCheckpointQuery): Promise<ProposedCheckpointData | undefined> {
614
+ return Promise.resolve(undefined);
615
+ }
616
+
534
617
  /** Returns checkpoints whose slot falls within the given epoch. */
535
618
  private getCheckpointsInEpoch(epochNumber: EpochNumber): Checkpoint[] {
536
619
  const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
@@ -543,22 +626,6 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
543
626
  return new L1PublishedData(BigInt(checkpoint.number), BigInt(checkpoint.number), Buffer32.random().toString());
544
627
  }
545
628
 
546
- /** Creates a CheckpointedL2Block from a block using stored checkpoint info. */
547
- private toCheckpointedBlock(block: L2Block): CheckpointedL2Block {
548
- const checkpoint = this.checkpointList.find(c => c.blocks.some(b => b.number === block.number));
549
- const checkpointNumber = checkpoint?.number ?? block.checkpointNumber;
550
- return new CheckpointedL2Block(
551
- checkpointNumber,
552
- block,
553
- new L1PublishedData(
554
- BigInt(block.number),
555
- BigInt(block.number),
556
- `0x${block.number.toString(16).padStart(64, '0')}`,
557
- ),
558
- [],
559
- );
560
- }
561
-
562
629
  /** Finds the checkpoint number for a block, or undefined if the block is not in any checkpoint. */
563
630
  private findCheckpointNumberForBlock(blockNumber: BlockNumber): CheckpointNumber | undefined {
564
631
  const checkpoint = this.checkpointList.find(c => c.blocks.some(b => b.number === blockNumber));