@aztec/archiver 0.0.1-commit.d431d1c → 0.0.1-commit.d58ff9d0

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 (138) hide show
  1. package/README.md +28 -11
  2. package/dest/archiver.d.ts +80 -21
  3. package/dest/archiver.d.ts.map +1 -1
  4. package/dest/archiver.js +347 -167
  5. package/dest/config.d.ts +7 -3
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +38 -14
  8. package/dest/errors.d.ts +60 -9
  9. package/dest/errors.d.ts.map +1 -1
  10. package/dest/errors.js +88 -13
  11. package/dest/factory.d.ts +13 -6
  12. package/dest/factory.d.ts.map +1 -1
  13. package/dest/factory.js +59 -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 +35 -32
  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 +45 -50
  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/l1/validate_trace.d.ts +6 -3
  33. package/dest/l1/validate_trace.d.ts.map +1 -1
  34. package/dest/l1/validate_trace.js +13 -9
  35. package/dest/modules/contract_data_source_adapter.d.ts +25 -0
  36. package/dest/modules/contract_data_source_adapter.d.ts.map +1 -0
  37. package/dest/modules/contract_data_source_adapter.js +32 -0
  38. package/dest/modules/data_source_base.d.ts +75 -48
  39. package/dest/modules/data_source_base.d.ts.map +1 -1
  40. package/dest/modules/data_source_base.js +251 -220
  41. package/dest/modules/data_store_updater.d.ts +65 -29
  42. package/dest/modules/data_store_updater.d.ts.map +1 -1
  43. package/dest/modules/data_store_updater.js +242 -153
  44. package/dest/modules/instrumentation.d.ts +30 -4
  45. package/dest/modules/instrumentation.d.ts.map +1 -1
  46. package/dest/modules/instrumentation.js +83 -20
  47. package/dest/modules/l1_synchronizer.d.ts +16 -14
  48. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  49. package/dest/modules/l1_synchronizer.js +493 -225
  50. package/dest/modules/outbox_trees_resolver.d.ts +62 -0
  51. package/dest/modules/outbox_trees_resolver.d.ts.map +1 -0
  52. package/dest/modules/outbox_trees_resolver.js +162 -0
  53. package/dest/modules/validation.d.ts +30 -7
  54. package/dest/modules/validation.d.ts.map +1 -1
  55. package/dest/modules/validation.js +35 -17
  56. package/dest/store/block_store.d.ts +200 -75
  57. package/dest/store/block_store.d.ts.map +1 -1
  58. package/dest/store/block_store.js +802 -276
  59. package/dest/store/contract_class_store.d.ts +17 -4
  60. package/dest/store/contract_class_store.d.ts.map +1 -1
  61. package/dest/store/contract_class_store.js +51 -73
  62. package/dest/store/contract_instance_store.d.ts +28 -1
  63. package/dest/store/contract_instance_store.d.ts.map +1 -1
  64. package/dest/store/contract_instance_store.js +54 -2
  65. package/dest/store/data_stores.d.ts +68 -0
  66. package/dest/store/data_stores.d.ts.map +1 -0
  67. package/dest/store/data_stores.js +54 -0
  68. package/dest/store/function_names_cache.d.ts +17 -0
  69. package/dest/store/function_names_cache.d.ts.map +1 -0
  70. package/dest/store/function_names_cache.js +30 -0
  71. package/dest/store/l2_tips_cache.d.ts +25 -0
  72. package/dest/store/l2_tips_cache.d.ts.map +1 -0
  73. package/dest/store/l2_tips_cache.js +26 -0
  74. package/dest/store/log_store.d.ts +42 -37
  75. package/dest/store/log_store.d.ts.map +1 -1
  76. package/dest/store/log_store.js +262 -388
  77. package/dest/store/log_store_codec.d.ts +78 -0
  78. package/dest/store/log_store_codec.d.ts.map +1 -0
  79. package/dest/store/log_store_codec.js +110 -0
  80. package/dest/store/message_store.d.ts +11 -1
  81. package/dest/store/message_store.d.ts.map +1 -1
  82. package/dest/store/message_store.js +51 -9
  83. package/dest/test/fake_l1_state.d.ts +28 -4
  84. package/dest/test/fake_l1_state.d.ts.map +1 -1
  85. package/dest/test/fake_l1_state.js +166 -32
  86. package/dest/test/index.js +3 -1
  87. package/dest/test/mock_archiver.d.ts +1 -1
  88. package/dest/test/mock_archiver.d.ts.map +1 -1
  89. package/dest/test/mock_archiver.js +3 -2
  90. package/dest/test/mock_l2_block_source.d.ts +65 -51
  91. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  92. package/dest/test/mock_l2_block_source.js +309 -215
  93. package/dest/test/mock_structs.d.ts +6 -2
  94. package/dest/test/mock_structs.d.ts.map +1 -1
  95. package/dest/test/mock_structs.js +26 -12
  96. package/dest/test/noop_l1_archiver.d.ts +34 -0
  97. package/dest/test/noop_l1_archiver.d.ts.map +1 -0
  98. package/dest/test/noop_l1_archiver.js +88 -0
  99. package/package.json +15 -14
  100. package/src/archiver.ts +458 -198
  101. package/src/config.ts +43 -13
  102. package/src/errors.ts +134 -21
  103. package/src/factory.ts +87 -36
  104. package/src/index.ts +19 -2
  105. package/src/l1/README.md +25 -68
  106. package/src/l1/bin/retrieve-calldata.ts +45 -33
  107. package/src/l1/calldata_retriever.ts +267 -379
  108. package/src/l1/data_retrieval.ts +70 -74
  109. package/src/l1/spire_proposer.ts +7 -15
  110. package/src/l1/validate_historical_logs.ts +140 -0
  111. package/src/l1/validate_trace.ts +24 -6
  112. package/src/modules/contract_data_source_adapter.ts +46 -0
  113. package/src/modules/data_source_base.ts +336 -288
  114. package/src/modules/data_store_updater.ts +287 -187
  115. package/src/modules/instrumentation.ts +86 -21
  116. package/src/modules/l1_synchronizer.ts +667 -278
  117. package/src/modules/outbox_trees_resolver.ts +199 -0
  118. package/src/modules/validation.ts +80 -23
  119. package/src/store/block_store.ts +984 -352
  120. package/src/store/contract_class_store.ts +57 -107
  121. package/src/store/contract_instance_store.ts +68 -5
  122. package/src/store/data_stores.ts +104 -0
  123. package/src/store/function_names_cache.ts +37 -0
  124. package/src/store/l2_tips_cache.ts +35 -0
  125. package/src/store/log_store.ts +305 -500
  126. package/src/store/log_store_codec.ts +143 -0
  127. package/src/store/message_store.ts +60 -10
  128. package/src/structs/inbox_message.ts +1 -1
  129. package/src/test/fake_l1_state.ts +215 -44
  130. package/src/test/index.ts +3 -0
  131. package/src/test/mock_archiver.ts +3 -2
  132. package/src/test/mock_l2_block_source.ts +381 -246
  133. package/src/test/mock_structs.ts +50 -17
  134. package/src/test/noop_l1_archiver.ts +158 -0
  135. package/dest/store/kv_archiver_store.d.ts +0 -336
  136. package/dest/store/kv_archiver_store.d.ts.map +0 -1
  137. package/dest/store/kv_archiver_store.js +0 -443
  138. package/src/store/kv_archiver_store.ts +0 -631
