@aztec/archiver 0.23.0 → 0.24.0

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