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

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