@aztec/archiver 0.0.1-commit.d431d1c → 0.0.1-commit.d58ff9d0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/README.md +28 -11
  2. package/dest/archiver.d.ts +80 -21
  3. package/dest/archiver.d.ts.map +1 -1
  4. package/dest/archiver.js +347 -167
  5. package/dest/config.d.ts +7 -3
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +38 -14
  8. package/dest/errors.d.ts +60 -9
  9. package/dest/errors.d.ts.map +1 -1
  10. package/dest/errors.js +88 -13
  11. package/dest/factory.d.ts +13 -6
  12. package/dest/factory.d.ts.map +1 -1
  13. package/dest/factory.js +59 -39
  14. package/dest/index.d.ts +12 -3
  15. package/dest/index.d.ts.map +1 -1
  16. package/dest/index.js +11 -2
  17. package/dest/l1/bin/retrieve-calldata.js +35 -32
  18. package/dest/l1/calldata_retriever.d.ts +81 -50
  19. package/dest/l1/calldata_retriever.d.ts.map +1 -1
  20. package/dest/l1/calldata_retriever.js +202 -260
  21. package/dest/l1/data_retrieval.d.ts +32 -18
  22. package/dest/l1/data_retrieval.d.ts.map +1 -1
  23. package/dest/l1/data_retrieval.js +45 -50
  24. package/dest/l1/spire_proposer.d.ts +5 -5
  25. package/dest/l1/spire_proposer.d.ts.map +1 -1
  26. package/dest/l1/spire_proposer.js +9 -17
  27. package/dest/l1/trace_tx.d.ts +12 -66
  28. package/dest/l1/trace_tx.d.ts.map +1 -1
  29. package/dest/l1/validate_historical_logs.d.ts +23 -0
  30. package/dest/l1/validate_historical_logs.d.ts.map +1 -0
  31. package/dest/l1/validate_historical_logs.js +108 -0
  32. package/dest/l1/validate_trace.d.ts +6 -3
  33. package/dest/l1/validate_trace.d.ts.map +1 -1
  34. package/dest/l1/validate_trace.js +13 -9
  35. package/dest/modules/contract_data_source_adapter.d.ts +25 -0
  36. package/dest/modules/contract_data_source_adapter.d.ts.map +1 -0
  37. package/dest/modules/contract_data_source_adapter.js +32 -0
  38. package/dest/modules/data_source_base.d.ts +75 -48
  39. package/dest/modules/data_source_base.d.ts.map +1 -1
  40. package/dest/modules/data_source_base.js +251 -220
  41. package/dest/modules/data_store_updater.d.ts +65 -29
  42. package/dest/modules/data_store_updater.d.ts.map +1 -1
  43. package/dest/modules/data_store_updater.js +242 -153
  44. package/dest/modules/instrumentation.d.ts +30 -4
  45. package/dest/modules/instrumentation.d.ts.map +1 -1
  46. package/dest/modules/instrumentation.js +83 -20
  47. package/dest/modules/l1_synchronizer.d.ts +16 -14
  48. package/dest/modules/l1_synchronizer.d.ts.map +1 -1
  49. package/dest/modules/l1_synchronizer.js +493 -225
  50. package/dest/modules/outbox_trees_resolver.d.ts +62 -0
  51. package/dest/modules/outbox_trees_resolver.d.ts.map +1 -0
  52. package/dest/modules/outbox_trees_resolver.js +162 -0
  53. package/dest/modules/validation.d.ts +30 -7
  54. package/dest/modules/validation.d.ts.map +1 -1
  55. package/dest/modules/validation.js +35 -17
  56. package/dest/store/block_store.d.ts +200 -75
  57. package/dest/store/block_store.d.ts.map +1 -1
  58. package/dest/store/block_store.js +802 -276
  59. package/dest/store/contract_class_store.d.ts +17 -4
  60. package/dest/store/contract_class_store.d.ts.map +1 -1
  61. package/dest/store/contract_class_store.js +51 -73
  62. package/dest/store/contract_instance_store.d.ts +28 -1
  63. package/dest/store/contract_instance_store.d.ts.map +1 -1
  64. package/dest/store/contract_instance_store.js +54 -2
  65. package/dest/store/data_stores.d.ts +68 -0
  66. package/dest/store/data_stores.d.ts.map +1 -0
  67. package/dest/store/data_stores.js +54 -0
  68. package/dest/store/function_names_cache.d.ts +17 -0
  69. package/dest/store/function_names_cache.d.ts.map +1 -0
  70. package/dest/store/function_names_cache.js +30 -0
  71. package/dest/store/l2_tips_cache.d.ts +25 -0
  72. package/dest/store/l2_tips_cache.d.ts.map +1 -0
  73. package/dest/store/l2_tips_cache.js +26 -0
  74. package/dest/store/log_store.d.ts +42 -37
  75. package/dest/store/log_store.d.ts.map +1 -1
  76. package/dest/store/log_store.js +262 -388
  77. package/dest/store/log_store_codec.d.ts +78 -0
  78. package/dest/store/log_store_codec.d.ts.map +1 -0
  79. package/dest/store/log_store_codec.js +110 -0
  80. package/dest/store/message_store.d.ts +11 -1
  81. package/dest/store/message_store.d.ts.map +1 -1
  82. package/dest/store/message_store.js +51 -9
  83. package/dest/test/fake_l1_state.d.ts +28 -4
  84. package/dest/test/fake_l1_state.d.ts.map +1 -1
  85. package/dest/test/fake_l1_state.js +166 -32
  86. package/dest/test/index.js +3 -1
  87. package/dest/test/mock_archiver.d.ts +1 -1
  88. package/dest/test/mock_archiver.d.ts.map +1 -1
  89. package/dest/test/mock_archiver.js +3 -2
  90. package/dest/test/mock_l2_block_source.d.ts +65 -51
  91. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  92. package/dest/test/mock_l2_block_source.js +309 -215
  93. package/dest/test/mock_structs.d.ts +6 -2
  94. package/dest/test/mock_structs.d.ts.map +1 -1
  95. package/dest/test/mock_structs.js +26 -12
  96. package/dest/test/noop_l1_archiver.d.ts +34 -0
  97. package/dest/test/noop_l1_archiver.d.ts.map +1 -0
  98. package/dest/test/noop_l1_archiver.js +88 -0
  99. package/package.json +15 -14
  100. package/src/archiver.ts +458 -198
  101. package/src/config.ts +43 -13
  102. package/src/errors.ts +134 -21
  103. package/src/factory.ts +87 -36
  104. package/src/index.ts +19 -2
  105. package/src/l1/README.md +25 -68
  106. package/src/l1/bin/retrieve-calldata.ts +45 -33
  107. package/src/l1/calldata_retriever.ts +267 -379
  108. package/src/l1/data_retrieval.ts +70 -74
  109. package/src/l1/spire_proposer.ts +7 -15
  110. package/src/l1/validate_historical_logs.ts +140 -0
  111. package/src/l1/validate_trace.ts +24 -6
  112. package/src/modules/contract_data_source_adapter.ts +46 -0
  113. package/src/modules/data_source_base.ts +336 -288
  114. package/src/modules/data_store_updater.ts +287 -187
  115. package/src/modules/instrumentation.ts +86 -21
  116. package/src/modules/l1_synchronizer.ts +667 -278
  117. package/src/modules/outbox_trees_resolver.ts +199 -0
  118. package/src/modules/validation.ts +80 -23
  119. package/src/store/block_store.ts +984 -352
  120. package/src/store/contract_class_store.ts +57 -107
  121. package/src/store/contract_instance_store.ts +68 -5
  122. package/src/store/data_stores.ts +104 -0
  123. package/src/store/function_names_cache.ts +37 -0
  124. package/src/store/l2_tips_cache.ts +35 -0
  125. package/src/store/log_store.ts +305 -500
  126. package/src/store/log_store_codec.ts +143 -0
  127. package/src/store/message_store.ts +60 -10
  128. package/src/structs/inbox_message.ts +1 -1
  129. package/src/test/fake_l1_state.ts +215 -44
  130. package/src/test/index.ts +3 -0
  131. package/src/test/mock_archiver.ts +3 -2
  132. package/src/test/mock_l2_block_source.ts +381 -246
  133. package/src/test/mock_structs.ts +50 -17
  134. package/src/test/noop_l1_archiver.ts +158 -0
  135. package/dest/store/kv_archiver_store.d.ts +0 -336
  136. package/dest/store/kv_archiver_store.d.ts.map +0 -1
  137. package/dest/store/kv_archiver_store.js +0 -443
  138. package/src/store/kv_archiver_store.ts +0 -631
