@aztec/archiver 0.0.1-commit.181e2d196 → 0.0.1-commit.189eedb3

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 (125) hide show
  1. package/README.md +19 -11
  2. package/dest/archiver.d.ts +55 -18
  3. package/dest/archiver.d.ts.map +1 -1
  4. package/dest/archiver.js +276 -79
  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 +55 -9
  9. package/dest/errors.d.ts.map +1 -1
  10. package/dest/errors.js +81 -14
  11. package/dest/factory.d.ts +14 -9
  12. package/dest/factory.d.ts.map +1 -1
  13. package/dest/factory.js +51 -37
  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 +73 -49
  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 +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/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 +174 -76
  50. package/dest/store/block_store.d.ts.map +1 -1
  51. package/dest/store/block_store.js +692 -277
  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 +51 -9
  76. package/dest/test/fake_l1_state.d.ts +20 -1
  77. package/dest/test/fake_l1_state.d.ts.map +1 -1
  78. package/dest/test/fake_l1_state.js +114 -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 +54 -52
  83. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  84. package/dest/test/mock_l2_block_source.js +248 -182
  85. package/dest/test/mock_structs.d.ts +4 -1
  86. package/dest/test/mock_structs.d.ts.map +1 -1
  87. package/dest/test/mock_structs.js +13 -1
  88. package/dest/test/noop_l1_archiver.d.ts +13 -6
  89. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  90. package/dest/test/noop_l1_archiver.js +27 -10
  91. package/package.json +14 -14
  92. package/src/archiver.ts +347 -88
  93. package/src/config.ts +24 -11
  94. package/src/errors.ts +122 -21
  95. package/src/factory.ts +58 -31
  96. package/src/index.ts +18 -2
  97. package/src/l1/calldata_retriever.ts +16 -5
  98. package/src/l1/data_retrieval.ts +36 -45
  99. package/src/l1/validate_historical_logs.ts +140 -0
  100. package/src/modules/contract_data_source_adapter.ts +55 -0
  101. package/src/modules/data_source_base.ts +343 -176
  102. package/src/modules/data_store_updater.ts +224 -154
  103. package/src/modules/instrumentation.ts +28 -8
  104. package/src/modules/l1_synchronizer.ts +572 -248
  105. package/src/modules/outbox_trees_resolver.ts +199 -0
  106. package/src/modules/validation.ts +10 -9
  107. package/src/store/block_store.ts +858 -333
  108. package/src/store/contract_class_store.ts +31 -103
  109. package/src/store/contract_instance_store.ts +51 -5
  110. package/src/store/data_stores.ts +104 -0
  111. package/src/store/function_names_cache.ts +37 -0
  112. package/src/store/l2_tips_cache.ts +16 -70
  113. package/src/store/log_store.ts +301 -559
  114. package/src/store/log_store_codec.ts +132 -0
  115. package/src/store/message_store.ts +60 -10
  116. package/src/structs/inbox_message.ts +1 -1
  117. package/src/test/fake_l1_state.ts +142 -29
  118. package/src/test/mock_l1_to_l2_message_source.ts +1 -0
  119. package/src/test/mock_l2_block_source.ts +306 -227
  120. package/src/test/mock_structs.ts +20 -6
  121. package/src/test/noop_l1_archiver.ts +49 -10
  122. package/dest/store/kv_archiver_store.d.ts +0 -354
  123. package/dest/store/kv_archiver_store.d.ts.map +0 -1
  124. package/dest/store/kv_archiver_store.js +0 -464
  125. 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,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';
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
41
  import { EmptyL1RollupConstants, type L1RollupConstants, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
22
42
  import { computeCheckpointOutHash } from '@aztec/stdlib/messaging';
23
43
  import { CheckpointHeader } from '@aztec/stdlib/rollup';
24
- 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';
25
46
  import type { UInt64 } from '@aztec/stdlib/types';
26
47
 
27
48
  /**
@@ -34,14 +55,79 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
34
55
  private provenBlockNumber: number = 0;
35
56
  private finalizedBlockNumber: number = 0;
36
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;
37
64
 
38
65
  private log = createLogger('archiver:mock_l2_block_source');
39
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
+
40
120
  /** Creates blocks grouped into single-block checkpoints. */
41
121
  public async createBlocks(numBlocks: number) {
42
122
  await this.createCheckpoints(numBlocks, 1);
43
123
  }
44
124
 
125
+ public getCheckpointNumber(): Promise<CheckpointNumber> {
126
+ return Promise.resolve(
127
+ this.checkpointList.length === 0 ? CheckpointNumber.ZERO : CheckpointNumber(this.checkpointList.length),
128
+ );
129
+ }
130
+
45
131
  /** Creates checkpoints, each containing `blocksPerCheckpoint` blocks. */
46
132
  public async createCheckpoints(numCheckpoints: number, blocksPerCheckpoint: number = 1) {
47
133
  for (let c = 0; c < numCheckpoints; c++) {
@@ -78,6 +164,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
78
164
  });
79
165
  // Keep tip numbers consistent with remaining blocks.
80
166
  this.checkpointedBlockNumber = Math.min(this.checkpointedBlockNumber, maxBlockNum);
167
+ this.proposedCheckpointBlockNumber = Math.min(this.proposedCheckpointBlockNumber, maxBlockNum);
81
168
  this.provenBlockNumber = Math.min(this.provenBlockNumber, maxBlockNum);
82
169
  this.finalizedBlockNumber = Math.min(this.finalizedBlockNumber, maxBlockNum);
83
170
  this.log.verbose(`Removed ${numBlocks} blocks from the mock L2 block source`);
@@ -94,9 +181,17 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
94
181
  this.finalizedBlockNumber = finalizedBlockNumber;
95
182
  }
