@aztec/archiver 0.0.1-commit.86469d5 → 0.0.1-commit.8655d4a

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 -11
  2. package/dest/archiver.d.ts +39 -17
  3. package/dest/archiver.d.ts.map +1 -1
  4. package/dest/archiver.js +260 -160
  5. package/dest/config.d.ts +6 -3
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +23 -15
  8. package/dest/errors.d.ts +55 -9
  9. package/dest/errors.d.ts.map +1 -1
  10. package/dest/errors.js +81 -14
  11. package/dest/factory.d.ts +13 -9
  12. package/dest/factory.d.ts.map +1 -1
  13. package/dest/factory.js +51 -37
  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 +36 -33
  18. package/dest/l1/calldata_retriever.d.ts +74 -50
  19. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  20. package/dest/l1/calldata_retriever.js +201 -260
  21. package/dest/l1/data_retrieval.d.ts +26 -17
  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 +40 -0
  35. package/dest/modules/data_source_base.d.ts +71 -42
  36. package/dest/modules/data_source_base.d.ts.map +1 -1
  37. package/dest/modules/data_source_base.js +270 -179
  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 +211 -121
  41. package/dest/modules/instrumentation.d.ts +21 -3
  42. package/dest/modules/instrumentation.d.ts.map +1 -1
  43. package/dest/modules/instrumentation.js +44 -9
  44. package/dest/modules/l1_synchronizer.d.ts +14 -12
  45. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  46. package/dest/modules/l1_synchronizer.js +443 -211
  47. package/dest/modules/validation.d.ts +4 -3
  48. package/dest/modules/validation.d.ts.map +1 -1
  49. package/dest/modules/validation.js +6 -6
  50. package/dest/store/block_store.d.ts +174 -66
  51. package/dest/store/block_store.d.ts.map +1 -1
  52. package/dest/store/block_store.js +743 -245
  53. package/dest/store/contract_class_store.d.ts +17 -4
  54. package/dest/store/contract_class_store.d.ts.map +1 -1
  55. package/dest/store/contract_class_store.js +24 -68
  56. package/dest/store/contract_instance_store.d.ts +28 -1
  57. package/dest/store/contract_instance_store.d.ts.map +1 -1
  58. package/dest/store/contract_instance_store.js +37 -2
  59. package/dest/store/data_stores.d.ts +68 -0
  60. package/dest/store/data_stores.d.ts.map +1 -0
  61. package/dest/store/data_stores.js +54 -0
  62. package/dest/store/function_names_cache.d.ts +17 -0
  63. package/dest/store/function_names_cache.d.ts.map +1 -0
  64. package/dest/store/function_names_cache.js +30 -0
  65. package/dest/store/l2_tips_cache.d.ts +25 -0
  66. package/dest/store/l2_tips_cache.d.ts.map +1 -0
  67. package/dest/store/l2_tips_cache.js +26 -0
  68. package/dest/store/log_store.d.ts +42 -37
  69. package/dest/store/log_store.d.ts.map +1 -1
  70. package/dest/store/log_store.js +262 -388
  71. package/dest/store/log_store_codec.d.ts +70 -0
  72. package/dest/store/log_store_codec.d.ts.map +1 -0
  73. package/dest/store/log_store_codec.js +101 -0
  74. package/dest/store/message_store.d.ts +11 -1
  75. package/dest/store/message_store.d.ts.map +1 -1
  76. package/dest/store/message_store.js +51 -9
  77. package/dest/test/fake_l1_state.d.ts +25 -1
  78. package/dest/test/fake_l1_state.d.ts.map +1 -1
  79. package/dest/test/fake_l1_state.js +166 -32
  80. package/dest/test/mock_archiver.d.ts +1 -1
  81. package/dest/test/mock_archiver.d.ts.map +1 -1
  82. package/dest/test/mock_archiver.js +3 -2
  83. package/dest/test/mock_l1_to_l2_message_source.d.ts +1 -1
  84. package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
  85. package/dest/test/mock_l1_to_l2_message_source.js +2 -1
  86. package/dest/test/mock_l2_block_source.d.ts +62 -41
  87. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  88. package/dest/test/mock_l2_block_source.js +321 -202
  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 +13 -1
  92. package/dest/test/noop_l1_archiver.d.ts +12 -6
  93. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  94. package/dest/test/noop_l1_archiver.js +26 -9
  95. package/package.json +14 -14
  96. package/src/archiver.ts +319 -181
  97. package/src/config.ts +32 -12
  98. package/src/errors.ts +122 -21
  99. package/src/factory.ts +75 -36
  100. package/src/index.ts +19 -2
  101. package/src/l1/README.md +25 -68
  102. package/src/l1/bin/retrieve-calldata.ts +46 -39
  103. package/src/l1/calldata_retriever.ts +260 -379
  104. package/src/l1/data_retrieval.ts +58 -69
  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 +55 -0
  108. package/src/modules/data_source_base.ts +347 -221
  109. package/src/modules/data_store_updater.ts +248 -153
  110. package/src/modules/instrumentation.ts +56 -9
  111. package/src/modules/l1_synchronizer.ts +585 -258
  112. package/src/modules/validation.ts +10 -9
  113. package/src/store/block_store.ts +924 -300
  114. package/src/store/contract_class_store.ts +31 -103
  115. package/src/store/contract_instance_store.ts +51 -5
  116. package/src/store/data_stores.ts +104 -0
  117. package/src/store/function_names_cache.ts +37 -0
  118. package/src/store/l2_tips_cache.ts +35 -0
  119. package/src/store/log_store.ts +303 -499
  120. package/src/store/log_store_codec.ts +132 -0
  121. package/src/store/message_store.ts +60 -10
  122. package/src/structs/inbox_message.ts +1 -1
  123. package/src/test/fake_l1_state.ts +213 -42
  124. package/src/test/mock_archiver.ts +3 -2
  125. package/src/test/mock_l1_to_l2_message_source.ts +1 -0
  126. package/src/test/mock_l2_block_source.ts +394 -210
  127. package/src/test/mock_structs.ts +20 -6
  128. package/src/test/noop_l1_archiver.ts +39 -9
  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,260 +1,351 @@