@@ -1,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 { CheckpointedL2Block, CommitteeAttestation, L2BlockNew, 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';
14
- import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
15
- import type { CheckpointHeader } from '@aztec/stdlib/rollup';
16
- 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';
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,113 +141,137 @@ 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
+ abstract getL2ToL1MembershipWitness(
153
+ txHash: TxHash,
154
+ message: Fr,
155
+ messageIndexInTx?: number,
156
+ ): Promise<L2ToL1MembershipWitness | undefined>;
60
157
 
61
- public getSynchedCheckpointNumber(): Promise<CheckpointNumber> {
62
- return this.store.getSynchedCheckpointNumber();
63
- }
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
+ }
64
168
 
65
- public getProvenCheckpointNumber(): Promise<CheckpointNumber> {
66
- return this.store.getProvenCheckpointNumber();
169
+ const oldestUnproven = await this.getCheckpointData({ number: CheckpointNumber(Number(proven) + 1) });
170
+ if (!oldestUnproven) {
171
+ return false;
172
+ }
173
+
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;
67
179
  }
68
180
 
69
- public getBlockNumber(): Promise<BlockNumber> {
70
- return this.store.getLatestBlockNumber();
181
+ public getCheckpointNumber(): Promise<CheckpointNumber> {
182
+ return this.stores.blocks.getLatestCheckpointNumber();
71
183
  }
72
184
 
73
- public getProvenBlockNumber(): Promise<BlockNumber> {
74
- return this.store.getProvenBlockNumber();
185
+ public getProvenCheckpointNumber(): Promise<CheckpointNumber> {
186
+ return this.stores.blocks.getProvenCheckpointNumber();
75
187
  }
76
188
 
77
- public async getBlockHeader(number: BlockNumber | 'latest'): Promise<BlockHeader | undefined> {
78
- const blockNumber = number === 'latest' ? await this.store.getLatestBlockNumber() : number;
79
- if (blockNumber === 0) {
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();
194
+ }
195
+ const resolved = await this.resolveBlockQuery(query);
196
+ if (resolved === undefined) {
80
197
  return undefined;
81
198
  }
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 getCheckpointedBlockNumber(): Promise<BlockNumber> {
91
- return this.store.getCheckpointedL2BlockNumber();
199
+ if (this.isGenesisBlockQuery(resolved)) {
200
+ return BlockNumber.ZERO;
201
+ }
202
+ return this.stores.blocks.getBlockNumber(resolved);
92
203
  }
93
204
 
94
- public async getCheckpointHeader(number: CheckpointNumber | 'latest'): Promise<CheckpointHeader | undefined> {
95
- if (number === 'latest') {
96
- number = await this.store.getSynchedCheckpointNumber();
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);
97
212
  }
98
- if (number === 0) {
99
- return undefined;
213
+ if ('slot' in query) {
214
+ return this.stores.blocks.getCheckpointNumberBySlot(query.slot);
100
215
  }
101
- const checkpoint = await this.store.getCheckpointData(number);
102
- if (!checkpoint) {
103
- return undefined;
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();
104
224
  }
105
- return checkpoint.header;
106
225
  }
107
226
 
108
- public async getLastBlockNumberInCheckpoint(checkpointNumber: CheckpointNumber): Promise<BlockNumber | undefined> {
109
- const checkpointData = await this.store.getCheckpointData(checkpointNumber);
110
- if (!checkpointData) {
227
+ public async getCheckpoint(query: CheckpointQuery): Promise<PublishedCheckpoint | undefined> {
228
+ const number = await this.resolveCheckpointQuery(query);
229
+ if (number === undefined || number === 0) {
111
230
  return undefined;
112
231
  }
113
- return BlockNumber(checkpointData.startBlock + checkpointData.numBlocks - 1);
114
- }
115
-
116
- public async getCheckpointedBlocks(
117
- from: BlockNumber,
118
- limit: number,
119
- proven?: boolean,
120
- ): Promise<CheckpointedL2Block[]> {
121
- const blocks = await this.store.getCheckpointedBlocks(from, limit);
122
-
123
- if (proven === true) {
124
- const provenBlockNumber = await this.store.getProvenBlockNumber();
125
- return blocks.filter(b => b.block.number <= provenBlockNumber);
232
+ const data = await this.stores.blocks.getCheckpointData(number);
233
+ if (!data) {
234
+ return undefined;
126
235
  }
127
- return blocks;
236
+ return this.getPublishedCheckpointFromCheckpointData(data);
128
237
  }
129
238
 
130
- public getBlockHeaderByHash(blockHash: Fr): Promise<BlockHeader | undefined> {
131
- return this.store.getBlockHeaderByHash(blockHash);
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)));
132
242
  }
133
243
 
134
- public getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
135
- return this.store.getBlockHeaderByArchive(archive);
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);
136
250
  }
137
251
 
138
- public async getL2BlockNew(number: BlockNumber): Promise<L2BlockNew | undefined> {
139
- // If the number provided is -ve, then return the latest block.
140
- if (number < 0) {
141
- number = await this.store.getLatestBlockNumber();
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);
142
255
  }
143
- if (number === 0) {
144
- return undefined;
256
+ if ('from' in query) {
257
+ return this.stores.blocks.getRangeOfCheckpoints(query.from, query.limit);
145
258
  }
146
- const publishedBlock = await this.store.getBlock(number);
147
- return publishedBlock;
259
+ const numbers = await this.getCheckpointNumbersForEpoch(query.epoch);
260
+ return numbers.length > 0 ? this.stores.blocks.getRangeOfCheckpoints(numbers[0], numbers.length) : [];
148
261
  }
149
262
 
150
- public getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
151
- return this.store.getTxEffect(txHash);
263
+ public getProposedCheckpointData(query?: ProposedCheckpointQuery): Promise<ProposedCheckpointData | undefined> {
264
+ if (!query || 'tag' in query) {
265
+ return this.stores.blocks.getLastProposedCheckpoint();
266
+ }
267
+ if ('number' in query) {
268
+ return this.stores.blocks.getProposedCheckpointByNumber(query.number);
269
+ }
270
+ return this.stores.blocks.getProposedCheckpointBySlot(query.slot);
152
271
  }
153
272
 
154
- public getSettledTxReceipt(txHash: TxHash): Promise<TxReceipt | undefined> {
155
- return this.store.getSettledTxReceipt(txHash);
273
+ public getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
274
+ return this.stores.blocks.getTxEffect(txHash);
156
275
  }
157
276
 
158
277
  public isPendingChainInvalid(): Promise<boolean> {
@@ -160,259 +279,188 @@ export abstract class ArchiverDataSourceBase
160
279
  }
161
280
 
162
281
  public async getPendingChainValidationStatus(): Promise<ValidateCheckpointResult> {
163
- return (await this.store.getPendingChainValidationStatus()) ?? { valid: true };
164
- }
165
-
166
- public async getL2BlocksNew(from: BlockNumber, limit: number, proven?: boolean): Promise<L2BlockNew[]> {
167
- const blocks = await this.store.getBlocks(from, limit);
168
-
169
- if (proven === true) {
170
- const provenBlockNumber = await this.store.getProvenBlockNumber();
171
- return blocks.filter(b => b.number <= provenBlockNumber);
172
- }
173
- return blocks;
174
- }
175
-
176
- public getPrivateLogsByTags(tags: SiloedTag[], page?: number): Promise<TxScopedL2Log[][]> {
177
- return this.store.getPrivateLogsByTags(tags, page);
178
- }
179
-
180
- public getPublicLogsByTagsFromContract(
181
- contractAddress: AztecAddress,
182
- tags: Tag[],
183
- page?: number,
184
- ): Promise<TxScopedL2Log[][]> {
185
- return this.store.getPublicLogsByTagsFromContract(contractAddress, tags, page);
282
+ return (await this.stores.blocks.getPendingChainValidationStatus()) ?? { valid: true };
186
283
  }
187
284
 
188
- public getPublicLogs(filter: LogFilter): Promise<GetPublicLogsResponse> {
189
- return this.store.getPublicLogs(filter);
285
+ public getPrivateLogsByTags(query: PrivateLogsQuery): Promise<LogResult[][]> {
286
+ return this.stores.logs.getPrivateLogsByTags(query);
190
287
  }
191
288
 
192
- public getContractClassLogs(filter: LogFilter): Promise<GetContractClassLogsResponse> {
193
- return this.store.getContractClassLogs(filter);
289
+ public getPublicLogsByTags(query: PublicLogsQuery): Promise<LogResult[][]> {
290
+ return this.stores.logs.getPublicLogsByTags(query);
194
291
  }
195
292
 
196
293
  public getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
197
- return this.store.getContractClass(id);
294
+ return this.stores.contractClasses.getContractClass(id);
198
295
  }
199
296
 
200
297
  public getBytecodeCommitment(id: Fr): Promise<Fr | undefined> {
201
- return this.store.getBytecodeCommitment(id);
202
- }
203
-
204
- public async getContract(
205
- address: AztecAddress,
206
- maybeTimestamp?: UInt64,
207
- ): Promise<ContractInstanceWithAddress | undefined> {
208
- let timestamp;
209
- if (maybeTimestamp === undefined) {
210
- const latestBlockHeader = await this.getBlockHeader('latest');
211
- // If we get undefined block header, it means that the archiver has not yet synced any block so we default to 0.
212
- timestamp = latestBlockHeader ? latestBlockHeader.globalVariables.timestamp : 0n;
213
- } else {
214
- timestamp = maybeTimestamp;
215
- }
298
+ return this.stores.contractClasses.getBytecodeCommitment(id);
299
+ }
216
300
 
217
- return this.store.getContractInstance(address, timestamp);
301
+ public getContract(address: AztecAddress, timestamp: UInt64): Promise<ContractInstanceWithAddress | undefined> {
302
+ return this.stores.contractInstances.getContractInstance(address, timestamp);
218
303
  }
219
304
 
220
305
  public getContractClassIds(): Promise<Fr[]> {
221
- return this.store.getContractClassIds();
306
+ return this.stores.contractClasses.getContractClassIds();
222
307
  }
223
308
 
224
- public getDebugFunctionName(address: AztecAddress, selector: FunctionSelector): Promise<string | undefined> {
225
- 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));
226
312
  }
