@aztec/archiver 0.0.1-commit.96dac018d → 0.0.1-commit.993d240

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