@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,291 +1,322 @@
1
- import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
2
- import { isDefined } from '@aztec/foundation/types';
3
- import { CheckpointedL2Block, 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');
54
+ }
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;
36
66
  }
37
- const headers = await this.store.getBlockHeaders(blockNumber, 1);
38
- return headers.length === 0 ? undefined : headers[0];
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;
39
72
  }
40
- getCheckpointedBlock(number) {
41
- return this.store.getCheckpointedBlock(number);
73
+ getCheckpointNumber() {
74
+ return this.stores.blocks.getLatestCheckpointNumber();
42
75
  }
43
- getCheckpointedBlockNumber() {
44
- return this.store.getCheckpointedL2BlockNumber();
76
+ getProvenCheckpointNumber() {
77
+ return this.stores.blocks.getProvenCheckpointNumber();
45
78
  }
46
- async getCheckpointHeader(number) {
47
- if (number === 'latest') {
48
- number = await this.store.getSynchedCheckpointNumber();
79
+ async getBlockNumber(query) {
80
+ if (!query) {
81
+ return this.stores.blocks.getLatestL2BlockNumber();
49
82
  }
50
- if (number === 0) {
83
+ const resolved = await this.resolveBlockQuery(query);
84
+ if (resolved === undefined) {
51
85
  return undefined;
52
86
  }
53
- const checkpoint = await this.store.getCheckpointData(number);
54
- if (!checkpoint) {
55
- return undefined;
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();
56
110
  }
57
- return checkpoint.header;
58
111
  }
59
- async getLastBlockNumberInCheckpoint(checkpointNumber) {
60
- const checkpointData = await this.store.getCheckpointData(checkpointNumber);
61
- if (!checkpointData) {
112
+ async getCheckpoint(query) {
113
+ const number = await this.resolveCheckpointQuery(query);
114
+ if (number === undefined || number === 0) {
62
115
  return undefined;
63
116
  }
64
- return BlockNumber(checkpointData.startBlock + checkpointData.numBlocks - 1);
65
- }
66
- async getCheckpointedBlocks(from, limit, proven) {
67
- const blocks = await this.store.getCheckpointedBlocks(from, limit);
68
- if (proven === true) {
69
- const provenBlockNumber = await this.store.getProvenBlockNumber();
70
- return blocks.filter((b)=>b.block.number <= provenBlockNumber);
117
+ const data = await this.stores.blocks.getCheckpointData(number);
118
+ if (!data) {
119
+ return undefined;
71
120
  }
72
- return blocks;
121
+ return this.getPublishedCheckpointFromCheckpointData(data);
73
122
  }
74
- getBlockHeaderByHash(blockHash) {
75
- return this.store.getBlockHeaderByHash(blockHash);
123
+ async getCheckpoints(query) {
124
+ const checkpoints = await this.getCheckpointsData(query);
125
+ return Promise.all(checkpoints.map((ch)=>this.getPublishedCheckpointFromCheckpointData(ch)));
76
126
  }
77
- getBlockHeaderByArchive(archive) {
78
- return this.store.getBlockHeaderByArchive(archive);
127
+ async getCheckpointData(query) {
128
+ const number = await this.resolveCheckpointQuery(query);
129
+ if (number === undefined || number === 0) {
130
+ return undefined;
131
+ }
132
+ return this.stores.blocks.getCheckpointData(number);
79
133
  }
80
- async getL2BlockNew(number) {
81
- // If the number provided is -ve, then return the latest block.
82
- if (number < 0) {
83
- number = await this.store.getLatestBlockNumber();
134
+ async getCheckpointsData(query) {
135
+ if ('fromSlot' in query) {
136
+ return this.stores.blocks.getCheckpointsBySlot(query.fromSlot, query.limit, query.reverse ?? false);
84
137
  }
85
- if (number === 0) {
86
- return undefined;
138
+ if ('from' in query) {
139
+ return this.stores.blocks.getRangeOfCheckpoints(query.from, query.limit);
87
140
  }
88
- const publishedBlock = await this.store.getBlock(number);
89
- return publishedBlock;
141
+ const numbers = await this.getCheckpointNumbersForEpoch(query.epoch);
142
+ return numbers.length > 0 ? this.stores.blocks.getRangeOfCheckpoints(numbers[0], numbers.length) : [];
90
143
  }
91
- getTxEffect(txHash) {
92
- return this.store.getTxEffect(txHash);
144
+ getProposedCheckpointData(query) {
145
+ if (!query || 'tag' in query) {
146
+ return this.stores.blocks.getLastProposedCheckpoint();
147
+ }
148
+ if ('number' in query) {
149
+ return this.stores.blocks.getProposedCheckpointByNumber(query.number);
150
+ }
151
+ return this.stores.blocks.getProposedCheckpointBySlot(query.slot);
93
152
  }
94
- getSettledTxReceipt(txHash) {
95
- return this.store.getSettledTxReceipt(txHash);
153
+ getTxEffect(txHash) {
154
+ return this.stores.blocks.getTxEffect(txHash);
96
155
  }
97
156
  isPendingChainInvalid() {
98
157
  return this.getPendingChainValidationStatus().then((status)=>!status.valid);
99
158
  }
100
159
  async getPendingChainValidationStatus() {
101
- return await this.store.getPendingChainValidationStatus() ?? {
160
+ return await this.stores.blocks.getPendingChainValidationStatus() ?? {
102
161
  valid: true
103
162
  };
104
163
  }
105
- async getL2BlocksNew(from, limit, proven) {
106
- const blocks = await this.store.getBlocks(from, limit);
107
- if (proven === true) {
108
- const provenBlockNumber = await this.store.getProvenBlockNumber();
109
- return blocks.filter((b)=>b.number <= provenBlockNumber);
110
- }
111
- return blocks;
112
- }
113
- getPrivateLogsByTags(tags, page) {
114
- return this.store.getPrivateLogsByTags(tags, page);
115
- }
116
- getPublicLogsByTagsFromContract(contractAddress, tags, page) {
117
- return this.store.getPublicLogsByTagsFromContract(contractAddress, tags, page);
118
- }
119
- getPublicLogs(filter) {
120
- return this.store.getPublicLogs(filter);
164
+ getPrivateLogsByTags(query) {
165
+ return this.stores.logs.getPrivateLogsByTags(query);
121
166
  }
122
- getContractClassLogs(filter) {
123
- return this.store.getContractClassLogs(filter);
167
+ getPublicLogsByTags(query) {
168
+ return this.stores.logs.getPublicLogsByTags(query);
124
169
  }
125
170
  getContractClass(id) {
126
- return this.store.getContractClass(id);
171
+ return this.stores.contractClasses.getContractClass(id);
127
172
  }
128
173
  getBytecodeCommitment(id) {
129
- return this.store.getBytecodeCommitment(id);
130
- }
131
- async getContract(address, maybeTimestamp) {
132
- let timestamp;
133
- if (maybeTimestamp === undefined) {
134
- const latestBlockHeader = await this.getBlockHeader('latest');
135
- // If we get undefined block header, it means that the archiver has not yet synced any block so we default to 0.
136
- timestamp = latestBlockHeader ? latestBlockHeader.globalVariables.timestamp : 0n;
137
- } else {
138
- timestamp = maybeTimestamp;
139
- }
140
- return this.store.getContractInstance(address, timestamp);
174
+ return this.stores.contractClasses.getBytecodeCommitment(id);
175
+ }
176
+ getContract(address, timestamp) {
177
+ return this.stores.contractInstances.getContractInstance(address, timestamp);
141
178
  }
142
179
  getContractClassIds() {
143
- return this.store.getContractClassIds();
180
+ return this.stores.contractClasses.getContractClassIds();
144
181
  }
145
- getDebugFunctionName(address, selector) {
146
- return this.store.getDebugFunctionName(address, selector);
182
+ /** Looks up a public function name given a selector. */ getDebugFunctionName(_address, selector) {
183
+ return Promise.resolve(this.stores.functionNames.get(selector));
147
184
  }
148
- registerContractFunctionSignatures(signatures) {
149
- return this.store.registerContractFunctionSignatures(signatures);
185
+ /** Register public function signatures so they can be looked up by selector. */ registerContractFunctionSignatures(signatures) {
186
+ return this.stores.functionNames.register(signatures);
150
187
  }
151
188
  getL1ToL2Messages(checkpointNumber) {
152
- return this.store.getL1ToL2Messages(checkpointNumber);
189
+ return this.stores.messages.getL1ToL2Messages(checkpointNumber);
153
190
  }
154
191
  getL1ToL2MessageIndex(l1ToL2Message) {
155
- return this.store.getL1ToL2MessageIndex(l1ToL2Message);
156
- }
157
- async getPublishedCheckpoints(checkpointNumber, limit) {
158
- const checkpoints = await this.store.getRangeOfCheckpoints(checkpointNumber, limit);
159
- const blocks = (await Promise.all(checkpoints.map((ch)=>this.store.getBlocksForCheckpoint(ch.checkpointNumber)))).filter(isDefined);
160
- const fullCheckpoints = [];
161
- for(let i = 0; i < checkpoints.length; i++){
162
- const blocksForCheckpoint = blocks[i];
163
- const checkpoint = checkpoints[i];
164
- const fullCheckpoint = new Checkpoint(checkpoint.archive, checkpoint.header, blocksForCheckpoint, checkpoint.checkpointNumber);
165
- const publishedCheckpoint = new PublishedCheckpoint(fullCheckpoint, checkpoint.l1, checkpoint.attestations.map((x)=>CommitteeAttestation.fromBuffer(x)));
166
- fullCheckpoints.push(publishedCheckpoint);
192
+ return this.stores.messages.getL1ToL2MessageIndex(l1ToL2Message);
193
+ }
194
+ async getPublishedCheckpointFromCheckpointData(checkpoint) {
195
+ const blocksForCheckpoint = await this.stores.blocks.getBlocksForCheckpoint(checkpoint.checkpointNumber);
196
+ if (!blocksForCheckpoint) {
197
+ throw new Error(`Blocks for checkpoint ${checkpoint.checkpointNumber} not found`);
167
198
  }
168
- return fullCheckpoints;
199
+ const fullCheckpoint = new Checkpoint(checkpoint.archive, checkpoint.header, blocksForCheckpoint, checkpoint.checkpointNumber, checkpoint.feeAssetPriceModifier);
200
+ return new PublishedCheckpoint(fullCheckpoint, checkpoint.l1, checkpoint.attestations);
169
201
  }
170
202
  getBlocksForSlot(slotNumber) {
171
- return this.store.getBlocksForSlot(slotNumber);
203
+ return this.stores.blocks.getBlocksForSlot(slotNumber);
172
204
  }
173
- async getBlocksForEpoch(epochNumber) {
205
+ /** Returns just the checkpoint numbers for all checkpoints whose slot falls within the given epoch. */ getCheckpointNumbersForEpoch(epochNumber) {
174
206
  if (!this.l1Constants) {
175
207
  throw new Error('L1 constants not set');
176
208
  }
177
209
  const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1Constants);
178
- const blocks = [];
179
- // Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
180
- // We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
181
- let checkpoint = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
182
- const slot = (b)=>b.header.slotNumber;
183
- while(checkpoint && slot(checkpoint) >= start){
184
- if (slot(checkpoint) <= end) {
185
- // push the blocks on backwards
186
- const endBlock = checkpoint.startBlock + checkpoint.numBlocks - 1;
187
- for(let i = endBlock; i >= checkpoint.startBlock; i--){
188
- const block = await this.getBlock(BlockNumber(i));
189
- if (block) {
190
- blocks.push(block);
191
- }
192
- }
193
- }
194
- checkpoint = await this.store.getCheckpointData(CheckpointNumber(checkpoint.checkpointNumber - 1));
195
- }
196
- return blocks.reverse();
210
+ return this.stores.blocks.getCheckpointNumbersForSlotRange(start, end);
197
211
  }
198
- async getBlockHeadersForEpoch(epochNumber) {
199
- if (!this.l1Constants) {
200
- throw new Error('L1 constants not set');
212
+ async getBlock(query) {
213
+ const resolved = await this.resolveBlockQuery(query);
214
+ if (resolved === undefined) {
215
+ return undefined;
201
216
  }
202
- const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1Constants);
203
- const blocks = [];
204
- // Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
205
- // We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
206
- let checkpoint = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
207
- const slot = (b)=>b.header.slotNumber;
208
- while(checkpoint && slot(checkpoint) >= start){
209
- if (slot(checkpoint) <= end) {
210
- // push the blocks on backwards
211
- const endBlock = checkpoint.startBlock + checkpoint.numBlocks - 1;
212
- for(let i = endBlock; i >= checkpoint.startBlock; i--){
213
- const block = await this.getBlockHeader(BlockNumber(i));
214
- if (block) {
215
- blocks.push(block);
216
- }
217
- }
218
- }
219
- checkpoint = await this.store.getCheckpointData(CheckpointNumber(checkpoint.checkpointNumber - 1));
217
+ if (this.isGenesisBlockQuery(resolved)) {
218
+ return this.getGenesisBlock();
220
219
  }
221
- return blocks.reverse();
222
- }
223
- async getCheckpointsForEpoch(epochNumber) {
224
- if (!this.l1Constants) {
225
- throw new Error('L1 constants not set');
220
+ return this.stores.blocks.getBlock(resolved);
221
+ }
222
+ /**
223
+ * Range queries iterate physical blocks only; the genesis block is NOT prepended.
224
+ * `L2BlockStream` consumers (`world-state.handleL2Blocks`, etc.) emit `blocks-added` events for
225
+ * real blocks and would be surprised by a synthetic block 0. Use {@link getBlock} or
226
+ * {@link getBlockData} for genesis-aware single-block lookups.
227
+ */ async getBlocks(query) {
228
+ const resolved = await this.resolveBlocksQuery(query);
229
+ return resolved ? this.stores.blocks.getBlocks(resolved) : [];
230
+ }
231
+ async getBlockData(query) {
232
+ const resolved = await this.resolveBlockQuery(query);
233
+ if (resolved === undefined) {
234
+ return undefined;
226
235
  }
227
- const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1Constants);
228
- const checkpoints = [];
229
- // Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
230
- // We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
231
- let checkpointData = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
232
- const slot = (b)=>b.header.slotNumber;
233
- while(checkpointData && slot(checkpointData) >= start){
234
- if (slot(checkpointData) <= end) {
235
- // push the checkpoints on backwards
236
- const [checkpoint] = await this.getPublishedCheckpoints(checkpointData.checkpointNumber, 1);
237
- checkpoints.push(checkpoint.checkpoint);
238
- }
239
- checkpointData = await this.store.getCheckpointData(CheckpointNumber(checkpointData.checkpointNumber - 1));
236
+ if (this.isGenesisBlockQuery(resolved)) {
237
+ return this.getGenesisBlockData();
240
238
  }
241
- return checkpoints.reverse();
242
- }
243
- async getPublishedBlocks(from, limit, proven) {
244
- const checkpoints = await this.store.getRangeOfCheckpoints(CheckpointNumber(from), limit);
245
- const provenCheckpointNumber = await this.store.getProvenCheckpointNumber();
246
- const blocks = (await Promise.all(checkpoints.map((ch)=>this.store.getBlocksForCheckpoint(ch.checkpointNumber)))).filter(isDefined);
247
- const publishedBlocks = [];
248
- for(let i = 0; i < checkpoints.length; i++){
249
- const blockForCheckpoint = blocks[i][0];
250
- const checkpoint = checkpoints[i];
251
- if (checkpoint.checkpointNumber > provenCheckpointNumber && proven === true) {
252
- continue;
253
- }
254
- const publishedBlock = new CheckpointedL2Block(checkpoint.checkpointNumber, blockForCheckpoint, checkpoint.l1, checkpoint.attestations.map((x)=>CommitteeAttestation.fromBuffer(x)));
255
- publishedBlocks.push(publishedBlock);
239
+ return this.stores.blocks.getBlockData(resolved);
240
+ }
241
+ /** See {@link getBlocks} — range queries do not prepend the genesis block. */ async getBlocksData(query) {
242
+ const resolved = await this.resolveBlocksQuery(query);
243
+ return resolved ? this.stores.blocks.getBlocksData(resolved) : [];
244
+ }
245
+ /**
246
+ * Resolves a {@link BlockQuery} to either the genesis sentinel or a {@link ResolvedBlockQuery}
247
+ * understood by BlockStore. Detects every shape that points at block 0 — `{number:0}`,
248
+ * `{hash}` matching the initial header, `{archive}` matching the post-genesis archive root,
249
+ * and `{tag}` resolving to 0 and rewrites them to the sentinel so callers branch once.
250
+ */ async resolveBlockQuery(query) {
251
+ if ('number' in query) {
252
+ return query.number === BlockNumber.ZERO ? {
253
+ genesis: true
254
+ } : query;
256
255
  }
257
- return publishedBlocks;
258
- }
259
- async getBlock(number) {
260
- // If the number provided is -ve, then return the latest block.
261
- if (number < 0) {
262
- number = await this.store.getLatestBlockNumber();
256
+ if ('hash' in query) {
257
+ return query.hash.equals(this.initialBlockHash) ? {
258
+ genesis: true
259
+ } : query;
263
260
  }
264
- if (number === 0) {
265
- return undefined;
261
+ if ('archive' in query) {
262
+ return query.archive.equals(this.genesisArchiveRoot) ? {
263
+ genesis: true
264
+ } : query;
266
265
  }
267
- return this.store.getBlock(number);
268
- }
269
- async getBlocks(from, limit, proven) {
270
- const blocks = await this.store.getBlocks(from, limit);
271
- if (proven === true) {
272
- const provenBlockNumber = await this.store.getProvenBlockNumber();
273
- return blocks.filter((b)=>b.number <= provenBlockNumber);
266
+ const number = await this.resolveBlockTag(query.tag);
267
+ if (number === BlockNumber.ZERO) {
268
+ return {
269
+ genesis: true
270
+ };
274
271
  }
275
- return blocks;
276
- }
277
- getPublishedBlockByHash(blockHash) {
278
- return this.store.getCheckpointedBlockByHash(blockHash);
279
- }
280
- getPublishedBlockByArchive(archive) {
281
- return this.store.getCheckpointedBlockByArchive(archive);
272
+ return {
273
+ number
274
+ };
282
275
  }
283
- async getL2BlockNewByHash(blockHash) {
284
- const checkpointedBlock = await this.store.getCheckpointedBlockByHash(blockHash);
285
- return checkpointedBlock?.block;
276
+ /** Maps a {@link BlockTag} to the matching block number for the current chain state. */ resolveBlockTag(tag) {
277
+ switch(tag){
278
+ case 'latest':
279
+ case 'proposed':
280
+ return this.stores.blocks.getLatestL2BlockNumber();
281
+ case 'checkpointed':
282
+ return this.stores.blocks.getCheckpointedL2BlockNumber();
283
+ case 'proven':
284
+ return this.stores.blocks.getProvenBlockNumber();
285
+ case 'finalized':
286
+ return this.stores.blocks.getFinalizedL2BlockNumber();
287
+ }
286
288
  }
287
- async getL2BlockNewByArchive(archive) {
288
- const checkpointedBlock = await this.store.getCheckpointedBlockByArchive(archive);
289
- return checkpointedBlock?.block;
289
+ /**
290
+ * Converts an epoch-based BlocksQuery to a from/limit query using l1Constants.
291
+ * Returns undefined when the epoch has no checkpoints, so callers can return [] without
292
+ * entering BlockStore. Reads only the two endpoint checkpoints rather than the whole epoch.
293
+ */ async resolveBlocksQuery(query) {
294
+ if (!('epoch' in query)) {
295
+ if (query.from < INITIAL_L2_BLOCK_NUM) {
296
+ 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.`);
297
+ }
298
+ return query;
299
+ }
300
+ const checkpointNumbers = await this.getCheckpointNumbersForEpoch(query.epoch);
301
+ if (checkpointNumbers.length === 0) {
302
+ return undefined;
303
+ }
304
+ const firstNumber = checkpointNumbers[0];
305
+ const lastNumber = checkpointNumbers[checkpointNumbers.length - 1];
306
+ const first = await this.stores.blocks.getCheckpointData(firstNumber);
307
+ if (!first) {
308
+ return undefined;
309
+ }
310
+ const last = firstNumber === lastNumber ? first : await this.stores.blocks.getCheckpointData(lastNumber);
311
+ if (!last) {
312
+ return undefined;
313
+ }
314
+ const from = BlockNumber(first.startBlock);
315
+ const limit = last.startBlock + last.blockCount - first.startBlock;
316
+ return {
317
+ from,
318
+ limit,
319
+ onlyCheckpointed: true
320
+ };
290
321
  }
291
322
  }