227
313
 
314
+ /** Register public function signatures so they can be looked up by selector. */
228
315
  public registerContractFunctionSignatures(signatures: string[]): Promise<void> {
229
- return this.store.registerContractFunctionSignatures(signatures);
316
+ return this.stores.functionNames.register(signatures);
230
317
  }
231
318
 
232
319
  public getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise<Fr[]> {
233
- return this.store.getL1ToL2Messages(checkpointNumber);
320
+ return this.stores.messages.getL1ToL2Messages(checkpointNumber);
234
321
  }
235
322
 
236
323
  public getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise<bigint | undefined> {
237
- return this.store.getL1ToL2MessageIndex(l1ToL2Message);
238
- }
239
-
240
- public async getPublishedCheckpoints(
241
- checkpointNumber: CheckpointNumber,
242
- limit: number,
243
- ): Promise<PublishedCheckpoint[]> {
244
- const checkpoints = await this.store.getRangeOfCheckpoints(checkpointNumber, limit);
245
- const blocks = (
246
- await Promise.all(checkpoints.map(ch => this.store.getBlocksForCheckpoint(ch.checkpointNumber)))
247
- ).filter(isDefined);
248
-
249
- const fullCheckpoints: PublishedCheckpoint[] = [];
250
- for (let i = 0; i < checkpoints.length; i++) {
251
- const blocksForCheckpoint = blocks[i];
252
- const checkpoint = checkpoints[i];
253
- const fullCheckpoint = new Checkpoint(
254
- checkpoint.archive,
255
- checkpoint.header,
256
- blocksForCheckpoint,
257
- checkpoint.checkpointNumber,
258
- );
259
- const publishedCheckpoint = new PublishedCheckpoint(
260
- fullCheckpoint,
261
- checkpoint.l1,
262
- checkpoint.attestations.map(x => CommitteeAttestation.fromBuffer(x)),
263
- );
264
- fullCheckpoints.push(publishedCheckpoint);
265
- }
266
- return fullCheckpoints;
324
+ return this.stores.messages.getL1ToL2MessageIndex(l1ToL2Message);
267
325
  }