96
183
 
184
+ public setProposedCheckpointBlockNumber(blockNumber: number) {
185
+ this.proposedCheckpointBlockNumber = blockNumber;
186
+ }
187
+
97
188
  public setCheckpointedBlockNumber(checkpointedBlockNumber: number) {
98
189
  const prevCheckpointed = this.checkpointedBlockNumber;
99
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
+ }
100
195
  // Auto-create single-block checkpoints for newly checkpointed blocks that don't have one yet.
101
196
  // This handles blocks added via addProposedBlocks that are now being marked as checkpointed.
102
197
  const newCheckpoints: Checkpoint[] = [];
@@ -144,80 +239,36 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
144
239
  * Gets the number of the latest L2 block processed by the block source implementation.
145
240
  * @returns In this mock instance, returns the number of L2 blocks that we've mocked.
146
241
  */
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);
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);
166
247
  }
167
- const block = this.l2Blocks[number - 1];
168
- if (!block) {
169
- return Promise.resolve(undefined);
248
+ if ('number' in query) {
249
+ return query.number;
170
250
  }
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
- }
251
+ if ('tag' in query) {
252
+ return BlockNumber(this.resolveBlockTag(query.tag));
185
253
  }
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);
254
+ const block = await this.getBlock(query);
255
+ return block ? block.header.globalVariables.blockNumber : undefined;
197
256
  }
198
257
 
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);
258
+ public getProposedCheckpointL2BlockNumber() {
259
+ return Promise.resolve(BlockNumber(this.proposedCheckpointBlockNumber));
207
260
  }
208
261
 
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));
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), []));
217
268
  }
218
269
 
219
- public getCheckpoints(from: CheckpointNumber, limit: number) {
220
- const checkpoints = this.checkpointList.slice(from - 1, from - 1 + limit);
270
+ public getCheckpoints(query: CheckpointsQuery): Promise<PublishedCheckpoint[]> {
271
+ const checkpoints = this.resolveCheckpointsQuery(query);
221
272
  return Promise.resolve(
222
273
  checkpoints.map(checkpoint => new PublishedCheckpoint(checkpoint, this.mockL1DataForCheckpoint(checkpoint), [])),
223
274
  );
@@ -228,115 +279,65 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
228
279
  return Promise.resolve(checkpoint);
229
280
  }
230
281
 
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) {
282
+ public getCheckpointData(query: CheckpointQuery): Promise<CheckpointData | undefined> {
283
+ const checkpoint = this.resolveCheckpointQuery(query);
284
+ if (!checkpoint) {
244
285
  return Promise.resolve(undefined);
245
286
  }
246
- return Promise.resolve(this.toCheckpointedBlock(block));
247
- }
248
-
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;
287
+ return Promise.resolve(this.checkpointToData(checkpoint));
272
288
  }
