@aztec/archiver 0.0.1-commit.b2a5d0dd1 → 0.0.1-commit.b3d3157a

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