@aztec/archiver 0.23.0 → 0.26.1

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 (65) hide show
  1. package/dest/archiver/archiver.d.ts +15 -11
  2. package/dest/archiver/archiver.d.ts.map +1 -1
  3. package/dest/archiver/archiver.js +67 -72
  4. package/dest/archiver/archiver_store.d.ts +37 -15
  5. package/dest/archiver/archiver_store.d.ts.map +1 -1
  6. package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
  7. package/dest/archiver/archiver_store_test_suite.js +70 -54
  8. package/dest/archiver/data_retrieval.d.ts +18 -8
  9. package/dest/archiver/data_retrieval.d.ts.map +1 -1
  10. package/dest/archiver/data_retrieval.js +39 -14
  11. package/dest/archiver/eth_log_handlers.d.ts +25 -23
  12. package/dest/archiver/eth_log_handlers.d.ts.map +1 -1
  13. package/dest/archiver/eth_log_handlers.js +98 -57
  14. package/dest/archiver/kv_archiver_store/block_body_store.d.ts +27 -0
  15. package/dest/archiver/kv_archiver_store/block_body_store.d.ts.map +1 -0
  16. package/dest/archiver/kv_archiver_store/block_body_store.js +47 -0
  17. package/dest/archiver/kv_archiver_store/block_store.d.ts +19 -11
  18. package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
  19. package/dest/archiver/kv_archiver_store/block_store.js +58 -30
  20. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +1 -0
  21. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
  22. package/dest/archiver/kv_archiver_store/contract_class_store.js +4 -1
  23. package/dest/archiver/kv_archiver_store/contract_store.d.ts.map +1 -1
  24. package/dest/archiver/kv_archiver_store/contract_store.js +6 -3
  25. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +36 -16
  26. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
  27. package/dest/archiver/kv_archiver_store/kv_archiver_store.js +52 -22
  28. package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
  29. package/dest/archiver/kv_archiver_store/log_store.js +4 -3
  30. package/dest/archiver/kv_archiver_store/message_store.d.ts +9 -9
  31. package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
  32. package/dest/archiver/kv_archiver_store/message_store.js +34 -31
  33. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +6 -6
  34. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts.map +1 -1
  35. package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.js +18 -18
  36. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +41 -16
  37. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
  38. package/dest/archiver/memory_archiver_store/memory_archiver_store.js +78 -29
  39. package/dest/index.js +2 -2
  40. package/dest/rpc/archiver_client.d.ts.map +1 -1
  41. package/dest/rpc/archiver_client.js +3 -3
  42. package/dest/rpc/archiver_server.d.ts.map +1 -1
  43. package/dest/rpc/archiver_server.js +4 -3
  44. package/package.json +10 -10
  45. package/src/archiver/archiver.ts +625 -0
  46. package/src/archiver/archiver_store.ts +226 -0
  47. package/src/archiver/archiver_store_test_suite.ts +676 -0
  48. package/src/archiver/config.ts +89 -0
  49. package/src/archiver/data_retrieval.ts +232 -0
  50. package/src/archiver/eth_log_handlers.ts +342 -0
  51. package/src/archiver/index.ts +5 -0
  52. package/src/archiver/kv_archiver_store/block_body_store.ts +54 -0
  53. package/src/archiver/kv_archiver_store/block_store.ts +199 -0
  54. package/src/archiver/kv_archiver_store/contract_class_store.ts +68 -0
  55. package/src/archiver/kv_archiver_store/contract_instance_store.ts +26 -0
  56. package/src/archiver/kv_archiver_store/contract_store.ts +98 -0
  57. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +300 -0
  58. package/src/archiver/kv_archiver_store/log_store.ts +174 -0
  59. package/src/archiver/kv_archiver_store/message_store.ts +174 -0
  60. package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +91 -0
  61. package/src/archiver/memory_archiver_store/memory_archiver_store.ts +506 -0
  62. package/src/index.ts +55 -0
  63. package/src/rpc/archiver_client.ts +35 -0
  64. package/src/rpc/archiver_server.ts +41 -0
  65. package/src/rpc/index.ts +2 -0