1
- import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
2
- import { isDefined } from '@aztec/foundation/types';
3
- import { CommitteeAttestation } from '@aztec/stdlib/block';
1
+ import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
2
+ import { BlockNumber, CheckpointNumber, IndexWithinCheckpoint } from '@aztec/foundation/branded-types';
3
+ import { Body, L2Block } from '@aztec/stdlib/block';
4
4
  import { Checkpoint, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
5
- import { getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
5
+ import { getEpochAtSlot, getEpochNumberAtTimestamp, getLastL1SlotTimestampForL2Slot, getProofSubmissionDeadlineEpoch, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
6
+ import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
6
7
  /**
7
- * Abstract base class implementing ArchiverDataSource using a KVArchiverDataStore.
8
- * Provides implementations for all store-delegating methods and declares abstract methods
9
- * for L1-dependent functionality that subclasses must implement.
8
+ * Abstract base class implementing ArchiverDataSource using a bundle of archiver substores.
9
+ * Provides implementations for all read-side methods and declares abstract methods for
10
+ * L1-dependent functionality that subclasses must implement.
10
11
  */ export class ArchiverDataSourceBase {
11
- store;
12
+ stores;
12
13
  l1Constants;
13
- constructor(store, l1Constants){
14
- this.store = store;
14
+ /** The injected genesis block header. */ initialHeader;
15
+ /** Precomputed hash of the initial header, exposed via {@link getGenesisBlockHash}. */ initialBlockHash;
16
+ /** Archive root after block 0 was appended — read from L1 (`Rollup.getGenesisArchiveTreeRoot`). */ genesisArchiveRoot;
17
+ /** Memoized synthetic genesis block — callers rely on referential identity for caching. */ genesisBlock;
18
+ /** Memoized synthetic genesis block data — kept consistent with {@link genesisBlock}. */ genesisBlockData;
19
+ constructor(stores, l1Constants, initialHeader, initialBlockHash, genesisArchiveRoot){
20
+ this.stores = stores;
15
21
  this.l1Constants = l1Constants;
22
+ this.initialHeader = initialHeader;
23
+ this.initialBlockHash = initialBlockHash;
24
+ this.genesisArchiveRoot = genesisArchiveRoot;
25
+ const genesisArchive = new AppendOnlyTreeSnapshot(genesisArchiveRoot, 1);
26
+ this.genesisBlock = new L2Block(genesisArchive, initialHeader, Body.empty(), CheckpointNumber.ZERO, IndexWithinCheckpoint(0));
27
+ this.genesisBlockData = {
28
+ header: initialHeader,
29
+ archive: genesisArchive,
30
+ blockHash: initialBlockHash,
31
+ checkpointNumber: CheckpointNumber.ZERO,
32
+ indexWithinCheckpoint: IndexWithinCheckpoint(0)
33
+ };
16
34
  }
17
- getCheckpointNumber() {
18
- return this.store.getSynchedCheckpointNumber();
19
- }
20
- getSynchedCheckpointNumber() {
21
- return this.store.getSynchedCheckpointNumber();
35
+ /** Returns the precomputed hash of the genesis block header. */ getGenesisBlockHash() {
36
+ return this.initialBlockHash;
22
37
  }
23
- getProvenCheckpointNumber() {
24
- return this.store.getProvenCheckpointNumber();
38
+ /** Returns the synthetic genesis L2Block (memoized — same instance across calls). */ getGenesisBlock() {
39
+ return this.genesisBlock;
25
40
  }
26
- getBlockNumber() {
27
- return this.store.getLatestBlockNumber();
41
+ /** Returns genesis block data (memoized — same instance across calls). */ getGenesisBlockData() {
42
+ return this.genesisBlockData;
28
43
  }
29
- getProvenBlockNumber() {
30
- return this.store.getProvenBlockNumber();
44
+ /**
45
+ * Type guard distinguishing the genesis sentinel from a {@link ResolvedBlockQuery}.
46
+ * `resolveBlockQuery` already rewrites every genesis-matching shape to the sentinel,
47
+ * so callers only need this single sync check.
48
+ */ isGenesisBlockQuery(query) {
49
+ return 'genesis' in query;
31
50
  }
32
- async getBlockHeader(number) {
33
- const blockNumber = number === 'latest' ? await this.store.getLatestBlockNumber() : number;
34
- if (blockNumber === 0) {
35
- return undefined;
51
+ async isPruneDueAtSlot(slot) {
52
+ if (!this.l1Constants) {
53
+ throw new Error('isPruneDueAtSlot requires l1Constants');
36
54
  }
37
- const headers = await this.store.getBlockHeaders(blockNumber, 1);
38
- return headers.length === 0 ? undefined : headers[0];
39
- }
40
- getCheckpointedBlock(number) {
41
- return this.store.getCheckpointedBlock(number);
55
+ const tips = await this.getL2Tips();
56
+ const proven = tips.proven.checkpoint.number;
57
+ const pending = tips.checkpointed.checkpoint.number;
58
+ if (pending === proven) {
59
+ return false;
60
+ }
61
+ const oldestUnproven = await this.getCheckpointData({
62
+ number: CheckpointNumber(Number(proven) + 1)
63
+ });
64
+ if (!oldestUnproven) {
65
+ return false;
66
+ }
67
+ const slotTs = getLastL1SlotTimestampForL2Slot(slot, this.l1Constants);
68
+ const slotEpoch = getEpochNumberAtTimestamp(slotTs, this.l1Constants);
69
+ const oldestUnprovenEpoch = getEpochAtSlot(oldestUnproven.header.slotNumber, this.l1Constants);
70
+ const deadlineEpoch = getProofSubmissionDeadlineEpoch(oldestUnprovenEpoch, this.l1Constants);
71
+ return slotEpoch >= deadlineEpoch;
42
72
  }
43
- getCheckpointedL2BlockNumber() {
44
- return this.store.getCheckpointedL2BlockNumber();
73
+ getCheckpointNumber() {
74
+ return this.stores.blocks.getLatestCheckpointNumber();
45
75
  }
46
- getFinalizedL2BlockNumber() {
47
- return this.store.getFinalizedL2BlockNumber();
76
+ getProvenCheckpointNumber() {
77
+ return this.stores.blocks.getProvenCheckpointNumber();
48
78
  }
49
- async getCheckpointHeader(number) {
50
- if (number === 'latest') {
51
- number = await this.store.getSynchedCheckpointNumber();
79
+ async getBlockNumber(query) {
80
+ if (!query) {
81
+ return this.stores.blocks.getLatestL2BlockNumber();
52
82
  }
53
- if (number === 0) {
83
+ const resolved = await this.resolveBlockQuery(query);
84
+ if (resolved === undefined) {
54
85
  return undefined;
55
86
  }
56
- const checkpoint = await this.store.getCheckpointData(number);
57
- if (!checkpoint) {
87
+ if (this.isGenesisBlockQuery(resolved)) {
88
+ return BlockNumber.ZERO;
89
+ }
90
+ return this.stores.blocks.getBlockNumber(resolved);
91
+ }
92
+ /**
93
+ * Resolves a {@link CheckpointQuery} to a concrete `CheckpointNumber`, or undefined when the
94
+ * query refers to a position that has no checkpoint yet (e.g. `{ slot }` not found).
95
+ */ resolveCheckpointQuery(query) {
96
+ if ('number' in query) {
97
+ return Promise.resolve(query.number);
98
+ }
99
+ if ('slot' in query) {
100
+ return this.stores.blocks.getCheckpointNumberBySlot(query.slot);
101
+ }
102
+ // tag variant
103
+ switch(query.tag){
104
+ case 'checkpointed':
105
+ return this.stores.blocks.getLatestCheckpointNumber();
106
+ case 'proven':
107
+ return this.stores.blocks.getProvenCheckpointNumber();
108
+ case 'finalized':
109
+ return this.stores.blocks.getFinalizedCheckpointNumber();
110
+ }
111
+ }
112
+ /**
113
+ * Resolves a {@link CheckpointsQuery} to a concrete `{from, limit}` pair used by BlockStore,
114
+ * or undefined when the epoch has no checkpoints.
115
+ */ async resolveCheckpointsQuery(query) {
116
+ if ('from' in query) {
117
+ return query;
118
+ }
119
+ const numbers = await this.getCheckpointNumbersForEpoch(query.epoch);
120
+ if (numbers.length === 0) {
58
121
  return undefined;
59
122
  }
60
- return checkpoint.header;
123
+ return {
124
+ from: numbers[0],
125
+ limit: numbers.length
126
+ };
61
127
  }
62
- async getLastBlockNumberInCheckpoint(checkpointNumber) {
63
- const checkpointData = await this.store.getCheckpointData(checkpointNumber);
64
- if (!checkpointData) {
128
+ async getCheckpoint(query) {
129
+ const number = await this.resolveCheckpointQuery(query);
130
+ if (number === undefined || number === 0) {
65
131
  return undefined;
66
132
  }
67
- return BlockNumber(checkpointData.startBlock + checkpointData.numBlocks - 1);
133
+ const data = await this.stores.blocks.getCheckpointData(number);
134
+ if (!data) {
135
+ return undefined;
136
+ }
137
+ return this.getPublishedCheckpointFromCheckpointData(data);
68
138
  }
69
- getCheckpointedBlocks(from, limit) {
70
- return this.store.getCheckpointedBlocks(from, limit);
139
+ async getCheckpoints(query) {
140
+ const resolved = await this.resolveCheckpointsQuery(query);
141
+ if (!resolved) {
142
+ return [];
143
+ }
144
+ const checkpoints = await this.stores.blocks.getRangeOfCheckpoints(resolved.from, resolved.limit);
145
+ return Promise.all(checkpoints.map((ch)=>this.getPublishedCheckpointFromCheckpointData(ch)));
71
146
  }
72
- getBlockHeaderByHash(blockHash) {
73
- return this.store.getBlockHeaderByHash(blockHash);
147
+ async getCheckpointData(query) {
148
+ const number = await this.resolveCheckpointQuery(query);
149
+ if (number === undefined || number === 0) {
150
+ return undefined;
151
+ }
152
+ return this.stores.blocks.getCheckpointData(number);
74
153
  }
75
- getBlockHeaderByArchive(archive) {
76
- return this.store.getBlockHeaderByArchive(archive);
154
+ async getCheckpointsData(query) {
155
+ const resolved = await this.resolveCheckpointsQuery(query);
156
+ if (!resolved) {
157
+ return [];
158
+ }
159
+ return this.stores.blocks.getRangeOfCheckpoints(resolved.from, resolved.limit);
77
160
  }
78
- async getL2Block(number) {
79
- // If the number provided is -ve, then return the latest block.
80
- if (number < 0) {
81
- number = await this.store.getLatestBlockNumber();
161
+ getProposedCheckpointData(query) {
162
+ if (!query || 'tag' in query) {
163
+ return this.stores.blocks.getLastProposedCheckpoint();
82
164
  }
83
- if (number === 0) {
84
- return undefined;
165
+ if ('number' in query) {
166
+ return this.stores.blocks.getProposedCheckpointByNumber(query.number);
85
167
  }
86
- const publishedBlock = await this.store.getBlock(number);
87
- return publishedBlock;
168
+ return this.stores.blocks.getProposedCheckpointBySlot(query.slot);
88
169
  }
89
170
  getTxEffect(txHash) {
90
- return this.store.getTxEffect(txHash);
171
+ return this.stores.blocks.getTxEffect(txHash);
91
172
  }
92
173
  getSettledTxReceipt(txHash) {
93
- return this.store.getSettledTxReceipt(txHash);
174
+ return this.stores.blocks.getSettledTxReceipt(txHash, this.l1Constants);
94
175
  }
95
176
  isPendingChainInvalid() {
96
177
  return this.getPendingChainValidationStatus().then((status)=>!status.valid);
97
178
  }
98
179
  async getPendingChainValidationStatus() {
99
- return await this.store.getPendingChainValidationStatus() ?? {
180
+ return await this.stores.blocks.getPendingChainValidationStatus() ?? {
100
181
  valid: true
101
182
  };
102
183
  }
103
- getPrivateLogsByTags(tags, page) {
104
- return this.store.getPrivateLogsByTags(tags, page);
184
+ getPrivateLogsByTags(query) {
185
+ return this.stores.logs.getPrivateLogsByTags(query);
105
186
  }
106
- getPublicLogsByTagsFromContract(contractAddress, tags, page) {
107
- return this.store.getPublicLogsByTagsFromContract(contractAddress, tags, page);
108
- }
109
- getPublicLogs(filter) {
110
- return this.store.getPublicLogs(filter);
111
- }
112
- getContractClassLogs(filter) {
113
- return this.store.getContractClassLogs(filter);
187
+ getPublicLogsByTags(query) {
188
+ return this.stores.logs.getPublicLogsByTags(query);
114
189
  }
115
190
  getContractClass(id) {
116
- return this.store.getContractClass(id);
191
+ return this.stores.contractClasses.getContractClass(id);
117
192
  }
118
193
  getBytecodeCommitment(id) {
119
- return this.store.getBytecodeCommitment(id);
194
+ return this.stores.contractClasses.getBytecodeCommitment(id);
120
195
  }
121
196
  async getContract(address, maybeTimestamp) {
122
197
  let timestamp;
123
198
  if (maybeTimestamp === undefined) {
124
- const latestBlockHeader = await this.getBlockHeader('latest');
125
- // If we get undefined block header, it means that the archiver has not yet synced any block so we default to 0.
126
- timestamp = latestBlockHeader ? latestBlockHeader.globalVariables.timestamp : 0n;
199
+ const latestBlockData = await this.getBlockData({
200
+ tag: 'proposed'
201
+ });
202
+ timestamp = latestBlockData ? latestBlockData.header.globalVariables.timestamp : 0n;
127
203
  } else {
128
204
  timestamp = maybeTimestamp;
129
205
  }
130
- return this.store.getContractInstance(address, timestamp);
206
+ return this.stores.contractInstances.getContractInstance(address, timestamp);
131
207
  }
132
208
  getContractClassIds() {
133
- return this.store.getContractClassIds();
209
+ return this.stores.contractClasses.getContractClassIds();
134
210
  }
135
- getDebugFunctionName(address, selector) {
136
- return this.store.getDebugFunctionName(address, selector);
211
+ /** Looks up a public function name given a selector. */ getDebugFunctionName(_address, selector) {
212
+ return Promise.resolve(this.stores.functionNames.get(selector));
137
213
  }
138
- registerContractFunctionSignatures(signatures) {
139
- return this.store.registerContractFunctionSignatures(signatures);
214
+ /** Register public function signatures so they can be looked up by selector. */ registerContractFunctionSignatures(signatures) {
215
+ return this.stores.functionNames.register(signatures);
140
216
  }
141
217
  getL1ToL2Messages(checkpointNumber) {
142
- return this.store.getL1ToL2Messages(checkpointNumber);
218
+ return this.stores.messages.getL1ToL2Messages(checkpointNumber);
143
219
  }
144
220
  getL1ToL2MessageIndex(l1ToL2Message) {
145
- return this.store.getL1ToL2MessageIndex(l1ToL2Message);
221
+ return this.stores.messages.getL1ToL2MessageIndex(l1ToL2Message);
146
222
  }
147
- async getCheckpoints(checkpointNumber, limit) {
148
- const checkpoints = await this.store.getRangeOfCheckpoints(checkpointNumber, limit);
149
- const blocks = (await Promise.all(checkpoints.map((ch)=>this.store.getBlocksForCheckpoint(ch.checkpointNumber)))).filter(isDefined);
150
- const fullCheckpoints = [];
151
- for(let i = 0; i < checkpoints.length; i++){
152
- const blocksForCheckpoint = blocks[i];
153
- const checkpoint = checkpoints[i];
154
- const fullCheckpoint = new Checkpoint(checkpoint.archive, checkpoint.header, blocksForCheckpoint, checkpoint.checkpointNumber);
155
- const publishedCheckpoint = new PublishedCheckpoint(fullCheckpoint, checkpoint.l1, checkpoint.attestations.map((x)=>CommitteeAttestation.fromBuffer(x)));
156
- fullCheckpoints.push(publishedCheckpoint);
223
+ async getPublishedCheckpointFromCheckpointData(checkpoint) {
224
+ const blocksForCheckpoint = await this.stores.blocks.getBlocksForCheckpoint(checkpoint.checkpointNumber);
225
+ if (!blocksForCheckpoint) {
226
+ throw new Error(`Blocks for checkpoint ${checkpoint.checkpointNumber} not found`);
157
227
  }
158
- return fullCheckpoints;
228
+ const fullCheckpoint = new Checkpoint(checkpoint.archive, checkpoint.header, blocksForCheckpoint, checkpoint.checkpointNumber, checkpoint.feeAssetPriceModifier);
229
+ return new PublishedCheckpoint(fullCheckpoint, checkpoint.l1, checkpoint.attestations);
159
230
  }
160
231
  getBlocksForSlot(slotNumber) {
161
- return this.store.getBlocksForSlot(slotNumber);
232
+ return this.stores.blocks.getBlocksForSlot(slotNumber);
162
233
  }
163
- async getCheckpointedBlocksForEpoch(epochNumber) {
234
+ /** Returns just the checkpoint numbers for all checkpoints whose slot falls within the given epoch. */ getCheckpointNumbersForEpoch(epochNumber) {
164
235
  if (!this.l1Constants) {
165
236
  throw new Error('L1 constants not set');
166
237
  }
167
238
  const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1Constants);
168
- const blocks = [];
169
- // Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
170
- // We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
171
- let checkpoint = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
172
- const slot = (b)=>b.header.slotNumber;
173
- while(checkpoint && slot(checkpoint) >= start){
174
- if (slot(checkpoint) <= end) {
175
- // push the blocks on backwards
176
- const endBlock = checkpoint.startBlock + checkpoint.numBlocks - 1;
177
- for(let i = endBlock; i >= checkpoint.startBlock; i--){
178
- const checkpointedBlock = await this.getCheckpointedBlock(BlockNumber(i));
179
- if (checkpointedBlock) {
180
- blocks.push(checkpointedBlock);
181
- }
182
- }
183
- }
184
- checkpoint = await this.store.getCheckpointData(CheckpointNumber(checkpoint.checkpointNumber - 1));
185
- }
186
- return blocks.reverse();
239
+ return this.stores.blocks.getCheckpointNumbersForSlotRange(start, end);
187
240
  }
188
- async getCheckpointedBlockHeadersForEpoch(epochNumber) {
189
- if (!this.l1Constants) {
190
- throw new Error('L1 constants not set');
241
+ async getBlock(query) {
242
+ const resolved = await this.resolveBlockQuery(query);
243
+ if (resolved === undefined) {
244
+ return undefined;
191
245
  }
192
- const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1Constants);
193
- const blocks = [];
194
- // Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
195
- // We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
196
- let checkpoint = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
197
- const slot = (b)=>b.header.slotNumber;
198
- while(checkpoint && slot(checkpoint) >= start){
199
- if (slot(checkpoint) <= end) {
200
- // push the blocks on backwards
201
- const endBlock = checkpoint.startBlock + checkpoint.numBlocks - 1;
202
- for(let i = endBlock; i >= checkpoint.startBlock; i--){
203
- const block = await this.getBlockHeader(BlockNumber(i));
204
- if (block) {
205
- blocks.push(block);
206
- }
207
- }
208
- }
209
- checkpoint = await this.store.getCheckpointData(CheckpointNumber(checkpoint.checkpointNumber - 1));
246
+ if (this.isGenesisBlockQuery(resolved)) {
247
+ return this.getGenesisBlock();
248
+ }
249
+ return this.stores.blocks.getBlock(resolved);
250
+ }
251
+ /**
252
+ * Range queries iterate physical blocks only; the genesis block is NOT prepended.
253
+ * `L2BlockStream` consumers (`world-state.handleL2Blocks`, etc.) emit `blocks-added` events for
254
+ * real blocks and would be surprised by a synthetic block 0. Use {@link getBlock} or
255
+ * {@link getBlockData} for genesis-aware single-block lookups.
256
+ */ async getBlocks(query) {
257
+ const resolved = await this.resolveBlocksQuery(query);
258
+ return resolved ? this.stores.blocks.getBlocks(resolved) : [];
259
+ }
260
+ async getBlockData(query) {
261
+ const resolved = await this.resolveBlockQuery(query);
262
+ if (resolved === undefined) {
263
+ return undefined;
264
+ }
265
+ if (this.isGenesisBlockQuery(resolved)) {
266
+ return this.getGenesisBlockData();
267
+ }
268
+ return this.stores.blocks.getBlockData(resolved);
269
+ }
270
+ /** See {@link getBlocks} — range queries do not prepend the genesis block. */ async getBlocksData(query) {
271
+ const resolved = await this.resolveBlocksQuery(query);
272
+ return resolved ? this.stores.blocks.getBlocksData(resolved) : [];
273
+ }
274
+ /**
275
+ * Resolves a {@link BlockQuery} to either the genesis sentinel or a {@link ResolvedBlockQuery}
276
+ * understood by BlockStore. Detects every shape that points at block 0 — `{number:0}`,
277
+ * `{hash}` matching the initial header, `{archive}` matching the post-genesis archive root,
278
+ * and `{tag}` resolving to 0 — and rewrites them to the sentinel so callers branch once.
279
+ */ async resolveBlockQuery(query) {
280
+ if ('number' in query) {
281
+ return query.number === BlockNumber.ZERO ? {
282
+ genesis: true
283
+ } : query;
284
+ }
285
+ if ('hash' in query) {
286
+ return query.hash.equals(this.initialBlockHash) ? {
287
+ genesis: true
288
+ } : query;
210
289
  }
211
- return blocks.reverse();
290
+ if ('archive' in query) {
291
+ return query.archive.equals(this.genesisArchiveRoot) ? {
292
+ genesis: true
293
+ } : query;
294
+ }
295
+ const number = await this.resolveBlockTag(query.tag);
296
+ if (number === BlockNumber.ZERO) {
297
+ return {
298
+ genesis: true
299
+ };
300
+ }
301
+ return {
302
+ number
303
+ };
212
304
  }
213
- async getCheckpointsForEpoch(epochNumber) {
214
- if (!this.l1Constants) {
215
- throw new Error('L1 constants not set');
305
+ /** Maps a {@link BlockTag} to the matching block number for the current chain state. */ resolveBlockTag(tag) {
306
+ switch(tag){
307
+ case 'latest':
308
+ case 'proposed':
309
+ return this.stores.blocks.getLatestL2BlockNumber();
310
+ case 'checkpointed':
311
+ return this.stores.blocks.getCheckpointedL2BlockNumber();
312
+ case 'proven':
313
+ return this.stores.blocks.getProvenBlockNumber();
314
+ case 'finalized':
315
+ return this.stores.blocks.getFinalizedL2BlockNumber();
216
316
  }
217
- const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1Constants);
218
- const checkpoints = [];
219
- // Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
220
- // We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
221
- let checkpointData = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
222
- const slot = (b)=>b.header.slotNumber;
223
- while(checkpointData && slot(checkpointData) >= start){
224
- if (slot(checkpointData) <= end) {
225
- // push the checkpoints on backwards
226
- const [checkpoint] = await this.getCheckpoints(checkpointData.checkpointNumber, 1);
227
- checkpoints.push(checkpoint.checkpoint);
317
+ }
318
+ /**
319
+ * Converts an epoch-based BlocksQuery to a from/limit query using l1Constants.
320
+ * Returns undefined when the epoch has no checkpoints, so callers can return [] without
321
+ * entering BlockStore. Reads only the two endpoint checkpoints rather than the whole epoch.
322
+ */ async resolveBlocksQuery(query) {
323
+ if (!('epoch' in query)) {
324
+ if (query.from < INITIAL_L2_BLOCK_NUM) {
325
+ throw new Error(`getBlocks/getBlocksData: 'from' must be >= ${INITIAL_L2_BLOCK_NUM}, got ${query.from}. ` + `Use getBlock({number:0})/getBlockData({number:0}) for genesis-aware single-block lookups.`);
228
326
  }
229
- checkpointData = await this.store.getCheckpointData(CheckpointNumber(checkpointData.checkpointNumber - 1));
327
+ return query;
230
328
  }
231
- return checkpoints.reverse();
232
- }
233
- async getBlock(number) {
234
- // If the number provided is -ve, then return the latest block.
235
- if (number < 0) {
236
- number = await this.store.getLatestBlockNumber();
329
+ const checkpointNumbers = await this.getCheckpointNumbersForEpoch(query.epoch);
330
+ if (checkpointNumbers.length === 0) {
331
+ return undefined;
237
332
  }
238
- if (number === 0) {
333
+ const firstNumber = checkpointNumbers[0];
334
+ const lastNumber = checkpointNumbers[checkpointNumbers.length - 1];
335
+ const first = await this.stores.blocks.getCheckpointData(firstNumber);
336
+ if (!first) {
239
337
  return undefined;
240
338
  }
241
- return this.store.getBlock(number);
242
- }
243
- getBlocks(from, limit) {
244
- return this.store.getBlocks(from, limit);
245
- }
246
- getCheckpointedBlockByHash(blockHash) {
247
- return this.store.getCheckpointedBlockByHash(blockHash);
248
- }
249
- getCheckpointedBlockByArchive(archive) {
250
- return this.store.getCheckpointedBlockByArchive(archive);
251
- }
252
- async getL2BlockByHash(blockHash) {
253
- const checkpointedBlock = await this.store.getCheckpointedBlockByHash(blockHash);
254
- return checkpointedBlock?.block;
255
- }
256
- async getL2BlockByArchive(archive) {
257
- const checkpointedBlock = await this.store.getCheckpointedBlockByArchive(archive);
258
- return checkpointedBlock?.block;
339
+ const last = firstNumber === lastNumber ? first : await this.stores.blocks.getCheckpointData(lastNumber);
340
+ if (!last) {
341
+ return undefined;
342
+ }
343
+ const from = BlockNumber(first.startBlock);
344
+ const limit = last.startBlock + last.blockCount - first.startBlock;
345
+ return {
346
+ from,
347
+ limit,
348
+ onlyCheckpointed: true
349
+ };
259
350
  }
260
351
  }