@aztec/archiver 5.0.0-private.20260319 → 5.0.0-rc.2

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 (121) hide show
  1. package/README.md +19 -13
  2. package/dest/archiver.d.ts +71 -15
  3. package/dest/archiver.d.ts.map +1 -1
  4. package/dest/archiver.js +268 -74
  5. package/dest/config.d.ts +7 -3
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +39 -16
  8. package/dest/errors.d.ts +44 -10
  9. package/dest/errors.d.ts.map +1 -1
  10. package/dest/errors.js +66 -12
  11. package/dest/factory.d.ts +12 -7
  12. package/dest/factory.d.ts.map +1 -1
  13. package/dest/factory.js +40 -25
  14. package/dest/index.d.ts +11 -3
  15. package/dest/index.d.ts.map +1 -1
  16. package/dest/index.js +10 -2
  17. package/dest/l1/calldata_retriever.d.ts +2 -1
  18. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  19. package/dest/l1/calldata_retriever.js +15 -5
  20. package/dest/l1/data_retrieval.d.ts +19 -10
  21. package/dest/l1/data_retrieval.d.ts.map +1 -1
  22. package/dest/l1/data_retrieval.js +25 -32
  23. package/dest/l1/trace_tx.d.ts +12 -66
  24. package/dest/l1/trace_tx.d.ts.map +1 -1
  25. package/dest/l1/validate_historical_logs.d.ts +23 -0
  26. package/dest/l1/validate_historical_logs.d.ts.map +1 -0
  27. package/dest/l1/validate_historical_logs.js +108 -0
  28. package/dest/modules/contract_data_source_adapter.d.ts +25 -0
  29. package/dest/modules/contract_data_source_adapter.d.ts.map +1 -0
  30. package/dest/modules/contract_data_source_adapter.js +32 -0
  31. package/dest/modules/data_source_base.d.ts +72 -48
  32. package/dest/modules/data_source_base.d.ts.map +1 -1
  33. package/dest/modules/data_source_base.js +266 -143
  34. package/dest/modules/data_store_updater.d.ts +30 -13
  35. package/dest/modules/data_store_updater.d.ts.map +1 -1
  36. package/dest/modules/data_store_updater.js +172 -117
  37. package/dest/modules/instrumentation.d.ts +7 -2
  38. package/dest/modules/instrumentation.d.ts.map +1 -1
  39. package/dest/modules/instrumentation.js +25 -7
  40. package/dest/modules/l1_synchronizer.d.ts +11 -6
  41. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  42. package/dest/modules/l1_synchronizer.js +437 -217
  43. package/dest/modules/outbox_trees_resolver.d.ts +62 -0
  44. package/dest/modules/outbox_trees_resolver.d.ts.map +1 -0
  45. package/dest/modules/outbox_trees_resolver.js +162 -0
  46. package/dest/modules/validation.d.ts +24 -5
  47. package/dest/modules/validation.d.ts.map +1 -1
  48. package/dest/modules/validation.js +40 -15
  49. package/dest/store/block_store.d.ts +164 -66
  50. package/dest/store/block_store.d.ts.map +1 -1
  51. package/dest/store/block_store.js +638 -233
  52. package/dest/store/contract_class_store.d.ts +17 -4
  53. package/dest/store/contract_class_store.d.ts.map +1 -1
  54. package/dest/store/contract_class_store.js +36 -68
  55. package/dest/store/contract_instance_store.d.ts +28 -1
  56. package/dest/store/contract_instance_store.d.ts.map +1 -1
  57. package/dest/store/contract_instance_store.js +48 -2
  58. package/dest/store/data_stores.d.ts +68 -0
  59. package/dest/store/data_stores.d.ts.map +1 -0
  60. package/dest/store/data_stores.js +54 -0
  61. package/dest/store/function_names_cache.d.ts +17 -0
  62. package/dest/store/function_names_cache.d.ts.map +1 -0
  63. package/dest/store/function_names_cache.js +30 -0
  64. package/dest/store/l2_tips_cache.d.ts +13 -7
  65. package/dest/store/l2_tips_cache.d.ts.map +1 -1
  66. package/dest/store/l2_tips_cache.js +13 -76
  67. package/dest/store/log_store.d.ts +42 -40
  68. package/dest/store/log_store.d.ts.map +1 -1
  69. package/dest/store/log_store.js +257 -480
  70. package/dest/store/log_store_codec.d.ts +78 -0
  71. package/dest/store/log_store_codec.d.ts.map +1 -0
  72. package/dest/store/log_store_codec.js +110 -0
  73. package/dest/store/message_store.d.ts +9 -3
  74. package/dest/store/message_store.d.ts.map +1 -1
  75. package/dest/store/message_store.js +39 -10
  76. package/dest/test/fake_l1_state.d.ts +15 -3
  77. package/dest/test/fake_l1_state.d.ts.map +1 -1
  78. package/dest/test/fake_l1_state.js +80 -18
  79. package/dest/test/mock_l2_block_source.d.ts +48 -50
  80. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  81. package/dest/test/mock_l2_block_source.js +223 -179
  82. package/dest/test/mock_structs.d.ts +1 -1
  83. package/dest/test/mock_structs.d.ts.map +1 -1
  84. package/dest/test/mock_structs.js +2 -2
  85. package/dest/test/noop_l1_archiver.d.ts +14 -6
  86. package/dest/test/noop_l1_archiver.d.ts.map +1 -1
  87. package/dest/test/noop_l1_archiver.js +25 -9
  88. package/package.json +14 -14
  89. package/src/archiver.ts +353 -80
  90. package/src/config.ts +43 -20
  91. package/src/errors.ts +103 -18
  92. package/src/factory.ts +56 -24
  93. package/src/index.ts +18 -2
  94. package/src/l1/calldata_retriever.ts +16 -5
  95. package/src/l1/data_retrieval.ts +38 -46
  96. package/src/l1/validate_historical_logs.ts +140 -0
  97. package/src/modules/contract_data_source_adapter.ts +46 -0
  98. package/src/modules/data_source_base.ts +340 -189
  99. package/src/modules/data_store_updater.ts +211 -148
  100. package/src/modules/instrumentation.ts +28 -8
  101. package/src/modules/l1_synchronizer.ts +583 -255
  102. package/src/modules/outbox_trees_resolver.ts +199 -0
  103. package/src/modules/validation.ts +81 -21
  104. package/src/store/block_store.ts +792 -285
  105. package/src/store/contract_class_store.ts +43 -103
  106. package/src/store/contract_instance_store.ts +62 -5
  107. package/src/store/data_stores.ts +104 -0
  108. package/src/store/function_names_cache.ts +37 -0
  109. package/src/store/l2_tips_cache.ts +16 -70
  110. package/src/store/log_store.ts +296 -652
  111. package/src/store/log_store_codec.ts +143 -0
  112. package/src/store/message_store.ts +43 -12
  113. package/src/structs/inbox_message.ts +1 -1
  114. package/src/test/fake_l1_state.ts +99 -27
  115. package/src/test/mock_l2_block_source.ts +279 -233
  116. package/src/test/mock_structs.ts +5 -2
  117. package/src/test/noop_l1_archiver.ts +52 -9
  118. package/dest/store/kv_archiver_store.d.ts +0 -367
  119. package/dest/store/kv_archiver_store.d.ts.map +0 -1
  120. package/dest/store/kv_archiver_store.js +0 -481
  121. package/src/store/kv_archiver_store.ts +0 -697