@@ -0,0 +1,625 @@
1
+ import {
2
+ ContractData,
3
+ ContractDataSource,
4
+ EncodedContractFunction,
5
+ ExtendedContractData,
6
+ GetUnencryptedLogsResponse,
7
+ L1ToL2Message,
8
+ L1ToL2MessageSource,
9
+ L2Block,
10
+ L2BlockL2Logs,
11
+ L2BlockSource,
12
+ L2LogsSource,
13
+ LogFilter,
14
+ LogType,
15
+ TxEffect,
16
+ TxHash,
17
+ TxReceipt,
18
+ UnencryptedL2Log,
19
+ } from '@aztec/circuit-types';
20
+ import { ContractClassRegisteredEvent, FunctionSelector } from '@aztec/circuits.js';
21
+ import { ContractInstanceDeployedEvent } from '@aztec/circuits.js/contract';
22
+ import { createEthereumChain } from '@aztec/ethereum';
23
+ import { AztecAddress } from '@aztec/foundation/aztec-address';
24
+ import { EthAddress } from '@aztec/foundation/eth-address';
25
+ import { Fr } from '@aztec/foundation/fields';
26
+ import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
27
+ import { RunningPromise } from '@aztec/foundation/running-promise';
28
+ import { ClassRegistererAddress } from '@aztec/protocol-contracts/class-registerer';
29
+ import { InstanceDeployerAddress } from '@aztec/protocol-contracts/instance-deployer';
30
+ import {
31
+ ContractClass,
32
+ ContractClassPublic,
33
+ ContractInstance,
34
+ ContractInstanceWithAddress,
35
+ } from '@aztec/types/contracts';
36
+
37
+ import { Chain, HttpTransport, PublicClient, createPublicClient, http } from 'viem';
38
+
39
+ import { ArchiverDataStore } from './archiver_store.js';
40
+ import { ArchiverConfig } from './config.js';
41
+ import {
42
+ retrieveBlockBodiesFromAvailabilityOracle,
43
+ retrieveBlockMetadataFromRollup,
44
+ retrieveNewCancelledL1ToL2Messages,
45
+ retrieveNewContractData,
46
+ retrieveNewPendingL1ToL2Messages,
47
+ } from './data_retrieval.js';
48
+
49
+ /**
50
+ * Helper interface to combine all sources this archiver implementation provides.
51
+ */
52
+ export type ArchiveSource = L2BlockSource & L2LogsSource & ContractDataSource & L1ToL2MessageSource;
53
+
54
+ /**
55
+ * Pulls L2 blocks in a non-blocking manner and provides interface for their retrieval.
56
+ * Responsible for handling robust L1 polling so that other components do not need to
57
+ * concern themselves with it.
58
+ */
59
+ export class Archiver implements ArchiveSource {
60
+ /**
61
+ * A promise in which we will be continually fetching new L2 blocks.
62
+ */
63
+ private runningPromise?: RunningPromise;
64
+
65
+ /**
66
+ * Use this to track logged block in order to avoid repeating the same message.
67
+ */
68
+ private lastLoggedL1BlockNumber = 0n;
69
+
70
+ /**
71
+ * Creates a new instance of the Archiver.
72
+ * @param publicClient - A client for interacting with the Ethereum node.
73
+ * @param rollupAddress - Ethereum address of the rollup contract.
74
+ * @param inboxAddress - Ethereum address of the inbox contract.
75
+ * @param registryAddress - Ethereum address of the registry contract.
76
+ * @param contractDeploymentEmitterAddress - Ethereum address of the contractDeploymentEmitter contract.
77
+ * @param pollingIntervalMs - The interval for polling for L1 logs (in milliseconds).
78
+ * @param store - An archiver data store for storage & retrieval of blocks, encrypted logs & contract data.
79
+ * @param log - A logger.
80
+ */
81
+ constructor(
82
+ private readonly publicClient: PublicClient<HttpTransport, Chain>,
83
+ private readonly rollupAddress: EthAddress,
84
+ private readonly availabilityOracleAddress: EthAddress,
85
+ private readonly inboxAddress: EthAddress,
86
+ private readonly registryAddress: EthAddress,
87
+ private readonly contractDeploymentEmitterAddress: EthAddress,
88
+ private readonly store: ArchiverDataStore,
89
+ private readonly pollingIntervalMs = 10_000,
90
+ private readonly log: DebugLogger = createDebugLogger('aztec:archiver'),
91
+ ) {}
92
+
93
+ /**
94
+ * Creates a new instance of the Archiver and blocks until it syncs from chain.
95
+ * @param config - The archiver's desired configuration.
96
+ * @param archiverStore - The backing store for the archiver.
97
+ * @param blockUntilSynced - If true, blocks until the archiver has fully synced.
98
+ * @returns - An instance of the archiver.
99
+ */
100
+ public static async createAndSync(
101
+ config: ArchiverConfig,
102
+ archiverStore: ArchiverDataStore,
103
+ blockUntilSynced = true,
104
+ ): Promise<Archiver> {
105
+ const chain = createEthereumChain(config.rpcUrl, config.apiKey);
106
+ const publicClient = createPublicClient({
107
+ chain: chain.chainInfo,
108
+ transport: http(chain.rpcUrl),
109
+ pollingInterval: config.viemPollingIntervalMS,
110
+ });
111
+
112
+ const archiver = new Archiver(
113
+ publicClient,
114
+ config.l1Contracts.rollupAddress,
115
+ config.l1Contracts.availabilityOracleAddress,
116
+ config.l1Contracts.inboxAddress,
117
+ config.l1Contracts.registryAddress,
118
+ config.l1Contracts.contractDeploymentEmitterAddress,
119
+ archiverStore,
120
+ config.archiverPollingIntervalMS,
121
+ );
122
+ await archiver.start(blockUntilSynced);
123
+ return archiver;
124
+ }
125
+
126
+ /**
127
+ * Starts sync process.
128
+ * @param blockUntilSynced - If true, blocks until the archiver has fully synced.
129
+ */
130
+ public async start(blockUntilSynced: boolean): Promise<void> {
131
+ if (this.runningPromise) {
132
+ throw new Error('Archiver is already running');
133
+ }
134
+
135
+ if (blockUntilSynced) {
136
+ this.log(`Performing initial chain sync...`);
137
+ await this.sync(blockUntilSynced);
138
+ }
139
+
140
+ this.runningPromise = new RunningPromise(() => this.sync(false), this.pollingIntervalMs);
141
+ this.runningPromise.start();
142
+ }
143
+
144
+ /**
145
+ * Fetches `L2BlockProcessed` and `ContractDeployment` logs from `nextL2BlockFromBlock` and processes them.
146
+ * @param blockUntilSynced - If true, blocks until the archiver has fully synced.
147
+ */
148
+ private async sync(blockUntilSynced: boolean) {
149
+ /**
150
+ * We keep track of three "pointers" to L1 blocks:
151
+ * 1. the last L1 block that published an L2 block
152
+ * 2. the last L1 block that added L1 to L2 messages
153
+ * 3. the last L1 block that cancelled L1 to L2 messages
154
+ *
155
+ * We do this to deal with L1 data providers that are eventually consistent (e.g. Infura).
156
+ * We guard against seeing block X with no data at one point, and later, the provider processes the block and it has data.
157
+ * The archiver will stay back, until there's data on L1 that will move the pointers forward.
158
+ *
159
+ * This code does not handle reorgs.
160
+ */
161
+ const lastL1Blocks = await this.store.getL1BlockNumber();
162
+ const currentL1BlockNumber = await this.publicClient.getBlockNumber();
163
+
164
+ if (
165
+ currentL1BlockNumber <= lastL1Blocks.addedBlock &&
166
+ currentL1BlockNumber <= lastL1Blocks.addedMessages &&
167
+ currentL1BlockNumber <= lastL1Blocks.cancelledMessages
168
+ ) {
169
+ // chain hasn't moved forward
170
+ // or it's been rolled back
171
+ return;
172
+ }
173
+
174
+ // ********** Ensuring Consistency of data pulled from L1 **********
175
+
176
+ /**
177
+ * There are a number of calls in this sync operation to L1 for retrieving
178
+ * events and transaction data. There are a couple of things we need to bear in mind
179
+ * to ensure that data is read exactly once.
180
+ *
181
+ * The first is the problem of eventually consistent ETH service providers like Infura.
182
+ * Each L1 read operation will query data from the last L1 block that it saw emit its kind of data.
183
+ * (so pending L1 to L2 messages will read from the last L1 block that emitted a message and so on)
184
+ * This will mean the archiver will lag behind L1 and will only advance when there's L2-relevant activity on the chain.
185
+ *
186
+ * The second is that in between the various calls to L1, the block number can move meaning some
187
+ * of the following calls will return data for blocks that were not present during earlier calls.
188
+ * To combat this for the time being we simply ensure that all data retrieval methods only retrieve
189
+ * data up to the currentBlockNumber captured at the top of this function. We might want to improve on this
190
+ * in future but for the time being it should give us the guarantees that we need
191
+ */
192
+
193
+ // ********** Events that are processed per L1 block **********
194
+
195
+ // Process l1ToL2Messages, these are consumed as time passes, not each block
196
+ const retrievedPendingL1ToL2Messages = await retrieveNewPendingL1ToL2Messages(
197
+ this.publicClient,
198
+ this.inboxAddress,
199
+ blockUntilSynced,
200
+ lastL1Blocks.addedMessages + 1n,
201
+ currentL1BlockNumber,
202
+ );
203
+ const retrievedCancelledL1ToL2Messages = await retrieveNewCancelledL1ToL2Messages(
204
+ this.publicClient,
205
+ this.inboxAddress,
206
+ blockUntilSynced,
207
+ lastL1Blocks.cancelledMessages + 1n,
208
+ currentL1BlockNumber,
209
+ );
210
+
211
+ // group pending messages and cancelled messages by their L1 block number
212
+ const messagesByBlock = new Map<bigint, [L1ToL2Message[], Fr[]]>();
213
+ for (const [message, blockNumber] of retrievedPendingL1ToL2Messages.retrievedData) {
214
+ const messages = messagesByBlock.get(blockNumber) || [[], []];
215
+ messages[0].push(message);
216
+ messagesByBlock.set(blockNumber, messages);
217
+ }
218
+
219
+ for (const [entryKey, blockNumber] of retrievedCancelledL1ToL2Messages.retrievedData) {
220
+ const messages = messagesByBlock.get(blockNumber) || [[], []];
221
+ messages[1].push(entryKey);
222
+ messagesByBlock.set(blockNumber, messages);
223
+ }
224
+
225
+ // process messages from each L1 block in sequence
226
+ const l1BlocksWithMessages = Array.from(messagesByBlock.keys()).sort((a, b) => (a < b ? -1 : a === b ? 0 : 1));
227
+ for (const l1Block of l1BlocksWithMessages) {
228
+ const [newMessages, cancelledMessages] = messagesByBlock.get(l1Block)!;
229
+ this.log(
230
+ `Adding ${newMessages.length} new messages and ${cancelledMessages.length} cancelled messages in L1 block ${l1Block}`,
231
+ );
232
+ await this.store.addPendingL1ToL2Messages(newMessages, l1Block);
233
+ await this.store.cancelPendingL1ToL2EntryKeys(cancelledMessages, l1Block);
234
+ }
235
+
236
+ // ********** Events that are processed per L2 block **********
237
+
238
+ // Read all data from chain and then write to our stores at the end
239
+ const nextExpectedL2BlockNum = BigInt((await this.store.getBlockNumber()) + 1);
240
+
241
+ const retrievedBlockBodies = await retrieveBlockBodiesFromAvailabilityOracle(
242
+ this.publicClient,
243
+ this.availabilityOracleAddress,
244
+ blockUntilSynced,
245
+ lastL1Blocks.addedBlock + 1n,
246
+ currentL1BlockNumber,
247
+ );
248
+
249
+ const blockBodies = retrievedBlockBodies.retrievedData.map(([blockBody]) => blockBody);
250
+
251
+ await this.store.addBlockBodies(blockBodies);
252
+
253
+ const retrievedBlockMetadata = await retrieveBlockMetadataFromRollup(
254
+ this.publicClient,
255
+ this.rollupAddress,
256
+ blockUntilSynced,
257
+ lastL1Blocks.addedBlock + 1n,
258
+ currentL1BlockNumber,
259
+ nextExpectedL2BlockNum,
260
+ );
261
+
262
+ const retrievedBodyHashes = retrievedBlockMetadata.retrievedData.map(
263
+ ([header]) => header.contentCommitment.txsHash,
264
+ );
265
+
266
+ const blockBodiesFromStore = await this.store.getBlockBodies(retrievedBodyHashes);
267
+
268
+ if (retrievedBlockMetadata.retrievedData.length !== blockBodiesFromStore.length) {
269
+ throw new Error('Block headers length does not equal block bodies length');
270
+ }
271
+
272
+ const retrievedBlocks = {
273
+ retrievedData: retrievedBlockMetadata.retrievedData.map(
274
+ (blockMetadata, i) =>
275
+ new L2Block(blockMetadata[1], blockMetadata[0], blockBodiesFromStore[i], blockMetadata[2]),
276
+ ),
277
+ };
278
+
279
+ if (retrievedBlocks.retrievedData.length === 0) {
280
+ return;
281
+ } else {
282
+ this.log(
283
+ `Retrieved ${retrievedBlocks.retrievedData.length} new L2 blocks between L1 blocks ${
284
+ lastL1Blocks.addedBlock + 1n
285
+ } and ${currentL1BlockNumber}.`,
286
+ );
287
+ }
288
+
289
+ // create the block number -> block hash mapping to ensure we retrieve the appropriate events
290
+ const blockNumberToBodyHash: { [key: number]: Buffer | undefined } = {};
291
+ retrievedBlocks.retrievedData.forEach((block: L2Block) => {
292
+ blockNumberToBodyHash[block.number] = block.header.contentCommitment.txsHash;
293
+ });
294
+ const retrievedContracts = await retrieveNewContractData(
295
+ this.publicClient,
296
+ this.contractDeploymentEmitterAddress,
297
+ blockUntilSynced,
298
+ lastL1Blocks.addedBlock + 1n,
299
+ currentL1BlockNumber,
300
+ blockNumberToBodyHash,
301
+ );
302
+
303
+ this.log(`Retrieved ${retrievedBlocks.retrievedData.length} block(s) from chain`);
304
+
305
+ await Promise.all(
306
+ retrievedBlocks.retrievedData.map(block => {
307
+ const encryptedLogs = block.body.encryptedLogs;
308
+ const unencryptedLogs = block.body.unencryptedLogs;
309
+
310
+ return this.store.addLogs(encryptedLogs, unencryptedLogs, block.number);
311
+ }),
312
+ );
313
+
314
+ // Unroll all logs emitted during the retrieved blocks and extract any contract classes and instances from them
315
+ await Promise.all(
316
+ retrievedBlocks.retrievedData.map(async block => {
317
+ const blockLogs = block.body.txEffects
318
+ .flatMap(txEffect => (txEffect ? [txEffect.unencryptedLogs] : []))
319
+ .flatMap(txLog => txLog.unrollLogs())
320
+ .map(log => UnencryptedL2Log.fromBuffer(log));
321
+ await this.storeRegisteredContractClasses(blockLogs, block.number);
322
+ await this.storeDeployedContractInstances(blockLogs, block.number);
323
+ }),
324
+ );
325
+
326
+ // store contracts for which we have retrieved L2 blocks
327
+ const lastKnownL2BlockNum = retrievedBlocks.retrievedData[retrievedBlocks.retrievedData.length - 1].number;
328
+ await Promise.all(
329
+ retrievedContracts.retrievedData.map(async ([contracts, l2BlockNum]) => {
330
+ this.log(`Retrieved extended contract data for l2 block number: ${l2BlockNum}`);
331
+ if (l2BlockNum <= lastKnownL2BlockNum) {
332
+ await this.store.addExtendedContractData(contracts, l2BlockNum);
333
+ await this.storeContractDataAsClassesAndInstances(contracts, l2BlockNum);
334
+ }
335
+ }),
336
+ );
337
+
338
+ // from retrieved L2Blocks, confirm L1 to L2 messages that have been published
339
+ // from each l2block fetch all entryKeys in a flattened array:
340
+ this.log(`Confirming l1 to l2 messages in store`);
341
+ for (const block of retrievedBlocks.retrievedData) {
342
+ await this.store.confirmL1ToL2EntryKeys(block.body.l1ToL2Messages);
343
+ }
344
+
345
+ await this.store.addBlocks(retrievedBlocks.retrievedData);
346
+ }
347
+
348
+ /**
349
+ * Extracts and stores contract classes out of ContractClassRegistered events emitted by the class registerer contract.
350
+ * @param allLogs - All logs emitted in a bunch of blocks.
351
+ */
352
+ private async storeRegisteredContractClasses(allLogs: UnencryptedL2Log[], blockNum: number) {
353
+ const contractClasses = ContractClassRegisteredEvent.fromLogs(allLogs, ClassRegistererAddress).map(e =>
354
+ e.toContractClassPublic(),
355
+ );
356
+ if (contractClasses.length > 0) {
357
+ contractClasses.forEach(c => this.log(`Registering contract class ${c.id.toString()}`));
358
+ await this.store.addContractClasses(contractClasses, blockNum);
359
+ }
360
+ }
361
+
362
+ /**
363
+ * Extracts and stores contract instances out of ContractInstanceDeployed events emitted by the canonical deployer contract.
364
+ * @param allLogs - All logs emitted in a bunch of blocks.
365
+ */
366
+ private async storeDeployedContractInstances(allLogs: UnencryptedL2Log[], blockNum: number) {
367
+ const contractInstances = ContractInstanceDeployedEvent.fromLogs(allLogs, InstanceDeployerAddress).map(e =>
368
+ e.toContractInstance(),
369
+ );
370
+ if (contractInstances.length > 0) {
371
+ contractInstances.forEach(c => this.log(`Storing contract instance at ${c.address.toString()}`));
372
+ await this.store.addContractInstances(contractInstances, blockNum);
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Stores extended contract data as classes and instances.
378
+ * Temporary solution until we source this data from the contract class registerer and instance deployer.
379
+ * @param contracts - The extended contract data to be stored.
380
+ * @param l2BlockNum - The L2 block number to which the contract data corresponds.
381
+ */
382
+ async storeContractDataAsClassesAndInstances(contracts: ExtendedContractData[], l2BlockNum: number) {
383
+ const classesAndInstances = contracts.map(extendedContractDataToContractClassAndInstance);
384
+ await this.store.addContractClasses(
385
+ classesAndInstances.map(([c, _]) => c),
386
+ l2BlockNum,
387
+ );
388
+ await this.store.addContractInstances(
389
+ classesAndInstances.map(([_, i]) => i),
390
+ l2BlockNum,
391
+ );
392
+ }
393
+
394
+ /**
395
+ * Stops the archiver.
396
+ * @returns A promise signalling completion of the stop process.
397
+ */
398
+ public async stop(): Promise<void> {
399
+ this.log('Stopping...');
400
+ await this.runningPromise?.stop();
401
+
402
+ this.log('Stopped.');
403
+ return Promise.resolve();
404
+ }
405
+
406
+ public getRollupAddress(): Promise<EthAddress> {
407
+ return Promise.resolve(this.rollupAddress);
408
+ }
409
+
410
+ public getRegistryAddress(): Promise<EthAddress> {
411
+ return Promise.resolve(this.registryAddress);
412
+ }
413
+
414
+ /**
415
+ * Gets up to `limit` amount of L2 blocks starting from `from`.
416
+ * @param from - Number of the first block to return (inclusive).
417
+ * @param limit - The number of blocks to return.
418
+ * @returns The requested L2 blocks.
419
+ */
420
+ public getBlocks(from: number, limit: number): Promise<L2Block[]> {
421
+ return this.store.getBlocks(from, limit);
422
+ }
423
+
424
+ /**
425
+ * Gets an l2 block.
426
+ * @param number - The block number to return (inclusive).
427
+ * @returns The requested L2 block.
428
+ */
429
+ public async getBlock(number: number): Promise<L2Block | undefined> {
430
+ // If the number provided is -ve, then return the latest block.
431
+ if (number < 0) {
432
+ number = await this.store.getBlockNumber();
433
+ }
434
+ const blocks = await this.store.getBlocks(number, 1);
435
+ return blocks.length === 0 ? undefined : blocks[0];
436
+ }
437
+
438
+ public getTxEffect(txHash: TxHash): Promise<TxEffect | undefined> {
439
+ return this.store.getTxEffect(txHash);
440
+ }
441
+
442
+ public getSettledTxReceipt(txHash: TxHash): Promise<TxReceipt | undefined> {
443
+ return this.store.getSettledTxReceipt(txHash);
444
+ }
445
+
446
+ /**
447
+ * Get the extended contract data for this contract.
448
+ * @param contractAddress - The contract data address.
449
+ * @returns The extended contract data or undefined if not found.
450
+ */
451
+ public async getExtendedContractData(contractAddress: AztecAddress): Promise<ExtendedContractData | undefined> {
452
+ return (
453
+ (await this.store.getExtendedContractData(contractAddress)) ?? this.makeExtendedContractDataFor(contractAddress)
454
+ );
455
+ }
456
+
457
+ /**
458
+ * Temporary method for creating a fake extended contract data out of classes and instances registered in the node.
459
+ * Used as a fallback if the extended contract data is not found.
460
+ */
461
+ private async makeExtendedContractDataFor(address: AztecAddress): Promise<ExtendedContractData | undefined> {
462
+ const instance = await this.store.getContractInstance(address);
463
+ if (!instance) {
464
+ return undefined;
465
+ }
466
+
467
+ const contractClass = await this.store.getContractClass(instance.contractClassId);
468
+ if (!contractClass) {
469
+ this.log.warn(`Class ${instance.contractClassId.toString()} for address ${address.toString()} not found`);
470
+ return undefined;
471
+ }
472
+
473
+ return ExtendedContractData.fromClassAndInstance(contractClass, instance);
474
+ }
475
+
476
+ /**
477
+ * Lookup all contract data in an L2 block.
478
+ * @param blockNum - The block number to get all contract data from.
479
+ * @returns All new contract data in the block (if found).
480
+ */
481
+ public getExtendedContractDataInBlock(blockNum: number): Promise<ExtendedContractData[]> {
482
+ return this.store.getExtendedContractDataInBlock(blockNum);
483
+ }
484
+
485
+ /**
486
+ * Lookup the contract data for this contract.
487
+ * Contains contract address & the ethereum portal address.
488
+ * @param contractAddress - The contract data address.
489
+ * @returns ContractData with the portal address (if we didn't throw an error).
490
+ */
491
+ public async getContractData(contractAddress: AztecAddress): Promise<ContractData | undefined> {
492
+ return (await this.store.getContractData(contractAddress)) ?? this.makeContractDataFor(contractAddress);
493
+ }
494
+
495
+ /**
496
+ * Temporary method for creating a fake contract data out of classes and instances registered in the node.
497
+ * Used as a fallback if the extended contract data is not found.
498
+ */
499
+ private async makeContractDataFor(address: AztecAddress): Promise<ContractData | undefined> {
500
+ const instance = await this.store.getContractInstance(address);
501
+ if (!instance) {
502
+ return undefined;
503
+ }
504
+
505
+ return new ContractData(address, instance.portalContractAddress);
506
+ }
507
+
508
+ /**
509
+ * Lookup the L2 contract data inside a block.
510
+ * Contains contract address & the ethereum portal address.
511
+ * @param l2BlockNum - The L2 block number to get the contract data from.
512
+ * @returns ContractData with the portal address (if we didn't throw an error).
513
+ */
514
+ public getContractDataInBlock(l2BlockNum: number): Promise<ContractData[] | undefined> {
515
+ return this.store.getContractDataInBlock(l2BlockNum);
516
+ }
517
+
518
+ /**
519
+ * Gets the public function data for a contract.
520
+ * @param contractAddress - The contract address containing the function to fetch.
521
+ * @param selector - The function selector of the function to fetch.
522
+ * @returns The public function data (if found).
523
+ */
524
+ public async getPublicFunction(
525
+ contractAddress: AztecAddress,
526
+ selector: FunctionSelector,
527
+ ): Promise<EncodedContractFunction | undefined> {
528
+ const contractData = await this.getExtendedContractData(contractAddress);
529
+ return contractData?.getPublicFunction(selector);
530
+ }
531
+
532
+ /**
533
+ * Gets up to `limit` amount of logs starting from `from`.
534
+ * @param from - Number of the L2 block to which corresponds the first logs to be returned.
535
+ * @param limit - The number of logs to return.
536
+ * @param logType - Specifies whether to return encrypted or unencrypted logs.
537
+ * @returns The requested logs.
538
+ */
539
+ public getLogs(from: number, limit: number, logType: LogType): Promise<L2BlockL2Logs[]> {
540
+ return this.store.getLogs(from, limit, logType);
541
+ }
542
+
543
+ /**
544
+ * Gets unencrypted logs based on the provided filter.
545
+ * @param filter - The filter to apply to the logs.
546
+ * @returns The requested logs.
547
+ */
548
+ getUnencryptedLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse> {
549
+ return this.store.getUnencryptedLogs(filter);
550
+ }
551
+
552
+ /**
553
+ * Gets the number of the latest L2 block processed by the block source implementation.
554
+ * @returns The number of the latest L2 block processed by the block source implementation.
555
+ */
556
+ public getBlockNumber(): Promise<number> {
557
+ return this.store.getBlockNumber();
558
+ }
559
+
560
+ public getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
561
+ return this.store.getContractClass(id);
562
+ }
563
+
564
+ public getContract(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
565
+ return this.store.getContractInstance(address);
566
+ }
567
+
568
+ /**
569
+ * Gets up to `limit` amount of pending L1 to L2 messages.
570
+ * @param limit - The number of messages to return.
571
+ * @returns The requested L1 to L2 messages' keys.
572
+ */
573
+ getPendingL1ToL2EntryKeys(limit: number): Promise<Fr[]> {
574
+ return this.store.getPendingL1ToL2EntryKeys(limit);
575
+ }
576
+
577
+ /**
578
+ * Gets the confirmed/consumed L1 to L2 message associated with the given entry key
579
+ * @param entryKey - The entry key.
580
+ * @returns The L1 to L2 message (throws if not found).
581
+ */
582
+ getConfirmedL1ToL2Message(entryKey: Fr): Promise<L1ToL2Message> {
583
+ return this.store.getConfirmedL1ToL2Message(entryKey);
584
+ }
585
+
586
+ getContractClassIds(): Promise<Fr[]> {
587
+ return this.store.getContractClassIds();
588
+ }
589
+ }
590
+
591
+ /**
592
+ * Converts ExtendedContractData into contract classes and instances.
593
+ * Note that the conversion is not correct, since there is some data missing from the broadcasted ExtendedContractData.
594
+ * The archiver will trust the ids broadcasted instead of trying to recompute them.
595
+ * Eventually this function and ExtendedContractData altogether will be removed.
596
+ */
597
+ function extendedContractDataToContractClassAndInstance(
598
+ data: ExtendedContractData,
599
+ ): [ContractClassPublic, ContractInstanceWithAddress] {
600
+ const contractClass: ContractClass = {
601
+ version: 1,
602
+ artifactHash: Fr.ZERO,
603
+ publicFunctions: data.publicFunctions.map(f => ({
604
+ selector: f.selector,
605
+ bytecode: f.bytecode,
606
+ isInternal: f.isInternal,
607
+ })),
608
+ privateFunctions: [],
609
+ packedBytecode: data.bytecode,
610
+ };
611
+ const contractClassId = data.contractClassId;
612
+ const contractInstance: ContractInstance = {
613
+ version: 1,
614
+ salt: data.saltedInitializationHash,
615
+ contractClassId,
616
+ initializationHash: data.saltedInitializationHash,
617
+ portalContractAddress: data.contractData.portalContractAddress,
618
+ publicKeysHash: data.publicKeyHash,
619
+ };
620
+ const address = data.contractData.contractAddress;
621
+ return [
622
+ { ...contractClass, id: contractClassId, privateFunctionsRoot: Fr.ZERO },
623
+ { ...contractInstance, address },
624
+ ];
625
+ }