268
326
 
269
- public getBlocksForSlot(slotNumber: SlotNumber): Promise<L2BlockNew[]> {
270
- return this.store.getBlocksForSlot(slotNumber);
271
- }
272
-
273
- public async getBlocksForEpoch(epochNumber: EpochNumber): Promise<L2BlockNew[]> {
274
- if (!this.l1Constants) {
275
- throw new Error('L1 constants not set');
276
- }
277
-
278
- const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1Constants);
279
- const blocks: L2BlockNew[] = [];
280
-
281
- // Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
282
- // We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
283
- let checkpoint = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
284
- const slot = (b: CheckpointData) => b.header.slotNumber;
285
- while (checkpoint && slot(checkpoint) >= start) {
286
- if (slot(checkpoint) <= end) {
287
- // push the blocks on backwards
288
- const endBlock = checkpoint.startBlock + checkpoint.numBlocks - 1;
289
- for (let i = endBlock; i >= checkpoint.startBlock; i--) {
290
- const block = await this.getBlock(BlockNumber(i));
291
- if (block) {
292
- blocks.push(block);
293
- }
294
- }
295
- }
296
- checkpoint = await this.store.getCheckpointData(CheckpointNumber(checkpoint.checkpointNumber - 1));
327
+ private async getPublishedCheckpointFromCheckpointData(checkpoint: CheckpointData): Promise<PublishedCheckpoint> {
328
+ const blocksForCheckpoint = await this.stores.blocks.getBlocksForCheckpoint(checkpoint.checkpointNumber);
329
+ if (!blocksForCheckpoint) {
330
+ throw new Error(`Blocks for checkpoint ${checkpoint.checkpointNumber} not found`);
297
331
  }
332
+ const fullCheckpoint = new Checkpoint(
333
+ checkpoint.archive,
334
+ checkpoint.header,
335
+ blocksForCheckpoint,
336
+ checkpoint.checkpointNumber,
337
+ checkpoint.feeAssetPriceModifier,
338
+ );
339
+ return new PublishedCheckpoint(fullCheckpoint, checkpoint.l1, checkpoint.attestations);
340
+ }
298
341
 
299
- return blocks.reverse();
342
+ public getBlocksForSlot(slotNumber: SlotNumber): Promise<L2Block[]> {
343
+ return this.stores.blocks.getBlocksForSlot(slotNumber);
300
344
  }
301
345
 
302
- public async getBlockHeadersForEpoch(epochNumber: EpochNumber): Promise<BlockHeader[]> {
346
+ /** Returns just the checkpoint numbers for all checkpoints whose slot falls within the given epoch. */
347
+ private getCheckpointNumbersForEpoch(epochNumber: EpochNumber): Promise<CheckpointNumber[]> {
303
348
  if (!this.l1Constants) {
304
349
  throw new Error('L1 constants not set');
305
350
  }
306
351
 
307
352
  const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1Constants);
308
- const blocks: BlockHeader[] = [];
309
-
310
- // Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
311
- // We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
312
- let checkpoint = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
313
- const slot = (b: CheckpointData) => b.header.slotNumber;
314
- while (checkpoint && slot(checkpoint) >= start) {
315
- if (slot(checkpoint) <= end) {
316
- // push the blocks on backwards
317
- const endBlock = checkpoint.startBlock + checkpoint.numBlocks - 1;
318
- for (let i = endBlock; i >= checkpoint.startBlock; i--) {
319
- const block = await this.getBlockHeader(BlockNumber(i));
320
- if (block) {
321
- blocks.push(block);
322
- }
323
- }
324
- }
325
- checkpoint = await this.store.getCheckpointData(CheckpointNumber(checkpoint.checkpointNumber - 1));
326
- }
327
- return blocks.reverse();
353
+ return this.stores.blocks.getCheckpointNumbersForSlotRange(start, end);
328
354
  }
329
355
 
330
- public async getCheckpointsForEpoch(epochNumber: EpochNumber): Promise<Checkpoint[]> {
331
- if (!this.l1Constants) {
332
- throw new Error('L1 constants not set');
356
+ public async getBlock(query: BlockQuery): Promise<L2Block | undefined> {
357
+ const resolved = await this.resolveBlockQuery(query);
358
+ if (resolved === undefined) {
359
+ return undefined;
333
360
  }
334
-
335
- const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1Constants);
336
- const checkpoints: Checkpoint[] = [];
337
-
338
- // Walk the list of checkpoints backwards and filter by slots matching the requested epoch.
339
- // We'll typically ask for checkpoints for a very recent epoch, so we shouldn't need an index here.
340
- let checkpointData = await this.store.getCheckpointData(await this.store.getSynchedCheckpointNumber());
341
- const slot = (b: CheckpointData) => b.header.slotNumber;
342
- while (checkpointData && slot(checkpointData) >= start) {
343
- if (slot(checkpointData) <= end) {
344
- // push the checkpoints on backwards
345
- const [checkpoint] = await this.getPublishedCheckpoints(checkpointData.checkpointNumber, 1);
346
- checkpoints.push(checkpoint.checkpoint);
347
- }
348
- checkpointData = await this.store.getCheckpointData(CheckpointNumber(checkpointData.checkpointNumber - 1));
361
+ if (this.isGenesisBlockQuery(resolved)) {
362
+ return this.getGenesisBlock();
349
363
  }
350
-
351
- return checkpoints.reverse();
364
+ return this.stores.blocks.getBlock(resolved);
352
365
  }
353
366
 
354
- public async getPublishedBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<CheckpointedL2Block[]> {
355
- const checkpoints = await this.store.getRangeOfCheckpoints(CheckpointNumber(from), limit);
356
- const provenCheckpointNumber = await this.store.getProvenCheckpointNumber();
357
- const blocks = (
358
- await Promise.all(checkpoints.map(ch => this.store.getBlocksForCheckpoint(ch.checkpointNumber)))
359
- ).filter(isDefined);
360
-
361
- const publishedBlocks: CheckpointedL2Block[] = [];
362
- for (let i = 0; i < checkpoints.length; i++) {
363
- const blockForCheckpoint = blocks[i][0];
364
- const checkpoint = checkpoints[i];
365
- if (checkpoint.checkpointNumber > provenCheckpointNumber && proven === true) {
366
- // this checkpoint isn't proven and we only want proven
367
- continue;
368
- }
369
- const publishedBlock = new CheckpointedL2Block(
370
- checkpoint.checkpointNumber,
371
- blockForCheckpoint,
372
- checkpoint.l1,
373
- checkpoint.attestations.map(x => CommitteeAttestation.fromBuffer(x)),
374
- );
375
- publishedBlocks.push(publishedBlock);
376
- }
377
- return publishedBlocks;
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) : [];
378
376
  }
379
377
 
380
- public async getBlock(number: BlockNumber): Promise<L2BlockNew | undefined> {
381
- // If the number provided is -ve, then return the latest block.
382
- if (number < 0) {
383
- number = await this.store.getLatestBlockNumber();
384
- }
385
- if (number === 0) {
378
+ public async getBlockData(query: BlockQuery): Promise<BlockData | undefined> {
379
+ const resolved = await this.resolveBlockQuery(query);
380
+ if (resolved === undefined) {
386
381
  return undefined;
387
382
  }
388
- return this.store.getBlock(number);
389
- }
390
-
391
- public async getBlocks(from: BlockNumber, limit: number, proven?: boolean): Promise<L2BlockNew[]> {
392
- const blocks = await this.store.getBlocks(from, limit);
393
-
394
- if (proven === true) {
395
- const provenBlockNumber = await this.store.getProvenBlockNumber();
396
- return blocks.filter(b => b.number <= provenBlockNumber);
383
+ if (this.isGenesisBlockQuery(resolved)) {
384
+ return this.getGenesisBlockData();
397
385
  }
398
- return blocks;
386
+ return this.stores.blocks.getBlockData(resolved);
399
387
  }
400
388
 
401
- public getPublishedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
402
- return this.store.getCheckpointedBlockByHash(blockHash);
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) : [];
403
393
  }
404
394
 
405
- public getPublishedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
406
- return this.store.getCheckpointedBlockByArchive(archive);
407
- }
408
-
409
- public async getL2BlockNewByHash(blockHash: Fr): Promise<L2BlockNew | undefined> {
410
- const checkpointedBlock = await this.store.getCheckpointedBlockByHash(blockHash);
411
- 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
+ }
412
431
  }
413
432
 
414
- public async getL2BlockNewByArchive(archive: Fr): Promise<L2BlockNew | undefined> {
415
- const checkpointedBlock = await this.store.getCheckpointedBlockByArchive(archive);
416
- 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 };
417
465
  }
418
466
  }