@aztec/archiver 0.0.1-commit.f2ce05ee → 0.0.1-commit.f5a9928

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 (132) hide show
  1. package/README.md +19 -13
  2. package/dest/archiver.d.ts +76 -18
  3. package/dest/archiver.d.ts.map +1 -1
  4. package/dest/archiver.js +329 -162
  5. package/dest/config.d.ts +7 -3
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +43 -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 +20 -9
  12. package/dest/factory.d.ts.map +1 -1
  13. package/dest/factory.js +87 -39
  14. package/dest/index.d.ts +12 -3
  15. package/dest/index.d.ts.map +1 -1
  16. package/dest/index.js +11 -2
  17. package/dest/l1/bin/retrieve-calldata.js +32 -28
  18. package/dest/l1/calldata_retriever.d.ts +81 -50
  19. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  20. package/dest/l1/calldata_retriever.js +202 -260
  21. package/dest/l1/data_retrieval.d.ts +32 -18
  22. package/dest/l1/data_retrieval.d.ts.map +1 -1
  23. package/dest/l1/data_retrieval.js +42 -47
  24. package/dest/l1/spire_proposer.d.ts +5 -5
  25. package/dest/l1/spire_proposer.d.ts.map +1 -1
  26. package/dest/l1/spire_proposer.js +9 -17
  27. package/dest/l1/trace_tx.d.ts +12 -66
  28. package/dest/l1/trace_tx.d.ts.map +1 -1
  29. package/dest/l1/validate_historical_logs.d.ts +23 -0
  30. package/dest/l1/validate_historical_logs.d.ts.map +1 -0
  31. package/dest/l1/validate_historical_logs.js +108 -0
  32. package/dest/modules/contract_data_source_adapter.d.ts +25 -0
  33. package/dest/modules/contract_data_source_adapter.d.ts.map +1 -0
  34. package/dest/modules/contract_data_source_adapter.js +32 -0
  35. package/dest/modules/data_source_base.d.ts +74 -46
  36. package/dest/modules/data_source_base.d.ts.map +1 -1
  37. package/dest/modules/data_source_base.js +252 -190
  38. package/dest/modules/data_store_updater.d.ts +49 -17
  39. package/dest/modules/data_store_updater.d.ts.map +1 -1
  40. package/dest/modules/data_store_updater.js +218 -122
  41. package/dest/modules/instrumentation.d.ts +29 -3
  42. package/dest/modules/instrumentation.d.ts.map +1 -1
  43. package/dest/modules/instrumentation.js +67 -11
  44. package/dest/modules/l1_synchronizer.d.ts +16 -14
  45. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  46. package/dest/modules/l1_synchronizer.js +488 -219
  47. package/dest/modules/outbox_trees_resolver.d.ts +64 -0
  48. package/dest/modules/outbox_trees_resolver.d.ts.map +1 -0
  49. package/dest/modules/outbox_trees_resolver.js +184 -0
  50. package/dest/modules/validation.d.ts +30 -7
  51. package/dest/modules/validation.d.ts.map +1 -1
  52. package/dest/modules/validation.js +35 -17
  53. package/dest/store/block_store.d.ts +186 -74
  54. package/dest/store/block_store.d.ts.map +1 -1
  55. package/dest/store/block_store.js +810 -285
  56. package/dest/store/contract_class_store.d.ts +17 -4
  57. package/dest/store/contract_class_store.d.ts.map +1 -1
  58. package/dest/store/contract_class_store.js +42 -68
  59. package/dest/store/contract_instance_store.d.ts +28 -1
  60. package/dest/store/contract_instance_store.d.ts.map +1 -1
  61. package/dest/store/contract_instance_store.js +58 -6
  62. package/dest/store/data_stores.d.ts +68 -0
  63. package/dest/store/data_stores.d.ts.map +1 -0
  64. package/dest/store/data_stores.js +54 -0
  65. package/dest/store/function_names_cache.d.ts +17 -0
  66. package/dest/store/function_names_cache.d.ts.map +1 -0
  67. package/dest/store/function_names_cache.js +30 -0
  68. package/dest/store/l2_tips_cache.d.ts +25 -0
  69. package/dest/store/l2_tips_cache.d.ts.map +1 -0
  70. package/dest/store/l2_tips_cache.js +26 -0
  71. package/dest/store/log_store.d.ts +42 -37
  72. package/dest/store/log_store.d.ts.map +1 -1
  73. package/dest/store/log_store.js +262 -408
  74. package/dest/store/log_store_codec.d.ts +78 -0
  75. package/dest/store/log_store_codec.d.ts.map +1 -0
  76. package/dest/store/log_store_codec.js +110 -0
  77. package/dest/store/message_store.d.ts +11 -1
  78. package/dest/store/message_store.d.ts.map +1 -1
  79. package/dest/store/message_store.js +51 -9
  80. package/dest/test/fake_l1_state.d.ts +25 -1
  81. package/dest/test/fake_l1_state.d.ts.map +1 -1
  82. package/dest/test/fake_l1_state.js +166 -32
  83. package/dest/test/mock_archiver.d.ts +1 -1
  84. package/dest/test/mock_archiver.d.ts.map +1 -1
  85. package/dest/test/mock_archiver.js +3 -2
  86. package/dest/test/mock_l2_block_source.d.ts +61 -47
  87. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  88. package/dest/test/mock_l2_block_source.js +306 -211
  89. package/dest/test/mock_structs.d.ts +4 -1
  90. package/dest/test/mock_structs.d.ts.map +1 -1
  91. package/dest/test/mock_structs.js +15 -3
  92. package/dest/test/noop_l1_archiver.d.ts +17 -6
  93. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  94. package/dest/test/noop_l1_archiver.js +31 -11
  95. package/package.json +15 -14
  96. package/src/archiver.ts +427 -185
  97. package/src/config.ts +51 -13
  98. package/src/errors.ts +122 -21
  99. package/src/factory.ts +104 -29
  100. package/src/index.ts +19 -2
  101. package/src/l1/README.md +25 -68
  102. package/src/l1/bin/retrieve-calldata.ts +40 -27
  103. package/src/l1/calldata_retriever.ts +267 -379
  104. package/src/l1/data_retrieval.ts +66 -70
  105. package/src/l1/spire_proposer.ts +7 -15
  106. package/src/l1/validate_historical_logs.ts +140 -0
  107. package/src/modules/contract_data_source_adapter.ts +46 -0
  108. package/src/modules/data_source_base.ts +337 -238
  109. package/src/modules/data_store_updater.ts +259 -154
  110. package/src/modules/instrumentation.ts +71 -11
  111. package/src/modules/l1_synchronizer.ts +659 -269
  112. package/src/modules/outbox_trees_resolver.ts +220 -0
  113. package/src/modules/validation.ts +80 -23
  114. package/src/store/block_store.ts +989 -358
  115. package/src/store/contract_class_store.ts +49 -103
  116. package/src/store/contract_instance_store.ts +74 -11
  117. package/src/store/data_stores.ts +104 -0
  118. package/src/store/function_names_cache.ts +37 -0
  119. package/src/store/l2_tips_cache.ts +35 -0
  120. package/src/store/log_store.ts +302 -559
  121. package/src/store/log_store_codec.ts +143 -0
  122. package/src/store/message_store.ts +60 -10
  123. package/src/structs/inbox_message.ts +1 -1
  124. package/src/test/fake_l1_state.ts +213 -42
  125. package/src/test/mock_archiver.ts +3 -2
  126. package/src/test/mock_l2_block_source.ts +378 -233
  127. package/src/test/mock_structs.ts +25 -8
  128. package/src/test/noop_l1_archiver.ts +61 -12
  129. package/dest/store/kv_archiver_store.d.ts +0 -340
  130. package/dest/store/kv_archiver_store.d.ts.map +0 -1
  131. package/dest/store/kv_archiver_store.js +0 -446
  132. package/src/store/kv_archiver_store.ts +0 -639