@@ -1,38 +1,133 @@
1
- import { range } from '@aztec/foundation/array';
2
- import { BlockNumber, CheckpointNumber, type EpochNumber, type SlotNumber } from '@aztec/foundation/branded-types';
1
+ import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
2
+ import {
3
+ BlockNumber,
4
+ CheckpointNumber,
5
+ type EpochNumber,
6
+ IndexWithinCheckpoint,
7
+ type SlotNumber,
8
+ } from '@aztec/foundation/branded-types';
3
9
  import type { Fr } from '@aztec/foundation/curves/bn254';
4
10
  import type { EthAddress } from '@aztec/foundation/eth-address';
5
- import { isDefined } from '@aztec/foundation/types';
6
11
  import type { FunctionSelector } from '@aztec/stdlib/abi';
7
12
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
8
- import { type BlockData, type BlockHash, CheckpointedL2Block, L2Block, type L2Tips } from '@aztec/stdlib/block';
9
- import { Checkpoint, type CheckpointData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
13
+ import {
14
+ type BlockData,
15
+ type BlockHash,
16
+ type BlockQuery,
17
+ type BlockTag,
18
+ type BlocksQuery,
19
+ Body,
20
+ type CheckpointQuery,
21
+ type CheckpointsQuery,
22
+ L2Block,
23
+ type L2Tips,
24
+ type ProposedCheckpointQuery,
25
+ } from '@aztec/stdlib/block';
26
+ import {
27
+ Checkpoint,
28
+ type CheckpointData,
29
+ type ProposedCheckpointData,
30
+ PublishedCheckpoint,
31
+ } from '@aztec/stdlib/checkpoint';
10
32
  import type { ContractClassPublic, ContractDataSource, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
11
- import { type L1RollupConstants, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
12
- import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
33
+ import {
34
+ type L1RollupConstants,
35
+ getEpochAtSlot,
36
+ getEpochNumberAtTimestamp,
37
+ getLastL1SlotTimestampForL2Slot,
38
+ getProofSubmissionDeadlineEpoch,
39
+ getSlotRangeForEpoch,
40
+ } from '@aztec/stdlib/epoch-helpers';
13
41
  import type { L2LogsSource } from '@aztec/stdlib/interfaces/server';
14
- import type { LogFilter, SiloedTag, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
15
- import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
16
- import type { CheckpointHeader } from '@aztec/stdlib/rollup';
17
- import type { BlockHeader, IndexedTxEffect, TxHash, TxReceipt } from '@aztec/stdlib/tx';
42
+ import type { LogResult, PrivateLogsQuery, PublicLogsQuery } from '@aztec/stdlib/logs';
43
+ import type { L1ToL2MessageSource, L2ToL1MembershipWitness } from '@aztec/stdlib/messaging';
44
+ import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
45
+ import type { BlockHeader, IndexedTxEffect, TxHash } from '@aztec/stdlib/tx';
18
46
  import type { UInt64 } from '@aztec/stdlib/types';
19
47
 
20
48
  import type { ArchiverDataSource } from '../interfaces.js';
21
- import type { KVArchiverDataStore } from '../store/kv_archiver_store.js';
49
+ import type { ResolvedBlockQuery, ResolvedBlocksQuery } from '../store/block_store.js';
50
+ import type { ArchiverDataStores } from '../store/data_stores.js';
22
51
  import type { ValidateCheckpointResult } from './validation.js';
23
52
 
24
53
  /**
25
- * Abstract base class implementing ArchiverDataSource using a KVArchiverDataStore.
26
- * Provides implementations for all store-delegating methods and declares abstract methods
27
- * for L1-dependent functionality that subclasses must implement.
54
+ * Sentinel returned by {@link ArchiverDataSourceBase#resolveBlockQuery} when a query resolves
55
+ * to the genesis block. Forces single-block lookup methods to take the genesis branch
56
+ * explicitly rather than silently falling through to the BlockStore (which never has a block 0).
57
+ */
58
+ type GenesisBlockQuery = { genesis: true };
59
+
60
+ /**
61
+ * Abstract base class implementing ArchiverDataSource using a bundle of archiver substores.
62
+ * Provides implementations for all read-side methods and declares abstract methods for
63
+ * L1-dependent functionality that subclasses must implement.
28
64
  */
29
65
  export abstract class ArchiverDataSourceBase
30
66
  implements ArchiverDataSource, L2LogsSource, ContractDataSource, L1ToL2MessageSource
31
67
  {
68
+ /** The injected genesis block header. */
69
+ protected readonly initialHeader: BlockHeader;
70
+ /** Precomputed hash of the initial header, exposed via {@link getGenesisBlockHash}. */
71
+ protected readonly initialBlockHash: BlockHash;
72
+ /** Archive root after block 0 was appended — read from L1 (`Rollup.getGenesisArchiveTreeRoot`). */
73
+ protected readonly genesisArchiveRoot: Fr;
74
+
75
+ /** Memoized synthetic genesis block — callers rely on referential identity for caching. */
76
+ private readonly genesisBlock: L2Block;
77
+ /** Memoized synthetic genesis block data — kept consistent with {@link genesisBlock}. */
78
+ private readonly genesisBlockData: BlockData;
79
+
32
80
  constructor(
33
- protected readonly store: KVArchiverDataStore,
34
- protected readonly l1Constants?: L1RollupConstants,
35
- ) {}
81
+ protected readonly stores: ArchiverDataStores,
82
+ protected readonly l1Constants: L1RollupConstants | undefined,
83
+ initialHeader: BlockHeader,
84
+ initialBlockHash: BlockHash,
85
+ genesisArchiveRoot: Fr,
86
+ ) {
87
+ this.initialHeader = initialHeader;
88
+ this.initialBlockHash = initialBlockHash;
89
+ this.genesisArchiveRoot = genesisArchiveRoot;
90
+
91
+ const genesisArchive = new AppendOnlyTreeSnapshot(genesisArchiveRoot, 1);
92
+ this.genesisBlock = new L2Block(
93
+ genesisArchive,
94
+ initialHeader,
95
+ Body.empty(),
96
+ CheckpointNumber.ZERO,
97
+ IndexWithinCheckpoint(0),
98
+ );
99
+ this.genesisBlockData = {
100
+ header: initialHeader,
101
+ archive: genesisArchive,
102
+ blockHash: initialBlockHash,
103
+ checkpointNumber: CheckpointNumber.ZERO,
104
+ indexWithinCheckpoint: IndexWithinCheckpoint(0),
105
+ };
106
+ }
107
+
108
+ /** Returns the precomputed hash of the genesis block header. */
109
+ public getGenesisBlockHash(): BlockHash {
110
+ return this.initialBlockHash;
111
+ }
112
+
113
+ /** Returns the synthetic genesis L2Block (memoized — same instance across calls). */
114
+ private getGenesisBlock(): L2Block {
115
+ return this.genesisBlock;
116
+ }
117
+
118
+ /** Returns genesis block data (memoized — same instance across calls). */
119
+ private getGenesisBlockData(): BlockData {
120
+ return this.genesisBlockData;
121
+ }
122
+
123
+ /**
124
+ * Type guard distinguishing the genesis sentinel from a {@link ResolvedBlockQuery}.
125
+ * `resolveBlockQuery` already rewrites every genesis-matching shape to the sentinel,
126
+ * so callers only need this single sync check.
127
+ */
128
+ private isGenesisBlockQuery(query: ResolvedBlockQuery | GenesisBlockQuery): query is GenesisBlockQuery {
129
+ return 'genesis' in query;
130
+ }
36
131
 
37
132
  abstract getRollupAddress(): Promise<EthAddress>;
38
133
 
@@ -54,107 +149,147 @@ export abstract class ArchiverDataSourceBase
54
149
 
55
150
  abstract syncImmediate(): Promise<void>;
56
151
 
57
- public getCheckpointNumber(): Promise<CheckpointNumber> {
58
- return this.store.getSynchedCheckpointNumber();
59
- }
152
+ abstract getL2ToL1MembershipWitness(
153
+ txHash: TxHash,
154
+ message: Fr,
155
+ messageIndexInTx?: number,
156
+ ): Promise<L2ToL1MembershipWitness | undefined>;
60
157
 
61
- public getSynchedCheckpointNumber(): Promise<CheckpointNumber> {
62
- return this.store.getSynchedCheckpointNumber();
63
- }
64
-
65
- public getProvenCheckpointNumber(): Promise<CheckpointNumber> {
66
- return this.store.getProvenCheckpointNumber();
67
- }
68
-
69
- public getBlockNumber(): Promise<BlockNumber> {
70
- return this.store.getLatestBlockNumber();
71
- }
72
-
73
- public getProvenBlockNumber(): Promise<BlockNumber> {
74
- return this.store.getProvenBlockNumber();
75
- }
158
+ public async isPruneDueAtSlot(slot: SlotNumber): Promise<boolean> {
159
+ if (!this.l1Constants) {
160
+ throw new Error('isPruneDueAtSlot requires l1Constants');
161
+ }
162
+ const tips = await this.getL2Tips();
163
+ const proven = tips.proven.checkpoint.number;
164
+ const pending = tips.checkpointed.checkpoint.number;
165
+ if (pending === proven) {
166
+ return false;
167
+ }
76
168
 
77
- public async getBlockHeader(number: BlockNumber | 'latest'): Promise<BlockHeader | undefined> {
78
- const blockNumber = number === 'latest' ? await this.store.getLatestBlockNumber() : number;
79
- if (blockNumber === 0) {
80
- return undefined;
169
+ const oldestUnproven = await this.getCheckpointData({ number: CheckpointNumber(Number(proven) + 1) });
170
+ if (!oldestUnproven) {
171
+ return false;
81
172
  }
82
- const headers = await this.store.getBlockHeaders(blockNumber, 1);
83
- return headers.length === 0 ? undefined : headers[0];
84
- }
85
173
 
86
- public getCheckpointedBlock(number: BlockNumber): Promise<CheckpointedL2Block | undefined> {
87
- return this.store.getCheckpointedBlock(number);
174
+ const slotTs = getLastL1SlotTimestampForL2Slot(slot, this.l1Constants);
175
+ const slotEpoch = getEpochNumberAtTimestamp(slotTs, this.l1Constants);
176
+ const oldestUnprovenEpoch = getEpochAtSlot(oldestUnproven.header.slotNumber, this.l1Constants);
177
+ const deadlineEpoch = getProofSubmissionDeadlineEpoch(oldestUnprovenEpoch, this.l1Constants);
178
+ return slotEpoch >= deadlineEpoch;
88
179
  }
89
180
 
90
- public getCheckpointedL2BlockNumber(): Promise<BlockNumber> {
91
- return this.store.getCheckpointedL2BlockNumber();
181
+ public getCheckpointNumber(): Promise<CheckpointNumber> {
182
+ return this.stores.blocks.getLatestCheckpointNumber();
92
183
  }
93
184
 
94
- public getFinalizedL2BlockNumber(): Promise<BlockNumber> {
95
- return this.store.getFinalizedL2BlockNumber();
185
+ public getProvenCheckpointNumber(): Promise<CheckpointNumber> {
186
+ return this.stores.blocks.getProvenCheckpointNumber();
96
187
  }
97
188
 
98
- public async getCheckpointHeader(number: CheckpointNumber | 'latest'): Promise<CheckpointHeader | undefined> {
99
- if (number === 'latest') {
100
- number = await this.store.getSynchedCheckpointNumber();
189
+ public getBlockNumber(): Promise<BlockNumber>;
190
+ public getBlockNumber(query: BlockQuery): Promise<BlockNumber | undefined>;
191
+ public async getBlockNumber(query?: BlockQuery): Promise<BlockNumber | undefined> {
192
+ if (!query) {
193
+ return this.stores.blocks.getLatestL2BlockNumber();
101
194
  }
102
- if (number === 0) {
195
+ const resolved = await this.resolveBlockQuery(query);
196
+ if (resolved === undefined) {
103
197
  return undefined;
104
198
  }
105
- const checkpoint = await this.store.getCheckpointData(number);
106
- if (!checkpoint) {
107
- return undefined;
199
+ if (this.isGenesisBlockQuery(resolved)) {
200
+ return BlockNumber.ZERO;
108
201
  }
109
- return checkpoint.header;
202
+ return this.stores.blocks.getBlockNumber(resolved);
110
203
  }
111
204
 
112
- public async getLastBlockNumberInCheckpoint(checkpointNumber: CheckpointNumber): Promise<BlockNumber | undefined> {
113
- const checkpointData = await this.store.getCheckpointData(checkpointNumber);
114
- if (!checkpointData) {
115
- return undefined;
205
+ /**
206
+ * Resolves a {@link CheckpointQuery} to a concrete `CheckpointNumber`, or undefined when the
207
+ * query refers to a position that has no checkpoint yet (e.g. `{ slot }` not found).
208
+ */
209
+ private resolveCheckpointQuery(query: CheckpointQuery): Promise<CheckpointNumber | undefined> {
210
+ if ('number' in query) {
211
+ return Promise.resolve(query.number);
212
+ }
213
+ if ('slot' in query) {
214
+ return this.stores.blocks.getCheckpointNumberBySlot(query.slot);
215
+ }
216
+ // tag variant
217
+ switch (query.tag) {
218
+ case 'checkpointed':
219
+ return this.stores.blocks.getLatestCheckpointNumber();
220
+ case 'proven':
221
+ return this.stores.blocks.getProvenCheckpointNumber();
222
+ case 'finalized':
223
+ return this.stores.blocks.getFinalizedCheckpointNumber();
116
224
  }
117
- return BlockNumber(checkpointData.startBlock + checkpointData.blockCount - 1);
118
225
  }
119
226
 
120
- public getCheckpointedBlocks(from: BlockNumber, limit: number): Promise<CheckpointedL2Block[]> {
121
- return this.store.getCheckpointedBlocks(from, limit);
227
+ /**
228
+ * Resolves a {@link CheckpointsQuery} to a concrete `{from, limit}` pair used by BlockStore,
229
+ * or undefined when the epoch has no checkpoints.
230
+ */
231
+ private async resolveCheckpointsQuery(
232
+ query: CheckpointsQuery,
233
+ ): Promise<{ from: CheckpointNumber; limit: number } | undefined> {
234
+ if ('from' in query) {
235
+ return query;
236
+ }
237
+ const numbers = await this.getCheckpointNumbersForEpoch(query.epoch);
238
+ if (numbers.length === 0) {
239
+ return undefined;
240
+ }
241
+ return { from: numbers[0], limit: numbers.length };
122
242
  }
123
243
 
124
- public getBlockHeaderByHash(blockHash: BlockHash): Promise<BlockHeader | undefined> {
125
- return this.store.getBlockHeaderByHash(blockHash);
244
+ public async getCheckpoint(query: CheckpointQuery): Promise<PublishedCheckpoint | undefined> {
245
+ const number = await this.resolveCheckpointQuery(query);
246
+ if (number === undefined || number === 0) {
247
+ return undefined;
248
+ }
249
+ const data = await this.stores.blocks.getCheckpointData(number);
250
+ if (!data) {
251
+ return undefined;
252
+ }
253
+ return this.getPublishedCheckpointFromCheckpointData(data);
126
254
  }
127
255
 
128
- public getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
129
- return this.store.getBlockHeaderByArchive(archive);
256
+ public async getCheckpoints(query: CheckpointsQuery): Promise<PublishedCheckpoint[]> {
257
+ const resolved = await this.resolveCheckpointsQuery(query);
258
+ if (!resolved) {
259
+ return [];
260
+ }
261
+ const checkpoints = await this.stores.blocks.getRangeOfCheckpoints(resolved.from, resolved.limit);
262
+ return Promise.all(checkpoints.map(ch => this.getPublishedCheckpointFromCheckpointData(ch)));
130
263
  }
131
264
 
132
- public getBlockData(number: BlockNumber): Promise<BlockData | undefined> {
133
- return this.store.getBlockData(number);
265
+ public async getCheckpointData(query: CheckpointQuery): Promise<CheckpointData | undefined> {
266
+ const number = await this.resolveCheckpointQuery(query);
267
+ if (number === undefined || number === 0) {
268
+ return undefined;
269
+ }
270
+ return this.stores.blocks.getCheckpointData(number);
134
271
  }
135
272
 
136
- public getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
137
- return this.store.getBlockDataByArchive(archive);
273
+ public async getCheckpointsData(query: CheckpointsQuery): Promise<CheckpointData[]> {
274
+ const resolved = await this.resolveCheckpointsQuery(query);
275
+ if (!resolved) {
276
+ return [];
277
+ }
278
+ return this.stores.blocks.getRangeOfCheckpoints(resolved.from, resolved.limit);
138
279
  }
139
280
 
140
- public async getL2Block(number: BlockNumber): Promise<L2Block | undefined> {
141
- // If the number provided is -ve, then return the latest block.
142
- if (number < 0) {
143
- number = await this.store.getLatestBlockNumber();
281
+ public getProposedCheckpointData(query?: ProposedCheckpointQuery): Promise<ProposedCheckpointData | undefined> {
282
+ if (!query || 'tag' in query) {
283
+ return this.stores.blocks.getLastProposedCheckpoint();
144
284
  }
145
- if (number === 0) {
146
- return undefined;
285
+ if ('number' in query) {
286
+ return this.stores.blocks.getProposedCheckpointByNumber(query.number);
147
287
  }
148
- const publishedBlock = await this.store.getBlock(number);
149
- return publishedBlock;
288
+ return this.stores.blocks.getProposedCheckpointBySlot(query.slot);
150
289
  }
151
290
 
152
291
  public getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
153
- return this.store.getTxEffect(txHash);
154
- }
155
-
156
- public getSettledTxReceipt(txHash: TxHash): Promise<TxReceipt | undefined> {
157
- return this.store.getSettledTxReceipt(txHash, this.l1Constants);
292
+ return this.stores.blocks.getTxEffect(txHash);
158
293
  }
159
294
 
160
295
  public isPendingChainInvalid(): Promise<boolean> {
@@ -162,85 +297,53 @@ export abstract class ArchiverDataSourceBase
162
297
  }
163
298
 
164
299
  public async getPendingChainValidationStatus(): Promise<ValidateCheckpointResult> {
165
- return (await this.store.getPendingChainValidationStatus()) ?? { valid: true };
166
- }
167
-
168
- public getPrivateLogsByTags(
169
- tags: SiloedTag[],
170
- page?: number,
171
- upToBlockNumber?: BlockNumber,
172
- ): Promise<TxScopedL2Log[][]> {
173
- return this.store.getPrivateLogsByTags(tags, page, upToBlockNumber);
174
- }
175
-
176
- public getPublicLogsByTagsFromContract(
177
- contractAddress: AztecAddress,
178
- tags: Tag[],
179
- page?: number,
180
- upToBlockNumber?: BlockNumber,
181
- ): Promise<TxScopedL2Log[][]> {
182
- return this.store.getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber);
300
+ return (await this.stores.blocks.getPendingChainValidationStatus()) ?? { valid: true };
183
301
  }
184
302
 
185
- public getPublicLogs(filter: LogFilter): Promise<GetPublicLogsResponse> {
186
- return this.store.getPublicLogs(filter);
303
+ public getPrivateLogsByTags(query: PrivateLogsQuery): Promise<LogResult[][]> {
304
+ return this.stores.logs.getPrivateLogsByTags(query);
187
305
  }
188
306
 
189
- public getContractClassLogs(filter: LogFilter): Promise<GetContractClassLogsResponse> {
190
- return this.store.getContractClassLogs(filter);
307
+ public getPublicLogsByTags(query: PublicLogsQuery): Promise<LogResult[][]> {
308
+ return this.stores.logs.getPublicLogsByTags(query);
191
309
  }
192
310
 
193
311
  public getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
194
- return this.store.getContractClass(id);
312
+ return this.stores.contractClasses.getContractClass(id);
195
313
  }
196
314
 
197
315
  public getBytecodeCommitment(id: Fr): Promise<Fr | undefined> {
198
- return this.store.getBytecodeCommitment(id);
316
+ return this.stores.contractClasses.getBytecodeCommitment(id);
199
317
  }
200
318
 
201
- public async getContract(
202
- address: AztecAddress,
203
- maybeTimestamp?: UInt64,
204
- ): Promise<ContractInstanceWithAddress | undefined> {
205
- let timestamp;
206
- if (maybeTimestamp === undefined) {
207
- const latestBlockHeader = await this.getBlockHeader('latest');
208
- // If we get undefined block header, it means that the archiver has not yet synced any block so we default to 0.
209
- timestamp = latestBlockHeader ? latestBlockHeader.globalVariables.timestamp : 0n;
210
- } else {
211
- timestamp = maybeTimestamp;
212
- }
213
-
214
- return this.store.getContractInstance(address, timestamp);
319
+ public getContract(address: AztecAddress, timestamp: UInt64): Promise<ContractInstanceWithAddress | undefined> {
320
+ return this.stores.contractInstances.getContractInstance(address, timestamp);
215
321
  }
216
322
 
217
323
  public getContractClassIds(): Promise<Fr[]> {
218
- return this.store.getContractClassIds();
324
+ return this.stores.contractClasses.getContractClassIds();
219
325
  }
220
326
 
221
- public getDebugFunctionName(address: AztecAddress, selector: FunctionSelector): Promise<string | undefined> {
222
- return this.store.getDebugFunctionName(address, selector);
327
+ /** Looks up a public function name given a selector. */
328
+ public getDebugFunctionName(_address: AztecAddress, selector: FunctionSelector): Promise<string | undefined> {
329
+ return Promise.resolve(this.stores.functionNames.get(selector));
223
330
  }
224
331
 
332
+ /** Register public function signatures so they can be looked up by selector. */
225
333
  public registerContractFunctionSignatures(signatures: string[]): Promise<void> {
226
- return this.store.registerContractFunctionSignatures(signatures);
334
+ return this.stores.functionNames.register(signatures);
227
335
  }
228
336
 
229
337
  public getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise<Fr[]> {
230
- return this.store.getL1ToL2Messages(checkpointNumber);
338
+ return this.stores.messages.getL1ToL2Messages(checkpointNumber);
231
339
  }
232
340
 
233
341
  public getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise<bigint | undefined> {
234
- return this.store.getL1ToL2MessageIndex(l1ToL2Message);
235
- }
236
-
237
- public async getCheckpoints(checkpointNumber: CheckpointNumber, limit: number): Promise<PublishedCheckpoint[]> {
238
- const checkpoints = await this.store.getRangeOfCheckpoints(checkpointNumber, limit);
239
- return Promise.all(checkpoints.map(ch => this.getPublishedCheckpointFromCheckpointData(ch)));
342
+ return this.stores.messages.getL1ToL2MessageIndex(l1ToL2Message);
240
343
  }
241
344
 
242
345
  private async getPublishedCheckpointFromCheckpointData(checkpoint: CheckpointData): Promise<PublishedCheckpoint> {
243
- const blocksForCheckpoint = await this.store.getBlocksForCheckpoint(checkpoint.checkpointNumber);
346
+ const blocksForCheckpoint = await this.stores.blocks.getBlocksForCheckpoint(checkpoint.checkpointNumber);
244
347
  if (!blocksForCheckpoint) {
245
348
  throw new Error(`Blocks for checkpoint ${checkpoint.checkpointNumber} not found`);
246
349
  }
@@ -249,85 +352,133 @@ export abstract class ArchiverDataSourceBase
249
352
  checkpoint.header,
250
353
  blocksForCheckpoint,
251
354
  checkpoint.checkpointNumber,
355
+ checkpoint.feeAssetPriceModifier,
252
356
  );
253
357
  return new PublishedCheckpoint(fullCheckpoint, checkpoint.l1, checkpoint.attestations);
254
358
  }
255
359
 
256
360
  public getBlocksForSlot(slotNumber: SlotNumber): Promise<L2Block[]> {
257
- return this.store.getBlocksForSlot(slotNumber);
258
- }
259
-
260
- public async getCheckpointedBlocksForEpoch(epochNumber: EpochNumber): Promise<CheckpointedL2Block[]> {
261
- const checkpointsData = await this.getCheckpointsDataForEpoch(epochNumber);
262
- const blocks = await Promise.all(
263
- checkpointsData.flatMap(checkpoint =>
264
- range(checkpoint.blockCount, checkpoint.startBlock).map(blockNumber =>
265
- this.getCheckpointedBlock(BlockNumber(blockNumber)),
266
- ),
267
- ),
268
- );
269
- return blocks.filter(isDefined);
270
- }
271
-
272
- public async getCheckpointedBlockHeadersForEpoch(epochNumber: EpochNumber): Promise<BlockHeader[]> {
273
- const checkpointsData = await this.getCheckpointsDataForEpoch(epochNumber);
274
- const blocks = await Promise.all(
275
- checkpointsData.flatMap(checkpoint =>
276
- range(checkpoint.blockCount, checkpoint.startBlock).map(blockNumber =>
277
- this.getBlockHeader(BlockNumber(blockNumber)),
278
- ),
279
- ),
280
- );
281
- return blocks.filter(isDefined);
282
- }
283
-
284
- public async getCheckpointsForEpoch(epochNumber: EpochNumber): Promise<Checkpoint[]> {
285
- const checkpointsData = await this.getCheckpointsDataForEpoch(epochNumber);
286
- return Promise.all(
287
- checkpointsData.map(data => this.getPublishedCheckpointFromCheckpointData(data).then(p => p.checkpoint)),
288
- );
361
+ return this.stores.blocks.getBlocksForSlot(slotNumber);
289
362
  }
290
363
 
291
- /** Returns checkpoint data for all checkpoints whose slot falls within the given epoch. */
292
- public getCheckpointsDataForEpoch(epochNumber: EpochNumber): Promise<CheckpointData[]> {
364
+ /** Returns just the checkpoint numbers for all checkpoints whose slot falls within the given epoch. */
365
+ private getCheckpointNumbersForEpoch(epochNumber: EpochNumber): Promise<CheckpointNumber[]> {
293
366
  if (!this.l1Constants) {
294
367
  throw new Error('L1 constants not set');
295
368
  }
296
369
 
297
370
  const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1Constants);
298
- return this.store.getCheckpointDataForSlotRange(start, end);
371
+ return this.stores.blocks.getCheckpointNumbersForSlotRange(start, end);
299
372
  }
300
373
 
301
- public async getBlock(number: BlockNumber): Promise<L2Block | undefined> {
302
- // If the number provided is -ve, then return the latest block.
303
- if (number < 0) {
304
- number = await this.store.getLatestBlockNumber();
305
- }
306
- if (number === 0) {
374
+ public async getBlock(query: BlockQuery): Promise<L2Block | undefined> {
375
+ const resolved = await this.resolveBlockQuery(query);
376
+ if (resolved === undefined) {
307
377
  return undefined;
308
378
  }
309
- return this.store.getBlock(number);
379
+ if (this.isGenesisBlockQuery(resolved)) {
380
+ return this.getGenesisBlock();
381
+ }
382
+ return this.stores.blocks.getBlock(resolved);
310
383
  }
311
384
 
312
- public getBlocks(from: BlockNumber, limit: number): Promise<L2Block[]> {
313
- return this.store.getBlocks(from, limit);
385
+ /**
386
+ * Range queries iterate physical blocks only; the genesis block is NOT prepended.
387
+ * `L2BlockStream` consumers (`world-state.handleL2Blocks`, etc.) emit `blocks-added` events for
388
+ * real blocks and would be surprised by a synthetic block 0. Use {@link getBlock} or
389
+ * {@link getBlockData} for genesis-aware single-block lookups.
390
+ */
391
+ public async getBlocks(query: BlocksQuery): Promise<L2Block[]> {
392
+ const resolved = await this.resolveBlocksQuery(query);
393
+ return resolved ? this.stores.blocks.getBlocks(resolved) : [];
314
394
  }
315
395
 
316
- public getCheckpointedBlockByHash(blockHash: BlockHash): Promise<CheckpointedL2Block | undefined> {
317
- return this.store.getCheckpointedBlockByHash(blockHash);
396
+ public async getBlockData(query: BlockQuery): Promise<BlockData | undefined> {
397
+ const resolved = await this.resolveBlockQuery(query);
398
+ if (resolved === undefined) {
399
+ return undefined;
400
+ }
401
+ if (this.isGenesisBlockQuery(resolved)) {
402
+ return this.getGenesisBlockData();
403
+ }
404
+ return this.stores.blocks.getBlockData(resolved);
318
405
  }
319
406
 
320
- public getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
321
- return this.store.getCheckpointedBlockByArchive(archive);
407
+ /** See {@link getBlocks} range queries do not prepend the genesis block. */
408
+ public async getBlocksData(query: BlocksQuery): Promise<BlockData[]> {
409
+ const resolved = await this.resolveBlocksQuery(query);
410
+ return resolved ? this.stores.blocks.getBlocksData(resolved) : [];
322
411
  }
323
412
 
324
- public async getL2BlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
325
- const checkpointedBlock = await this.store.getCheckpointedBlockByHash(blockHash);
326
- return checkpointedBlock?.block;
413
+ /**
414
+ * Resolves a {@link BlockQuery} to either the genesis sentinel or a {@link ResolvedBlockQuery}
415
+ * understood by BlockStore. Detects every shape that points at block 0 — `{number:0}`,
416
+ * `{hash}` matching the initial header, `{archive}` matching the post-genesis archive root,
417
+ * and `{tag}` resolving to 0 — and rewrites them to the sentinel so callers branch once.
418
+ */
419
+ private async resolveBlockQuery(query: BlockQuery): Promise<ResolvedBlockQuery | GenesisBlockQuery | undefined> {
420
+ if ('number' in query) {
421
+ return query.number === BlockNumber.ZERO ? { genesis: true } : query;
422
+ }
423
+ if ('hash' in query) {
424
+ return query.hash.equals(this.initialBlockHash) ? { genesis: true } : query;
425
+ }
426
+ if ('archive' in query) {
427
+ return query.archive.equals(this.genesisArchiveRoot) ? { genesis: true } : query;
428
+ }
429
+ const number = await this.resolveBlockTag(query.tag);
430
+ if (number === BlockNumber.ZERO) {
431
+ return { genesis: true };
432
+ }
433
+ return { number };
434
+ }
435
+
436
+ /** Maps a {@link BlockTag} to the matching block number for the current chain state. */
437
+ private resolveBlockTag(tag: BlockTag): Promise<BlockNumber> {
438
+ switch (tag) {
439
+ case 'latest':
440
+ case 'proposed':
441
+ return this.stores.blocks.getLatestL2BlockNumber();
442
+ case 'checkpointed':
443
+ return this.stores.blocks.getCheckpointedL2BlockNumber();
444
+ case 'proven':
445
+ return this.stores.blocks.getProvenBlockNumber();
446
+ case 'finalized':
447
+ return this.stores.blocks.getFinalizedL2BlockNumber();
448
+ }
327
449
  }
328
450
 
329
- public async getL2BlockByArchive(archive: Fr): Promise<L2Block | undefined> {
330
- const checkpointedBlock = await this.store.getCheckpointedBlockByArchive(archive);
331
- return checkpointedBlock?.block;
451
+ /**
452
+ * Converts an epoch-based BlocksQuery to a from/limit query using l1Constants.
453
+ * Returns undefined when the epoch has no checkpoints, so callers can return [] without
454
+ * entering BlockStore. Reads only the two endpoint checkpoints rather than the whole epoch.
455
+ */
456
+ private async resolveBlocksQuery(query: BlocksQuery): Promise<ResolvedBlocksQuery | undefined> {
457
+ if (!('epoch' in query)) {
458
+ if (query.from < INITIAL_L2_BLOCK_NUM) {
459
+ throw new Error(
460
+ `getBlocks/getBlocksData: 'from' must be >= ${INITIAL_L2_BLOCK_NUM}, got ${query.from}. ` +
461
+ `Use getBlock({number:0})/getBlockData({number:0}) for genesis-aware single-block lookups.`,
462
+ );
463
+ }
464
+ return query;
465
+ }
466
+ const checkpointNumbers = await this.getCheckpointNumbersForEpoch(query.epoch);
467
+ if (checkpointNumbers.length === 0) {
468
+ return undefined;
469
+ }
470
+ const firstNumber = checkpointNumbers[0];
471
+ const lastNumber = checkpointNumbers[checkpointNumbers.length - 1];
472
+ const first = await this.stores.blocks.getCheckpointData(firstNumber);
473
+ if (!first) {
474
+ return undefined;
475
+ }
476
+ const last = firstNumber === lastNumber ? first : await this.stores.blocks.getCheckpointData(lastNumber);
477
+ if (!last) {
478
+ return undefined;
479
+ }
480
+ const from = BlockNumber(first.startBlock);
481
+ const limit = last.startBlock + last.blockCount - first.startBlock;
482
+ return { from, limit, onlyCheckpointed: true };
332
483
  }
333
484
  }