273
289
 
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);
290
+ public getCheckpointsData(query: CheckpointsQuery): Promise<CheckpointData[]> {
291
+ const checkpoints = this.resolveCheckpointsQuery(query);
292
+ return Promise.resolve(checkpoints.map(c => this.checkpointToData(c)));
277
293
  }
278
294
 
279
- public async getBlockData(number: BlockNumber): Promise<BlockData | undefined> {
280
- const block = this.l2Blocks[number - 1];
281
- if (!block) {
282
- return undefined;
283
- }
295
+ private checkpointToData(checkpoint: Checkpoint): CheckpointData {
284
296
  return {
285
- header: block.header,
286
- archive: block.archive,
287
- blockHash: await block.hash(),
288
- checkpointNumber: block.checkpointNumber,
289
- 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),
290
308
  };
291
309
  }
292
310
 
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;
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
+ }
297
333
  }
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
334
  }
334
335
 
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
- );
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);
340
341
  }
341
342
 
342
343
  getBlocksForSlot(slotNumber: SlotNumber): Promise<L2Block[]> {
@@ -344,11 +345,6 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
344
345
  return Promise.resolve(blocks);
345
346
  }
346
347
 
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
348
  /**
353
349
  * Gets a tx effect.
354
350
  * @param txHash - The hash of the tx corresponding to the tx effect.
@@ -366,86 +362,88 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
366
362
  data: txEffect,
367
363
  l2BlockNumber: block.number,
368
364
  l2BlockHash: await block.hash(),
369
- txIndexInBlock: block.body.txEffects.indexOf(txEffect),
365
+ txIndexInBlock: block.body.txEffects.findIndex(t => t.txHash.equals(txHash)),
366
+ slotNumber: block.header.globalVariables.slotNumber,
370
367
  };
371
368
  }
372
369
 
373
- /**
374
- * Gets a receipt of a settled tx.
375
- * @param txHash - The hash of a tx we try to get the receipt for.
376
- * @returns The requested tx receipt (or undefined if not found).
377
- */
378
- public async getSettledTxReceipt(txHash: TxHash): Promise<TxReceipt | undefined> {
379
- for (const block of this.l2Blocks) {
380
- for (const txEffect of block.body.txEffects) {
381
- if (txEffect.txHash.equals(txHash)) {
382
- // In mock, assume all txs are checkpointed with successful execution
383
- return new TxReceipt(
384
- txHash,
385
- TxStatus.CHECKPOINTED,
386
- TxExecutionResult.SUCCESS,
387
- undefined,
388
- txEffect.transactionFee.toBigInt(),
389
- await block.hash(),
390
- block.number,
391
- );
392
- }
393
- }
394
- }
395
- return undefined;
370
+ public getL2ToL1MembershipWitness(): Promise<undefined> {
371
+ // Mock does not back the L2-to-L1 message witness flow.
372
+ return Promise.resolve(undefined);
396
373
  }
397
374
 
398
375
  async getL2Tips(): Promise<L2Tips> {
399
- const [latest, proven, finalized, checkpointed] = [
376
+ const [latest, proven, finalized, checkpointed, proposedCheckpoint] = [
400
377
  await this.getBlockNumber(),
401
- await this.getProvenBlockNumber(),
378
+ this.provenBlockNumber,
402
379
  this.finalizedBlockNumber,
403
380
  this.checkpointedBlockNumber,
381
+ await this.getProposedCheckpointL2BlockNumber(),
404
382
  ] as const;
405
383
 
406
384
  const latestBlock = this.l2Blocks[latest - 1];
407
385
  const provenBlock = this.l2Blocks[proven - 1];
408
386
  const finalizedBlock = this.l2Blocks[finalized - 1];
409
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
+ };
410
399
 
411
400
  const latestBlockId = {
412
401
  number: BlockNumber(latest),
413
- hash: (await latestBlock?.hash())?.toString(),
402
+ hash: await tipHash(latestBlock, latest),
414
403
  };
415
404
  const provenBlockId = {
416
405
  number: BlockNumber(proven),
417
- hash: (await provenBlock?.hash())?.toString(),
406
+ hash: await tipHash(provenBlock, proven),
418
407
  };
419
408
  const finalizedBlockId = {
420
409
  number: BlockNumber(finalized),
421
- hash: (await finalizedBlock?.hash())?.toString(),
410
+ hash: await tipHash(finalizedBlock, finalized),
422
411
  };
423
412
  const checkpointedBlockId = {
424
413
  number: BlockNumber(checkpointed),
425
- 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),
426
419
  };
427
420
 
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
- });
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
+ };
435
432
 
