@aztec/archiver 0.0.1-commit.42ee6df9b → 0.0.1-commit.431c48d

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