@@ -1,38 +1,104 @@
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, L2BlockHash, L2BlockNew } 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 { 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 L2BlockNew.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
- addBlocks(blocks) {
86
+ addProposedBlocks(blocks) {
31
87
  this.l2Blocks.push(...blocks);
32
- this.log.verbose(`Added ${blocks.length} blocks to the mock L2 block source`);
88
+ this.log.verbose(`Added ${blocks.length} proposed blocks to the mock L2 block source`);
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 { 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,201 +150,95 @@ import { 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));
67
- }
68
- getProvenBlockNumber() {
69
- return Promise.resolve(BlockNumber(this.provenBlockNumber));
70
- }
71
- getCheckpointedBlock(number) {
72
- if (number > this.checkpointedBlockNumber) {
73
- return Promise.resolve(undefined);
153
+ async getBlockNumber(query) {
154
+ if (!query) {
155
+ return BlockNumber(this.l2Blocks.length);
74
156
  }
75
- const block = this.l2Blocks[number - 1];
76
- if (!block) {
77
- return Promise.resolve(undefined);
157
+ if ('number' in query) {
158
+ return query.number;
78
159
  }
79
- const checkpointedBlock = new CheckpointedL2Block(CheckpointNumber(number), block, new L1PublishedData(BigInt(number), BigInt(number), `0x${number.toString(16).padStart(64, '0')}`), []);
80
- return Promise.resolve(checkpointedBlock);
81
- }
82
- async getCheckpointedBlocks(from, limit, _proven) {
83
- const result = [];
84
- for(let i = 0; i < limit; i++){
85
- const blockNum = from + i;
86
- if (blockNum > this.checkpointedBlockNumber) {
87
- break;
88
- }
89
- const block = await this.getCheckpointedBlock(BlockNumber(blockNum));
90
- if (block) {
91
- result.push(block);
92
- }
160
+ if ('tag' in query) {
161
+ return BlockNumber(this.resolveBlockTag(query.tag));
93
162
  }
94
- return result;
163
+ const block = await this.getBlock(query);
164
+ return block ? block.header.globalVariables.blockNumber : undefined;
95
165
  }
96
- /**
97
- * Gets an l2 block.
98
- * @param number - The block number to return (inclusive).
99
- * @returns The requested L2 block.
100
- */ getBlock(number) {
101
- const block = this.l2Blocks[number - 1];
102
- return Promise.resolve(block);
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), []));
103
172
  }
104
- /**
105
- * Gets an L2 block (new format).
106
- * @param number - The block number to return.
107
- * @returns The requested L2 block.
108
- */ getL2BlockNew(number) {
109
- const block = this.l2Blocks[number - 1];
110
- return Promise.resolve(block);
173
+ getCheckpoints(query) {
174
+ const checkpoints = this.resolveCheckpointsQuery(query);
175
+ return Promise.resolve(checkpoints.map((checkpoint)=>new PublishedCheckpoint(checkpoint, this.mockL1DataForCheckpoint(checkpoint), [])));
111
176
  }
112
- /**
113
- * Gets up to `limit` amount of L2 blocks starting from `from`.
114
- * @param from - Number of the first block to return (inclusive).
115
- * @param limit - The maximum number of blocks to return.
116
- * @returns The requested mocked L2 blocks.
117
- */ getBlocks(from, limit, proven) {
118
- return Promise.resolve(this.l2Blocks.slice(from - 1, from - 1 + limit).filter((b)=>!proven || this.provenBlockNumber === undefined || b.number <= this.provenBlockNumber));
119
- }
120
- getPublishedCheckpoints(from, limit) {
121
- // TODO(mbps): Implement this properly. This only works when we have one block per checkpoint.
122
- const blocks = this.l2Blocks.slice(from - 1, from - 1 + limit);
123
- return Promise.all(blocks.map(async (block)=>{
124
- // Create a checkpoint from the block - manually construct since L2BlockNew doesn't have toCheckpoint()
125
- const checkpoint = await Checkpoint.random(block.checkpointNumber, {
126
- numBlocks: 1
127
- });
128
- checkpoint.blocks = [
129
- block
130
- ];
131
- return new PublishedCheckpoint(checkpoint, new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()), []);
132
- }));
177
+ getCheckpointByArchive(archive) {
178
+ const checkpoint = this.checkpointList.find((c)=>c.archive.root.equals(archive));
179
+ return Promise.resolve(checkpoint);
133
180
  }
134
- async getCheckpointByArchive(archive) {
135
- // TODO(mbps): Implement this properly. This only works when we have one block per checkpoint.
136
- const block = this.l2Blocks.find((b)=>b.archive.root.equals(archive));
137
- if (!block) {
138
- return undefined;
181
+ getCheckpointData(query) {
182
+ const checkpoint = this.resolveCheckpointQuery(query);
183
+ if (!checkpoint) {
184
+ return Promise.resolve(undefined);
139
185
  }
140
- // Create a checkpoint from the block - manually construct since L2BlockNew doesn't have toCheckpoint()
141
- const checkpoint = await Checkpoint.random(block.checkpointNumber, {
142
- numBlocks: 1
143
- });
144
- checkpoint.blocks = [
145
- block
146
- ];
147
- return checkpoint;
148
- }
149
- getPublishedBlocks(from, limit, proven) {
150
- const blocks = this.l2Blocks.slice(from - 1, from - 1 + limit).filter((b)=>!proven || this.provenBlockNumber === undefined || b.number <= this.provenBlockNumber);
151
- return Promise.resolve(blocks.map((block)=>CheckpointedL2Block.fromFields({
152
- checkpointNumber: CheckpointNumber(block.number),
153
- block,
154
- l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
155
- attestations: []
156
- })));
186
+ return Promise.resolve(this.checkpointToData(checkpoint));
157
187
  }
158
- getL2BlocksNew(from, limit, proven) {
159
- // getBlocks already returns L2BlockNew[], so just return directly
160
- return this.getBlocks(from, limit, proven);
161
- }
162
- async getPublishedBlockByHash(blockHash) {
163
- for (const block of this.l2Blocks){
164
- const hash = await block.hash();
165
- if (hash.equals(blockHash)) {
166
- return CheckpointedL2Block.fromFields({
167
- checkpointNumber: CheckpointNumber(block.number),
168
- block,
169
- l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
170
- attestations: []
171
- });
172
- }
173
- }
174
- return undefined;
188
+ getCheckpointsData(query) {
189
+ const checkpoints = this.resolveCheckpointsQuery(query);
190
+ return Promise.resolve(checkpoints.map((c)=>this.checkpointToData(c)));
175
191
  }
176
- getPublishedBlockByArchive(archive) {
177
- const block = this.l2Blocks.find((b)=>b.archive.root.equals(archive));
178
- if (!block) {
179
- return Promise.resolve(undefined);
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
+ };
204
+ }
205
+ resolveCheckpointQuery(query) {
206
+ if ('number' in query) {
207
+ return this.checkpointList[query.number - 1];
180
208
  }
181
- return Promise.resolve(CheckpointedL2Block.fromFields({
182
- checkpointNumber: CheckpointNumber(block.number),
183
- block,
184
- l1: new L1PublishedData(BigInt(block.number), BigInt(block.number), Buffer32.random().toString()),
185
- attestations: []
186
- }));
187
- }
188
- async getL2BlockNewByHash(blockHash) {
189
- for (const block of this.l2Blocks){
190
- const hash = await block.hash();
191
- if (hash.equals(blockHash)) {
192
- return block;
193
- }
209
+ if ('slot' in query) {
210
+ return this.checkpointList.find((c)=>c.header.slotNumber === query.slot);
194
211
  }
195
- return undefined;
196
- }
197
- getL2BlockNewByArchive(archive) {
198
- const block = this.l2Blocks.find((b)=>b.archive.root.equals(archive));
199
- return Promise.resolve(block);
200
- }
201
- async getBlockHeaderByHash(blockHash) {
202
- for (const block of this.l2Blocks){
203
- const hash = await block.hash();
204
- if (hash.equals(blockHash)) {
205
- return block.header;
206
- }
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
+ }
207
225
  }
208
- return undefined;
209
- }
210
- getBlockHeaderByArchive(archive) {
211
- const block = this.l2Blocks.find((b)=>b.archive.root.equals(archive));
212
- return Promise.resolve(block?.header);
213
- }
214
- getBlockHeader(number) {
215
- return Promise.resolve(this.l2Blocks.at(typeof number === 'number' ? number - 1 : -1)?.header);
216
- }
217
- getCheckpointsForEpoch(epochNumber) {
218
- // TODO(mbps): Implement this properly. This only works when we have one block per checkpoint.
219
- const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
220
- const [start, end] = getSlotRangeForEpoch(epochNumber, {
221
- epochDuration
222
- });
223
- const blocks = this.l2Blocks.filter((b)=>{
224
- const slot = b.header.globalVariables.slotNumber;
225
- return slot >= start && slot <= end;
226
- });
227
- // Create checkpoints from blocks - manually construct since L2BlockNew doesn't have toCheckpoint()
228
- return Promise.all(blocks.map(async (block)=>{
229
- const checkpoint = await Checkpoint.random(block.checkpointNumber, {
230
- numBlocks: 1
231
- });
232
- checkpoint.blocks = [
233
- block
234
- ];
235
- return checkpoint;
236
- }));
237
226
  }
238
- getBlocksForEpoch(epochNumber) {
239
- const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration;
240
- const [start, end] = getSlotRangeForEpoch(epochNumber, {
241
- epochDuration
242
- });
243
- const blocks = this.l2Blocks.filter((b)=>{
244
- const slot = b.header.globalVariables.slotNumber;
245
- return slot >= start && slot <= end;
246
- });
247
- return Promise.resolve(blocks);
227
+ resolveCheckpointsQuery(query) {
228
+ if ('from' in query) {
229
+ return this.checkpointList.slice(query.from - 1, query.from - 1 + query.limit);
230
+ }
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);
248
237
  }
249
238
  getBlocksForSlot(slotNumber) {
250
239
  const blocks = this.l2Blocks.filter((b)=>b.header.globalVariables.slotNumber === slotNumber);
251
240
  return Promise.resolve(blocks);
252
241
  }
253
- async getBlockHeadersForEpoch(epochNumber) {
254
- const blocks = await this.getBlocksForEpoch(epochNumber);
255
- return blocks.map((b)=>b.header);
256
- }
257
242
  /**
258
243
  * Gets a tx effect.
259
244
  * @param txHash - The hash of the tx corresponding to the tx effect.
@@ -270,28 +255,19 @@ import { TxReceipt, TxStatus } from '@aztec/stdlib/tx';
270
255
  return {
271
256
  data: txEffect,
272
257
  l2BlockNumber: block.number,
273
- l2BlockHash: L2BlockHash.fromField(await block.hash()),
274
- txIndexInBlock: block.body.txEffects.indexOf(txEffect)
258
+ l2BlockHash: await block.hash(),
259
+ txIndexInBlock: block.body.txEffects.findIndex((t)=>t.txHash.equals(txHash)),
260
+ slotNumber: block.header.globalVariables.slotNumber
275
261
  };
276
262
  }
277
- /**
278
- * Gets a receipt of a settled tx.
279
- * @param txHash - The hash of a tx we try to get the receipt for.
280
- * @returns The requested tx receipt (or undefined if not found).
281
- */ async getSettledTxReceipt(txHash) {
282
- for (const block of this.l2Blocks){
283
- for (const txEffect of block.body.txEffects){
284
- if (txEffect.txHash.equals(txHash)) {
285
- return new TxReceipt(txHash, TxStatus.SUCCESS, '', txEffect.transactionFee.toBigInt(), L2BlockHash.fromField(await block.hash()), block.number);
286
- }
287
- }
288
- }
289
- return undefined;
263
+ getL2ToL1MembershipWitness() {
264
+ // Mock does not back the L2-to-L1 message witness flow.
265
+ return Promise.resolve(undefined);
290
266
  }
291
267
  async getL2Tips() {
292
268
  const [latest, proven, finalized, checkpointed] = [
293
269
  await this.getBlockNumber(),
294
- await this.getProvenBlockNumber(),
270
+ this.provenBlockNumber,
295
271
  this.finalizedBlockNumber,
296
272
  this.checkpointedBlockNumber
297
273
  ];
@@ -299,29 +275,45 @@ import { TxReceipt, TxStatus } from '@aztec/stdlib/tx';
299
275
  const provenBlock = this.l2Blocks[proven - 1];
300
276
  const finalizedBlock = this.l2Blocks[finalized - 1];
301
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
+ };
302
287
  const latestBlockId = {
303
288
  number: BlockNumber(latest),
304
- hash: (await latestBlock?.hash())?.toString()
289
+ hash: await tipHash(latestBlock, latest)
305
290
  };
306
291
  const provenBlockId = {
307
292
  number: BlockNumber(proven),
308
- hash: (await provenBlock?.hash())?.toString()
293
+ hash: await tipHash(provenBlock, proven)
309
294
  };
310
295
  const finalizedBlockId = {
311
296
  number: BlockNumber(finalized),
312
- hash: (await finalizedBlock?.hash())?.toString()
297
+ hash: await tipHash(finalizedBlock, finalized)
313
298
  };
314
299
  const checkpointedBlockId = {
315
300
  number: BlockNumber(checkpointed),
316
- hash: (await checkpointedBlock?.hash())?.toString()
301
+ hash: await tipHash(checkpointedBlock, checkpointed)
317
302
  };
318
- 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 {
319
310
  block: blockId,
320
311
  checkpoint: {
321
- number: CheckpointNumber(blockId.number),
322
- hash: blockId.hash
312
+ number: checkpointNumber,
313
+ hash
323
314
  }
324
- });
315
+ };
316
+ };
325
317
  return {
326
318
  proposed: latestBlockId,
327
319
  checkpointed: makeTipId(checkpointedBlockId),
@@ -329,10 +321,10 @@ import { TxReceipt, TxStatus } from '@aztec/stdlib/tx';
329
321
  finalized: makeTipId(finalizedBlockId)
330
322
  };
331
323
  }
332
- getL2EpochNumber() {
324
+ getSyncedL2EpochNumber() {
333
325
  throw new Error('Method not implemented.');
334
326
  }
335
- getL2SlotNumber() {
327
+ getSyncedL2SlotNumber() {
336
328
  throw new Error('Method not implemented.');
337
329
  }
338
330
  isEpochComplete(_epochNumber) {
@@ -341,9 +333,12 @@ import { TxReceipt, TxStatus } from '@aztec/stdlib/tx';
341
333
  getL1Constants() {
342
334
  return Promise.resolve(EmptyL1RollupConstants);
343
335
  }
336
+ isPruneDueAtSlot(_slot) {
337
+ return Promise.resolve(false);
338
+ }
344
339
  getGenesisValues() {
345
340
  return Promise.resolve({
346
- genesisArchiveRoot: new Fr(GENESIS_ARCHIVE_ROOT)
341
+ genesisArchiveRoot: this.genesisArchiveRoot ?? new Fr(GENESIS_ARCHIVE_ROOT)
347
342
  });
348
343
  }
349
344
  getL1Timestamp() {
@@ -384,6 +379,88 @@ import { TxReceipt, TxStatus } from '@aztec/stdlib/tx';
384
379
  syncImmediate() {
385
380
  return Promise.resolve();
386
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
+ }
387
464
  isPendingChainInvalid() {
388
465
  return Promise.resolve(false);
389
466
  }
@@ -392,4 +469,21 @@ import { TxReceipt, TxStatus } from '@aztec/stdlib/tx';
392
469
  valid: true
393
470
  });
394
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
+ }
395
489
  }