436
433
  return {
437
434
  proposed: latestBlockId,
438
435
  checkpointed: makeTipId(checkpointedBlockId),
439
436
  proven: makeTipId(provenBlockId),
440
437
  finalized: makeTipId(finalizedBlockId),
438
+ proposedCheckpoint: makeTipId(proposedCheckpointBlockId),
441
439
  };
442
440
  }
443
441
 
444
- getL2EpochNumber(): Promise<EpochNumber> {
442
+ getSyncedL2EpochNumber(): Promise<EpochNumber> {
445
443
  throw new Error('Method not implemented.');
446
444
  }
447
445
 
448
- getL2SlotNumber(): Promise<SlotNumber> {
446
+ getSyncedL2SlotNumber(): Promise<SlotNumber> {
449
447
  throw new Error('Method not implemented.');
450
448
  }
451
449
 
@@ -457,8 +455,12 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
457
455
  return Promise.resolve(EmptyL1RollupConstants);
458
456
  }
459
457
 
458
+ isPruneDueAtSlot(_slot: SlotNumber): Promise<boolean> {
459
+ return Promise.resolve(false);
460
+ }
461
+
460
462
  getGenesisValues(): Promise<{ genesisArchiveRoot: Fr }> {
461
- return Promise.resolve({ genesisArchiveRoot: new Fr(GENESIS_ARCHIVE_ROOT) });
463
+ return Promise.resolve({ genesisArchiveRoot: this.genesisArchiveRoot ?? new Fr(GENESIS_ARCHIVE_ROOT) });
462
464
  }
463
465
 
464
466
  getL1Timestamp(): Promise<bigint> {
@@ -511,6 +513,95 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
511
513
  return Promise.resolve();
512
514
  }
513
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
+
514
605
  isPendingChainInvalid(): Promise<boolean> {
515
606
  return Promise.resolve(false);
516
607
  }
@@ -519,6 +610,10 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
519
610
  return Promise.resolve({ valid: true });
520
611
  }
521
612
 
613
+ getProposedCheckpointData(_query?: ProposedCheckpointQuery): Promise<ProposedCheckpointData | undefined> {
614
+ return Promise.resolve(undefined);
615
+ }
616
+
522
617
  /** Returns checkpoints whose slot falls within the given epoch. */
523
618
  private getCheckpointsInEpoch(epochNumber: EpochNumber): Checkpoint[] {
524
619
  const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
@@ -531,22 +626,6 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource {
531
626
  return new L1PublishedData(BigInt(checkpoint.number), BigInt(checkpoint.number), Buffer32.random().toString());
532
627
  }
533
628
 
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
629
  /** Finds the checkpoint number for a block, or undefined if the block is not in any checkpoint. */
551
630
  private findCheckpointNumberForBlock(blockNumber: BlockNumber): CheckpointNumber | undefined {
552
631
  const checkpoint = this.checkpointList.find(c => c.blocks.some(b => b.number === blockNumber));