@@ -1,31 +1,87 @@
1
1
  import { GENESIS_ARCHIVE_ROOT } from '@aztec/constants';
2
2
  import { DefaultL1ContractsConfig } from '@aztec/ethereum/config';
3
- import { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
3
+ import { BlockNumber, CheckpointNumber, IndexWithinCheckpoint, SlotNumber } from '@aztec/foundation/branded-types';
4
4
  import { Buffer32 } from '@aztec/foundation/buffer';
5
5
  import { Fr } from '@aztec/foundation/curves/bn254';
6
6
  import { EthAddress } from '@aztec/foundation/eth-address';
7
7
  import { createLogger } from '@aztec/foundation/log';
8
- import { CheckpointedL2Block, L2Block } from '@aztec/stdlib/block';
8
+ import { Body, GENESIS_BLOCK_HEADER_HASH, GENESIS_CHECKPOINT_HEADER_HASH, L2Block } from '@aztec/stdlib/block';
9
9
  import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
10
10
  import { EmptyL1RollupConstants, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
11
- import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
11
+ import { computeCheckpointOutHash } from '@aztec/stdlib/messaging';
12
+ import { CheckpointHeader } from '@aztec/stdlib/rollup';
13
+ import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
14
+ import { BlockHeader } from '@aztec/stdlib/tx';
12
15
  /**
13
16
  * A mocked implementation of L2BlockSource to be used in tests.
14
17
  */ export class MockL2BlockSource {
15
18
  l2Blocks = [];
19
+ checkpointList = [];
16
20
  provenBlockNumber = 0;
17
21
  finalizedBlockNumber = 0;
18
22
  checkpointedBlockNumber = 0;
23
+ initialHeader = BlockHeader.empty();
24
+ initialHeaderHash = GENESIS_BLOCK_HEADER_HASH;
25
+ genesisArchiveRoot;
26
+ genesisBlock;
19
27
  log = createLogger('archiver:mock_l2_block_source');
20
- async createBlocks(numBlocks) {
21
- for(let i = 0; i < numBlocks; i++){
22
- const blockNum = this.l2Blocks.length + 1;
23
- const block = await L2Block.random(BlockNumber(blockNum), {
24
- slotNumber: SlotNumber(blockNum)
28
+ /** Returns the initial header used to synthesize block 0. */ getInitialHeader() {
29
+ return this.initialHeader;
30
+ }
31
+ /**
32
+ * Sets the initial header used to synthesize block 0. Tests that wire up a real
33
+ * world-state should call this with `worldState.getInitialHeader()` so the L2BlockStream
34
+ * agrees on the genesis hash on both sides. Precomputes and caches the header hash so
35
+ * `getGenesisBlockHash()` can return synchronously.
36
+ */ async setInitialHeader(header) {
37
+ this.initialHeader = header;
38
+ this.initialHeaderHash = await header.hash();
39
+ this.genesisBlock = undefined;
40
+ }
41
+ /**
42
+ * Returns the precomputed hash of the genesis block header. Defaults to the static
43
+ * {@link GENESIS_BLOCK_HEADER_HASH} unless {@link setInitialHeader} has been called with a
44
+ * custom header.
45
+ */ getGenesisBlockHash() {
46
+ return this.initialHeaderHash;
47
+ }
48
+ /**
49
+ * Sets the post-genesis archive root used to synthesize block 0. Mirrors the real archiver,
50
+ * whose synthetic block 0 carries `new AppendOnlyTreeSnapshot(genesisArchiveRoot, 1)` rather
51
+ * than `AppendOnlyTreeSnapshot.empty()`. Tests wiring up a real world-state should set this so
52
+ * archive-based block lookups against the mock match production semantics.
53
+ */ setGenesisArchiveRoot(root) {
54
+ this.genesisArchiveRoot = root;
55
+ this.genesisBlock = undefined;
56
+ }
57
+ getGenesisBlock() {
58
+ if (this.genesisBlock) {
59
+ return this.genesisBlock;
60
+ }
61
+ const archive = this.genesisArchiveRoot ? new AppendOnlyTreeSnapshot(this.genesisArchiveRoot, 1) : AppendOnlyTreeSnapshot.empty();
62
+ return this.genesisBlock = new L2Block(archive, this.initialHeader, Body.empty(), CheckpointNumber.ZERO, IndexWithinCheckpoint(0));
63
+ }
64
+ /** Creates blocks grouped into single-block checkpoints. */ async createBlocks(numBlocks) {
65
+ await this.createCheckpoints(numBlocks, 1);
66
+ }
67
+ getCheckpointNumber() {
68
+ return Promise.resolve(this.checkpointList.length === 0 ? CheckpointNumber.ZERO : CheckpointNumber(this.checkpointList.length));
69
+ }
70
+ /** Creates checkpoints, each containing `blocksPerCheckpoint` blocks. */ async createCheckpoints(numCheckpoints, blocksPerCheckpoint = 1) {
71
+ for(let c = 0; c < numCheckpoints; c++){
72
+ const checkpointNum = CheckpointNumber(this.checkpointList.length + 1);
73
+ const startBlockNum = this.l2Blocks.length + 1;
74
+ const slotNumber = SlotNumber(Number(checkpointNum));
75
+ const checkpoint = await Checkpoint.random(checkpointNum, {
76
+ numBlocks: blocksPerCheckpoint,
77
+ startBlockNumber: startBlockNum,
78
+ slotNumber,
79
+ checkpointNumber: checkpointNum
25
80
  });
26
- this.l2Blocks.push(block);
81
+ this.checkpointList.push(checkpoint);
82
+ this.l2Blocks.push(...checkpoint.blocks);
27
83
  }
28
- this.log.verbose(`Created ${numBlocks} blocks in the mock L2 block source`);
84
+ this.log.verbose(`Created ${numCheckpoints} checkpoints with ${blocksPerCheckpoint} blocks each in the mock L2 block source`);
29
85
  }
30
86
  addProposedBlocks(blocks) {
31
87
  this.l2Blocks.push(...blocks);
@@ -33,6 +89,16 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
33
89
  }
34
90
  removeBlocks(numBlocks) {
35
91
  this.l2Blocks = this.l2Blocks.slice(0, -numBlocks);
92
+ const maxBlockNum = this.l2Blocks.length;
93
+ // Remove any checkpoint whose last block is beyond the remaining blocks.
94
+ this.checkpointList = this.checkpointList.filter((c)=>{
95
+ const lastBlockNum = c.blocks[0].number + c.blocks.length - 1;
96
+ return lastBlockNum <= maxBlockNum;
97
+ });
98
+ // Keep tip numbers consistent with remaining blocks.
99
+ this.checkpointedBlockNumber = Math.min(this.checkpointedBlockNumber, maxBlockNum);
100
+ this.provenBlockNumber = Math.min(this.provenBlockNumber, maxBlockNum);
101
+ this.finalizedBlockNumber = Math.min(this.finalizedBlockNumber, maxBlockNum);
36
102
  this.log.verbose(`Removed ${numBlocks} blocks from the mock L2 block source`);
37
103
  }
38
104
  setProvenBlockNumber(provenBlockNumber) {
@@ -45,7 +111,32 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
45
111
  this.finalizedBlockNumber = finalizedBlockNumber;
46
112
  }
47
113
  setCheckpointedBlockNumber(checkpointedBlockNumber) {
114
+ const prevCheckpointed = this.checkpointedBlockNumber;
48
115
  this.checkpointedBlockNumber = checkpointedBlockNumber;
116
+ // Auto-create single-block checkpoints for newly checkpointed blocks that don't have one yet.
117
+ // This handles blocks added via addProposedBlocks that are now being marked as checkpointed.
118
+ const newCheckpoints = [];
119
+ for(let blockNum = prevCheckpointed + 1; blockNum <= checkpointedBlockNumber; blockNum++){
120
+ const block = this.l2Blocks[blockNum - 1];
121
+ if (!block) {
122
+ continue;
123
+ }
124
+ if (this.checkpointList.some((c)=>c.blocks.some((b)=>b.number === block.number))) {
125
+ continue;
126
+ }
127
+ const checkpointNum = CheckpointNumber(this.checkpointList.length + newCheckpoints.length + 1);
128
+ const checkpoint = new Checkpoint(block.archive, CheckpointHeader.random({
129
+ slotNumber: block.header.globalVariables.slotNumber
130
+ }), [
131
+ block
132
+ ], checkpointNum);
133
+ newCheckpoints.push(checkpoint);
134
+ }
135
+ // Insert new checkpoints in order by number.
136
+ if (newCheckpoints.length > 0) {
137
+ this.checkpointList.push(...newCheckpoints);
138
+ this.checkpointList.sort((a, b)=>a.number - b.number);
139
+ }
49
140
  }
50
141
  /**
51
142
  * Method to fetch the rollup contract address at the base-layer.
@@ -59,199 +150,95 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
59
150
  */ getRegistryAddress() {
60
151
  return Promise.resolve(EthAddress.random());
61
152
  }
62
- /**
63
- * Gets the number of the latest L2 block processed by the block source implementation.
64
- * @returns In this mock instance, returns the number of L2 blocks that we've mocked.
65
- */ getBlockNumber() {
66
- return Promise.resolve(BlockNumber(this.l2Blocks.length));
153
+ async getBlockNumber(query) {
154
+ if (!query) {
155
+ return BlockNumber(this.l2Blocks.length);
156
+ }
157
+ if ('number' in query) {
158
+ return query.number;
159
+ }
160
+ if ('tag' in query) {
161
+ return BlockNumber(this.resolveBlockTag(query.tag));
162
+ }
163
+ const block = await this.getBlock(query);
164
+ return block ? block.header.globalVariables.blockNumber : undefined;
67
165
  }
68
- getProvenBlockNumber() {
69
- return Promise.resolve(BlockNumber(this.provenBlockNumber));
166
+ getCheckpoint(query) {
167
+ const checkpoint = this.resolveCheckpointQuery(query);
168
+ if (!checkpoint) {
169
+ return Promise.resolve(undefined);
170
+ }
171
+ return Promise.resolve(new PublishedCheckpoint(checkpoint, this.mockL1DataForCheckpoint(checkpoint), []));
70
172
  }
71
- getCheckpointedL2BlockNumber() {
72
- return Promise.resolve(BlockNumber(this.checkpointedBlockNumber));
173
+ getCheckpoints(query) {
174
+ const checkpoints = this.resolveCheckpointsQuery(query);
175
+ return Promise.resolve(checkpoints.map((checkpoint)=>new PublishedCheckpoint(checkpoint, this.mockL1DataForCheckpoint(checkpoint), [])));
73
176
  }
74
- getFinalizedL2BlockNumber() {
75
- return Promise.resolve(BlockNumber(this.finalizedBlockNumber));
177
+ getCheckpointByArchive(archive) {
178
+ const checkpoint = this.checkpointList.find((c)=>c.archive.root.equals(archive));
179
+ return Promise.resolve(checkpoint);
76
180
  }
77
- getCheckpointedBlock(number) {
78
- if (number > this.checkpointedBlockNumber) {
79
- return Promise.resolve(undefined);
80
- }
81
- const block = this.l2Blocks[number - 1];
82
- if (!block) {
181
+ getCheckpointData(query) {
182
+ const checkpoint = this.resolveCheckpointQuery(query);
183
+ if (!checkpoint) {
83
184
  return Promise.resolve(undefined);
84
185
  }
85
- const checkpointedBlock = new CheckpointedL2Block(CheckpointNumber.fromBlockNumber(number), block, new L1PublishedData(BigInt(number), BigInt(number), `0x${number.toString(16).padStart(64, '0')}`), []);
86
- return Promise.resolve(checkpointedBlock);
87
- }
88
- async getCheckpointedBlocks(from, limit) {
89
- const result = [];
90
- for(let i = 0; i < limit; i++){
91
- const blockNum = from + i;
92
- if (blockNum > this.checkpointedBlockNumber) {
93
- break;
94
- }
95
- const block = await this.getCheckpointedBlock(BlockNumber(blockNum));
96
- if (block) {
97
- result.push(block);
98
- }
99
- }
100
- return result;
101
- }
102
- /**
103
- * Gets an l2 block.
104
- * @param number - The block number to return (inclusive).
105
- * @returns The requested L2 block.
106
- */ getBlock(number) {
107
- const block = this.l2Blocks[number - 1];
108
- return Promise.resolve(block);
186
+ return Promise.resolve(this.checkpointToData(checkpoint));
109
187
  }
110
- /**
111
- * Gets an L2 block (new format).
112
- * @param number - The block number to return.
113
- * @returns The requested L2 block.
114
- */ getL2Block(number) {
115
- const block = this.l2Blocks[number - 1];
116
- return Promise.resolve(block);
188
+ getCheckpointsData(query) {
189
+ const checkpoints = this.resolveCheckpointsQuery(query);
190
+ return Promise.resolve(checkpoints.map((c)=>this.checkpointToData(c)));
117
191
  }
118
- /**
119
- * Gets up to `limit` amount of L2 blocks starting from `from`.
120
- * @param from - Number of the first block to return (inclusive).
121
- * @param limit - The maximum number of blocks to return.
122
- * @returns The requested mocked L2 blocks.
123
- */ getBlocks(from, limit) {
124
- return Promise.resolve(this.l2Blocks.slice(from - 1, from - 1 + limit));
125
- }
126
- getCheckpoints(from, limit) {
127
- // TODO(mbps): Implement this properly. This only works when we have one block per checkpoint.
128
- const blocks = this.l2Blocks.slice(from - 1, from - 1 + limit);
129
- return Promise.all(blocks.map(async (block)=>{
130
- // Create a checkpoint from the block - manually construct since L2Block doesn't have toCheckpoint()
131
- const checkpoint = await Checkpoint.random(block.checkpointNumber, {
132
- numBlocks: 1
133
- });
134
- checkpoint.blocks = [
135
- block
136
- ];
137
- return new PublishedCheckpoint(checkpoint, new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()), []);
138
- }));
192
+ checkpointToData(checkpoint) {
193
+ return {
194
+ checkpointNumber: checkpoint.number,
195
+ header: checkpoint.header,
196
+ archive: checkpoint.archive,
197
+ checkpointOutHash: computeCheckpointOutHash(checkpoint.blocks.map((b)=>b.body.txEffects.map((tx)=>tx.l2ToL1Msgs))),
198
+ startBlock: checkpoint.blocks[0].number,
199
+ blockCount: checkpoint.blocks.length,
200
+ feeAssetPriceModifier: checkpoint.feeAssetPriceModifier,
201
+ attestations: [],
202
+ l1: this.mockL1DataForCheckpoint(checkpoint)
203
+ };
139
204
  }
140
- async getCheckpointByArchive(archive) {
141
- // TODO(mbps): Implement this properly. This only works when we have one block per checkpoint.
142
- const block = this.l2Blocks.find((b)=>b.archive.root.equals(archive));
143
- if (!block) {
144
- return undefined;
205
+ resolveCheckpointQuery(query) {
206
+ if ('number' in query) {
207
+ return this.checkpointList[query.number - 1];
145
208
  }
146
- // Create a checkpoint from the block - manually construct since L2Block doesn't have toCheckpoint()
147
- const checkpoint = await Checkpoint.random(block.checkpointNumber, {
148
- numBlocks: 1
149
- });
150
- checkpoint.blocks = [
151
- block
152
- ];
153
- return checkpoint;
154
- }
155
- async getCheckpointedBlockByHash(blockHash) {
156
- for (const block of this.l2Blocks){
157
- const hash = await block.hash();
158
- if (hash.equals(blockHash)) {
159
- return CheckpointedL2Block.fromFields({
160
- checkpointNumber: CheckpointNumber.fromBlockNumber(block.number),
161
- block,
162
- l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
163
- attestations: []
164
- });
165
- }
166
- }
167
- return undefined;
168
- }
169
- getCheckpointedBlockByArchive(archive) {
170
- const block = this.l2Blocks.find((b)=>b.archive.root.equals(archive));
171
- if (!block) {
172
- return Promise.resolve(undefined);
209
+ if ('slot' in query) {
210
+ return this.checkpointList.find((c)=>c.header.slotNumber === query.slot);
173
211
  }
174
- return Promise.resolve(CheckpointedL2Block.fromFields({
175
- checkpointNumber: CheckpointNumber.fromBlockNumber(block.number),
176
- block,
177
- l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
178
- attestations: []
179
- }));
180
- }
181
- async getL2BlockByHash(blockHash) {
182
- for (const block of this.l2Blocks){
183
- const hash = await block.hash();
184
- if (hash.equals(blockHash)) {
185
- return block;
186
- }
212
+ switch(query.tag){
213
+ case 'checkpointed':
214
+ return this.checkpointList[this.checkpointList.length - 1];
215
+ case 'proven':
216
+ {
217
+ const provenCheckpoint = this.checkpointList.filter((c)=>c.blocks.some((b)=>b.number <= this.provenBlockNumber));
218
+ return provenCheckpoint.at(-1);
219
+ }
220
+ case 'finalized':
221
+ {
222
+ const finalizedCheckpoint = this.checkpointList.filter((c)=>c.blocks.some((b)=>b.number <= this.finalizedBlockNumber));
223
+ return finalizedCheckpoint.at(-1);
224
+ }
187
225
  }
188
- return undefined;
189
226
  }
190
- getL2BlockByArchive(archive) {
191
- const block = this.l2Blocks.find((b)=>b.archive.root.equals(archive));
192
- return Promise.resolve(block);
193
- }
194
- async getBlockHeaderByHash(blockHash) {
195
- for (const block of this.l2Blocks){
196
- const hash = await block.hash();
197
- if (hash.equals(blockHash)) {
198
- return block.header;
199
- }
227
+ resolveCheckpointsQuery(query) {
228
+ if ('from' in query) {
229
+ return this.checkpointList.slice(query.from - 1, query.from - 1 + query.limit);
200
230
  }
201
- return undefined;
202
- }
203
- getBlockHeaderByArchive(archive) {
204
- const block = this.l2Blocks.find((b)=>b.archive.root.equals(archive));
205
- return Promise.resolve(block?.header);
206
- }
207
- getBlockHeader(number) {
208
- return Promise.resolve(this.l2Blocks.at(typeof number === 'number' ? number - 1 : -1)?.header);
209
- }
210
- getCheckpointsForEpoch(epochNumber) {
211
- // TODO(mbps): Implement this properly. This only works when we have one block per checkpoint.
212
- const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
213
- const [start, end] = getSlotRangeForEpoch(epochNumber, {
214
- epochDuration
215
- });
216
- const blocks = this.l2Blocks.filter((b)=>{
217
- const slot = b.header.globalVariables.slotNumber;
218
- return slot >= start && slot <= end;
219
- });
220
- // Create checkpoints from blocks - manually construct since L2Block doesn't have toCheckpoint()
221
- return Promise.all(blocks.map(async (block)=>{
222
- const checkpoint = await Checkpoint.random(block.checkpointNumber, {
223
- numBlocks: 1
224
- });
225
- checkpoint.blocks = [
226
- block
227
- ];
228
- return checkpoint;
229
- }));
230
- }
231
- getCheckpointedBlocksForEpoch(epochNumber) {
232
- const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
233
- const [start, end] = getSlotRangeForEpoch(epochNumber, {
234
- epochDuration
235
- });
236
- const blocks = this.l2Blocks.filter((b)=>{
237
- const slot = b.header.globalVariables.slotNumber;
238
- return slot >= start && slot <= end;
239
- });
240
- return Promise.resolve(blocks.map((block)=>CheckpointedL2Block.fromFields({
241
- checkpointNumber: CheckpointNumber.fromBlockNumber(block.number),
242
- block,
243
- l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
244
- attestations: []
245
- })));
231
+ if ('fromSlot' in query) {
232
+ const matching = this.checkpointList.filter((c)=>query.reverse ? c.header.slotNumber <= query.fromSlot : c.header.slotNumber >= query.fromSlot);
233
+ const nearestFirst = query.reverse ? matching.reverse() : matching;
234
+ return nearestFirst.slice(0, query.limit);
235
+ }
236
+ return this.getCheckpointsInEpoch(query.epoch);
246
237
  }
247
238
  getBlocksForSlot(slotNumber) {
248
239
  const blocks = this.l2Blocks.filter((b)=>b.header.globalVariables.slotNumber === slotNumber);
249
240
  return Promise.resolve(blocks);
250
241
  }
251
- async getCheckpointedBlockHeadersForEpoch(epochNumber) {
252
- const checkpointedBlocks = await this.getCheckpointedBlocksForEpoch(epochNumber);
253
- return checkpointedBlocks.map((b)=>b.block.header);
254
- }
255
242
  /**
256
243
  * Gets a tx effect.
257
244
  * @param txHash - The hash of the tx corresponding to the tx effect.
@@ -269,28 +256,18 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
269
256
  data: txEffect,
270
257
  l2BlockNumber: block.number,
271
258
  l2BlockHash: await block.hash(),
272
- txIndexInBlock: block.body.txEffects.indexOf(txEffect)
259
+ txIndexInBlock: block.body.txEffects.findIndex((t)=>t.txHash.equals(txHash)),
260
+ slotNumber: block.header.globalVariables.slotNumber
273
261
  };
274
262
  }
275
- /**
276
- * Gets a receipt of a settled tx.
277
- * @param txHash - The hash of a tx we try to get the receipt for.
278
- * @returns The requested tx receipt (or undefined if not found).
279
- */ async getSettledTxReceipt(txHash) {
280
- for (const block of this.l2Blocks){
281
- for (const txEffect of block.body.txEffects){
282
- if (txEffect.txHash.equals(txHash)) {
283
- // In mock, assume all txs are checkpointed with successful execution
284
- return new TxReceipt(txHash, TxStatus.CHECKPOINTED, TxExecutionResult.SUCCESS, undefined, txEffect.transactionFee.toBigInt(), await block.hash(), block.number);
285
- }
286
- }
287
- }
288
- return undefined;
263
+ getL2ToL1MembershipWitness() {
264
+ // Mock does not back the L2-to-L1 message witness flow.
265
+ return Promise.resolve(undefined);
289
266
  }
290
267
  async getL2Tips() {
291
268
  const [latest, proven, finalized, checkpointed] = [
292
269
  await this.getBlockNumber(),
293
- await this.getProvenBlockNumber(),
270
+ this.provenBlockNumber,
294
271
  this.finalizedBlockNumber,
295
272
  this.checkpointedBlockNumber
296
273
  ];
@@ -298,29 +275,45 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
298
275
  const provenBlock = this.l2Blocks[proven - 1];
299
276
  const finalizedBlock = this.l2Blocks[finalized - 1];
300
277
  const checkpointedBlock = this.l2Blocks[checkpointed - 1];
278
+ // For genesis tips (block number 0) report the dynamic initial header hash so consumers
279
+ // running L2BlockStream against this mock agree at block 0 with their local tip store.
280
+ const genesisHash = (await this.initialHeader.hash()).toString();
281
+ const tipHash = async (block, number)=>{
282
+ if (block) {
283
+ return (await block.hash()).toString();
284
+ }
285
+ return number === 0 ? genesisHash : '';
286
+ };
301
287
  const latestBlockId = {
302
288
  number: BlockNumber(latest),
303
- hash: (await latestBlock?.hash())?.toString()
289
+ hash: await tipHash(latestBlock, latest)
304
290
  };
305
291
  const provenBlockId = {
306
292
  number: BlockNumber(proven),
307
- hash: (await provenBlock?.hash())?.toString()
293
+ hash: await tipHash(provenBlock, proven)
308
294
  };
309
295
  const finalizedBlockId = {
310
296
  number: BlockNumber(finalized),
311
- hash: (await finalizedBlock?.hash())?.toString()
297
+ hash: await tipHash(finalizedBlock, finalized)
312
298
  };
313
299
  const checkpointedBlockId = {
314
300
  number: BlockNumber(checkpointed),
315
- hash: (await checkpointedBlock?.hash())?.toString()
301
+ hash: await tipHash(checkpointedBlock, checkpointed)
316
302
  };
317
- const makeTipId = (blockId)=>({
303
+ const makeTipId = (blockId)=>{
304
+ const checkpointNumber = this.findCheckpointNumberForBlock(blockId.number) ?? CheckpointNumber(0);
305
+ // Match production semantics: checkpoint 0 is fully synthetic (no real checkpoint header
306
+ // exists at 0), so its hash stays at the protocol constant `GENESIS_CHECKPOINT_HEADER_HASH`
307
+ // even though the block-0 hash is dynamic. See L2TipsCache for the production path.
308
+ const hash = checkpointNumber === 0 ? GENESIS_CHECKPOINT_HEADER_HASH.toString() : blockId.hash;
309
+ return {
318
310
  block: blockId,
319
311
  checkpoint: {
320
- number: CheckpointNumber.fromBlockNumber(blockId.number),
321
- hash: blockId.hash
312
+ number: checkpointNumber,
313
+ hash
322
314
  }
323
- });
315
+ };
316
+ };
324
317
  return {
325
318
  proposed: latestBlockId,
326
319
  checkpointed: makeTipId(checkpointedBlockId),
@@ -328,10 +321,10 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
328
321
  finalized: makeTipId(finalizedBlockId)
329
322
  };
330
323
  }
331
- getL2EpochNumber() {
324
+ getSyncedL2EpochNumber() {
332
325
  throw new Error('Method not implemented.');
333
326
  }
334
- getL2SlotNumber() {
327
+ getSyncedL2SlotNumber() {
335
328
  throw new Error('Method not implemented.');
336
329
  }
337
330
  isEpochComplete(_epochNumber) {
@@ -340,9 +333,12 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
340
333
  getL1Constants() {
341
334
  return Promise.resolve(EmptyL1RollupConstants);
342
335
  }
336
+ isPruneDueAtSlot(_slot) {
337
+ return Promise.resolve(false);
338
+ }
343
339
  getGenesisValues() {
344
340
  return Promise.resolve({
345
- genesisArchiveRoot: new Fr(GENESIS_ARCHIVE_ROOT)
341
+ genesisArchiveRoot: this.genesisArchiveRoot ?? new Fr(GENESIS_ARCHIVE_ROOT)
346
342
  });
347
343
  }
348
344
  getL1Timestamp() {
@@ -383,6 +379,88 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
383
379
  syncImmediate() {
384
380
  return Promise.resolve();
385
381
  }
382
+ async getBlock(query) {
383
+ if ('number' in query) {
384
+ if (query.number === 0) {
385
+ return this.getGenesisBlock();
386
+ }
387
+ return this.l2Blocks[query.number - 1];
388
+ }
389
+ if ('hash' in query) {
390
+ const genesis = this.getGenesisBlock();
391
+ if ((await genesis.hash()).equals(query.hash)) {
392
+ return genesis;
393
+ }
394
+ for (const b of this.l2Blocks){
395
+ const hash = await b.hash();
396
+ if (hash.equals(query.hash)) {
397
+ return b;
398
+ }
399
+ }
400
+ return undefined;
401
+ }
402
+ if ('archive' in query) {
403
+ const genesis = this.getGenesisBlock();
404
+ if (genesis.archive.root.equals(query.archive)) {
405
+ return genesis;
406
+ }
407
+ return this.l2Blocks.find((b)=>b.archive.root.equals(query.archive));
408
+ }
409
+ const number = this.resolveBlockTag(query.tag);
410
+ if (number === 0) {
411
+ return this.getGenesisBlock();
412
+ }
413
+ return this.l2Blocks[number - 1];
414
+ }
415
+ resolveBlockTag(tag) {
416
+ switch(tag){
417
+ case 'latest':
418
+ case 'proposed':
419
+ return this.l2Blocks.length;
420
+ case 'checkpointed':
421
+ return this.checkpointedBlockNumber;
422
+ case 'proven':
423
+ return this.provenBlockNumber;
424
+ case 'finalized':
425
+ return this.finalizedBlockNumber;
426
+ }
427
+ }
428
+ getBlocks(query) {
429
+ let blocks;
430
+ if ('from' in query) {
431
+ blocks = this.l2Blocks.slice(query.from - 1, query.from - 1 + query.limit);
432
+ } else {
433
+ const epochCheckpoints = this.getCheckpointsInEpoch(query.epoch);
434
+ blocks = epochCheckpoints.flatMap((c)=>c.blocks);
435
+ }
436
+ if (query.onlyCheckpointed) {
437
+ blocks = blocks.filter((b)=>b.header.globalVariables.blockNumber <= this.checkpointedBlockNumber);
438
+ }
439
+ return Promise.resolve(blocks);
440
+ }
441
+ async getBlockData(query) {
442
+ const block = await this.getBlock(query);
443
+ if (!block) {
444
+ return undefined;
445
+ }
446
+ return {
447
+ header: block.header,
448
+ archive: block.archive,
449
+ blockHash: await block.hash(),
450
+ checkpointNumber: block.checkpointNumber,
451
+ indexWithinCheckpoint: block.indexWithinCheckpoint
452
+ };
453
+ }
454
+ async getBlocksData(query) {
455
+ const blocks = await this.getBlocks(query);
456
+ return Promise.all(blocks.map(async (block)=>({
457
+ header: block.header,
458
+ archive: block.archive,
459
+ blockHash: await block.hash(),
460
+ checkpointNumber: block.checkpointNumber,
461
+ indexWithinCheckpoint: block.indexWithinCheckpoint
462
+ })));
463
+ }
386
464
  isPendingChainInvalid() {
387
465
  return Promise.resolve(false);
388
466
  }
@@ -391,4 +469,21 @@ import { TxExecutionResult, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
391
469
  valid: true
392
470
  });
393
471
  }
472
+ getProposedCheckpointData(_query) {
473
+ return Promise.resolve(undefined);
474
+ }
475
+ /** Returns checkpoints whose slot falls within the given epoch. */ getCheckpointsInEpoch(epochNumber) {
476
+ const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
477
+ const [start, end] = getSlotRangeForEpoch(epochNumber, {
478
+ epochDuration
479
+ });
480
+ return this.checkpointList.filter((c)=>c.header.slotNumber >= start && c.header.slotNumber <= end);
481
+ }
482
+ /** Creates a mock L1PublishedData for a checkpoint. */ mockL1DataForCheckpoint(checkpoint) {
483
+ return new L1PublishedData(BigInt(checkpoint.number), BigInt(checkpoint.number), Buffer32.random().toString());
484
+ }
485
+ /** Finds the checkpoint number for a block, or undefined if the block is not in any checkpoint. */ findCheckpointNumberForBlock(blockNumber) {
486
+ const checkpoint = this.checkpointList.find((c)=>c.blocks.some((b)=>b.number === blockNumber));
487
+ return checkpoint?.number;
488
+ }
394
489
  }