@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,89 @@
1
+ import { L1ContractAddresses } from '@aztec/ethereum';
2
+ import { EthAddress } from '@aztec/foundation/eth-address';
3
+
4
+ /**
5
+ * There are 2 polling intervals used in this configuration. The first is the archiver polling interval, archiverPollingIntervalMS.
6
+ * This is the interval between successive calls to eth_blockNumber via viem.
7
+ * Results of calls to eth_blockNumber are cached by viem with this cache being updated periodically at the interval specified by viemPollingIntervalMS.
8
+ * As a result the maximum observed polling time for new blocks will be viemPollingIntervalMS + archiverPollingIntervalMS.
9
+ */
10
+
11
+ /**
12
+ * The archiver configuration.
13
+ */
14
+ export interface ArchiverConfig {
15
+ /**
16
+ * The url of the Ethereum RPC node.
17
+ */
18
+ rpcUrl: string;
19
+
20
+ /**
21
+ * The key for the ethereum node.
22
+ */
23
+ apiKey?: string;
24
+
25
+ /**
26
+ * The polling interval in ms for retrieving new L2 blocks and encrypted logs.
27
+ */
28
+ archiverPollingIntervalMS?: number;
29
+
30
+ /**
31
+ * The polling interval viem uses in ms
32
+ */
33
+ viemPollingIntervalMS?: number;
34
+
35
+ /**
36
+ * The deployed L1 contract addresses
37
+ */
38
+ l1Contracts: L1ContractAddresses;
39
+
40
+ /**
41
+ * Optional dir to store data. If omitted will store in memory.
42
+ */
43
+ dataDirectory?: string;
44
+
45
+ /** The max number of logs that can be obtained in 1 "getUnencryptedLogs" call. */
46
+ maxLogs?: number;
47
+ }
48
+
49
+ /**
50
+ * Returns the archiver configuration from the environment variables.
51
+ * Note: If an environment variable is not set, the default value is used.
52
+ * @returns The archiver configuration.
53
+ */
54
+ export function getConfigEnvVars(): ArchiverConfig {
55
+ const {
56
+ ETHEREUM_HOST,
57
+ ARCHIVER_POLLING_INTERVAL_MS,
58
+ ARCHIVER_VIEM_POLLING_INTERVAL_MS,
59
+ AVAILABILITY_ORACLE_CONTRACT_ADDRESS,
60
+ ROLLUP_CONTRACT_ADDRESS,
61
+ CONTRACT_DEPLOYMENT_EMITTER_ADDRESS,
62
+ API_KEY,
63
+ INBOX_CONTRACT_ADDRESS,
64
+ OUTBOX_CONTRACT_ADDRESS,
65
+ REGISTRY_CONTRACT_ADDRESS,
66
+ DATA_DIRECTORY,
67
+ } = process.env;
68
+ // Populate the relevant addresses for use by the archiver.
69
+ const addresses: L1ContractAddresses = {
70
+ availabilityOracleAddress: AVAILABILITY_ORACLE_CONTRACT_ADDRESS
71
+ ? EthAddress.fromString(AVAILABILITY_ORACLE_CONTRACT_ADDRESS)
72
+ : EthAddress.ZERO,
73
+ rollupAddress: ROLLUP_CONTRACT_ADDRESS ? EthAddress.fromString(ROLLUP_CONTRACT_ADDRESS) : EthAddress.ZERO,
74
+ registryAddress: REGISTRY_CONTRACT_ADDRESS ? EthAddress.fromString(REGISTRY_CONTRACT_ADDRESS) : EthAddress.ZERO,
75
+ inboxAddress: INBOX_CONTRACT_ADDRESS ? EthAddress.fromString(INBOX_CONTRACT_ADDRESS) : EthAddress.ZERO,
76
+ outboxAddress: OUTBOX_CONTRACT_ADDRESS ? EthAddress.fromString(OUTBOX_CONTRACT_ADDRESS) : EthAddress.ZERO,
77
+ contractDeploymentEmitterAddress: CONTRACT_DEPLOYMENT_EMITTER_ADDRESS
78
+ ? EthAddress.fromString(CONTRACT_DEPLOYMENT_EMITTER_ADDRESS)
79
+ : EthAddress.ZERO,
80
+ };
81
+ return {
82
+ rpcUrl: ETHEREUM_HOST || 'http://127.0.0.1:8545/',
83
+ archiverPollingIntervalMS: ARCHIVER_POLLING_INTERVAL_MS ? +ARCHIVER_POLLING_INTERVAL_MS : 1_000,
84
+ viemPollingIntervalMS: ARCHIVER_VIEM_POLLING_INTERVAL_MS ? +ARCHIVER_VIEM_POLLING_INTERVAL_MS : 1_000,
85
+ apiKey: API_KEY,
86
+ l1Contracts: addresses,
87
+ dataDirectory: DATA_DIRECTORY,
88
+ };
89
+ }
@@ -0,0 +1,232 @@
1
+ import { Body, ExtendedContractData, L1ToL2Message } from '@aztec/circuit-types';
2
+ import { AppendOnlyTreeSnapshot, Fr, Header } from '@aztec/circuits.js';
3
+ import { EthAddress } from '@aztec/foundation/eth-address';
4
+
5
+ import { PublicClient } from 'viem';
6
+
7
+ import {
8
+ getContractDeploymentLogs,
9
+ getL1ToL2MessageCancelledLogs,
10
+ getL2BlockProcessedLogs,
11
+ getPendingL1ToL2MessageLogs,
12
+ getTxsPublishedLogs,
13
+ processCancelledL1ToL2MessagesLogs,
14
+ processContractDeploymentLogs,
15
+ processL2BlockProcessedLogs,
16
+ processPendingL1ToL2MessageAddedLogs,
17
+ processTxsPublishedLogs,
18
+ } from './eth_log_handlers.js';
19
+
20
+ /**
21
+ * Data retrieved from logs
22
+ */
23
+ type DataRetrieval<T> = {
24
+ /**
25
+ * The next block number.
26
+ */
27
+ nextEthBlockNumber: bigint;
28
+ /**
29
+ * The data returned.
30
+ */
31
+ retrievedData: T[];
32
+ };
33
+
34
+ /**
35
+ * Fetches new L2 block metadata (header, archive snapshot).
36
+ * @param publicClient - The viem public client to use for transaction retrieval.
37
+ * @param rollupAddress - The address of the rollup contract.
38
+ * @param blockUntilSynced - If true, blocks until the archiver has fully synced.
39
+ * @param searchStartBlock - The block number to use for starting the search.
40
+ * @param searchEndBlock - The highest block number that we should search up to.
41
+ * @param expectedNextL2BlockNum - The next L2 block number that we expect to find.
42
+ * @returns An array of tuples representing block metadata including the header, archive tree snapshot, and associated l1 block number; as well as the next eth block to search from.
43
+ */
44
+ export async function retrieveBlockMetadataFromRollup(
45
+ publicClient: PublicClient,
46
+ rollupAddress: EthAddress,
47
+ blockUntilSynced: boolean,
48
+ searchStartBlock: bigint,
49
+ searchEndBlock: bigint,
50
+ expectedNextL2BlockNum: bigint,
51
+ ): Promise<DataRetrieval<[Header, AppendOnlyTreeSnapshot, bigint]>> {
52
+ const retrievedBlockMetadata: [Header, AppendOnlyTreeSnapshot, bigint][] = [];
53
+ do {
54
+ if (searchStartBlock > searchEndBlock) {
55
+ break;
56
+ }
57
+ const l2BlockProcessedLogs = await getL2BlockProcessedLogs(
58
+ publicClient,
59
+ rollupAddress,
60
+ searchStartBlock,
61
+ searchEndBlock,
62
+ );
63
+ if (l2BlockProcessedLogs.length === 0) {
64
+ break;
65
+ }
66
+
67
+ const newBlockMetadata = await processL2BlockProcessedLogs(
68
+ publicClient,
69
+ expectedNextL2BlockNum,
70
+ l2BlockProcessedLogs,
71
+ );
72
+ retrievedBlockMetadata.push(...newBlockMetadata);
73
+ searchStartBlock = l2BlockProcessedLogs[l2BlockProcessedLogs.length - 1].blockNumber! + 1n;
74
+ expectedNextL2BlockNum += BigInt(newBlockMetadata.length);
75
+ } while (blockUntilSynced && searchStartBlock <= searchEndBlock);
76
+ return { nextEthBlockNumber: searchStartBlock, retrievedData: retrievedBlockMetadata };
77
+ }
78
+
79
+ /**
80
+ * Fetches new L2 block bodies and their hashes.
81
+ * @param publicClient - The viem public client to use for transaction retrieval.
82
+ * @param availabilityOracleAddress - The address of the availability oracle contract.
83
+ * @param blockUntilSynced - If true, blocks until the archiver has fully synced.
84
+ * @param searchStartBlock - The block number to use for starting the search.
85
+ * @param searchEndBlock - The highest block number that we should search up to.
86
+ * @returns A array of tuples of L2 block bodies and their associated hash as well as the next eth block to search from
87
+ */
88
+ export async function retrieveBlockBodiesFromAvailabilityOracle(
89
+ publicClient: PublicClient,
90
+ availabilityOracleAddress: EthAddress,
91
+ blockUntilSynced: boolean,
92
+ searchStartBlock: bigint,
93
+ searchEndBlock: bigint,
94
+ ): Promise<DataRetrieval<[Body, Buffer]>> {
95
+ const retrievedBlockBodies: [Body, Buffer][] = [];
96
+
97
+ do {
98
+ if (searchStartBlock > searchEndBlock) {
99
+ break;
100
+ }
101
+ const l2TxsPublishedLogs = await getTxsPublishedLogs(
102
+ publicClient,
103
+ availabilityOracleAddress,
104
+ searchStartBlock,
105
+ searchEndBlock,
106
+ );
107
+ if (l2TxsPublishedLogs.length === 0) {
108
+ break;
109
+ }
110
+
111
+ const newBlockBodies = await processTxsPublishedLogs(publicClient, l2TxsPublishedLogs);
112
+ retrievedBlockBodies.push(...newBlockBodies);
113
+ searchStartBlock = l2TxsPublishedLogs[l2TxsPublishedLogs.length - 1].blockNumber! + 1n;
114
+ } while (blockUntilSynced && searchStartBlock <= searchEndBlock);
115
+ return { nextEthBlockNumber: searchStartBlock, retrievedData: retrievedBlockBodies };
116
+ }
117
+
118
+ /**
119
+ * Fetches new contract data.
120
+ * @param publicClient - The viem public client to use for transaction retrieval.
121
+ * @param contractDeploymentEmitterAddress - The address of the contract deployment emitter contract.
122
+ * @param blockUntilSynced - If true, blocks until the archiver has fully synced.
123
+ * @param searchStartBlock - The block number to use for starting the search.
124
+ * @param searchEndBlock - The highest block number that we should search up to.
125
+ * @param blockNumberToBodyHash - A mapping from block number to relevant body hash.
126
+ * @returns An array of ExtendedContractData and their equivalent L2 Block number along with the next eth block to search from..
127
+ */
128
+ export async function retrieveNewContractData(
129
+ publicClient: PublicClient,
130
+ contractDeploymentEmitterAddress: EthAddress,
131
+ blockUntilSynced: boolean,
132
+ searchStartBlock: bigint,
133
+ searchEndBlock: bigint,
134
+ blockNumberToBodyHash: { [key: number]: Buffer | undefined },
135
+ ): Promise<DataRetrieval<[ExtendedContractData[], number]>> {
136
+ let retrievedNewContracts: [ExtendedContractData[], number][] = [];
137
+ do {
138
+ if (searchStartBlock > searchEndBlock) {
139
+ break;
140
+ }
141
+ const contractDataLogs = await getContractDeploymentLogs(
142
+ publicClient,
143
+ contractDeploymentEmitterAddress,
144
+ searchStartBlock,
145
+ searchEndBlock,
146
+ );
147
+ if (contractDataLogs.length === 0) {
148
+ break;
149
+ }
150
+ const newContracts = processContractDeploymentLogs(blockNumberToBodyHash, contractDataLogs);
151
+ retrievedNewContracts = retrievedNewContracts.concat(newContracts);
152
+ searchStartBlock = (contractDataLogs.findLast(cd => !!cd)?.blockNumber || searchStartBlock) + 1n;
153
+ } while (blockUntilSynced && searchStartBlock <= searchEndBlock);
154
+ return { nextEthBlockNumber: searchStartBlock, retrievedData: retrievedNewContracts };
155
+ }
156
+
157
+ /**
158
+ * Fetch new pending L1 to L2 messages.
159
+ * @param publicClient - The viem public client to use for transaction retrieval.
160
+ * @param inboxAddress - The address of the inbox contract to fetch messages from.
161
+ * @param blockUntilSynced - If true, blocks until the archiver has fully synced.
162
+ * @param searchStartBlock - The block number to use for starting the search.
163
+ * @param searchEndBlock - The highest block number that we should search up to.
164
+ * @returns An array of L1ToL2Message and next eth block to search from.
165
+ */
166
+ export async function retrieveNewPendingL1ToL2Messages(
167
+ publicClient: PublicClient,
168
+ inboxAddress: EthAddress,
169
+ blockUntilSynced: boolean,
170
+ searchStartBlock: bigint,
171
+ searchEndBlock: bigint,
172
+ ): Promise<DataRetrieval<[L1ToL2Message, bigint]>> {
173
+ const retrievedNewL1ToL2Messages: [L1ToL2Message, bigint][] = [];
174
+ do {
175
+ if (searchStartBlock > searchEndBlock) {
176
+ break;
177
+ }
178
+ const newL1ToL2MessageLogs = await getPendingL1ToL2MessageLogs(
179
+ publicClient,
180
+ inboxAddress,
181
+ searchStartBlock,
182
+ searchEndBlock,
183
+ );
184
+ if (newL1ToL2MessageLogs.length === 0) {
185
+ break;
186
+ }
187
+ const newL1ToL2Messages = processPendingL1ToL2MessageAddedLogs(newL1ToL2MessageLogs);
188
+ retrievedNewL1ToL2Messages.push(...newL1ToL2Messages);
189
+ // handles the case when there are no new messages:
190
+ searchStartBlock = (newL1ToL2MessageLogs.findLast(msgLog => !!msgLog)?.blockNumber || searchStartBlock) + 1n;
191
+ } while (blockUntilSynced && searchStartBlock <= searchEndBlock);
192
+ return { nextEthBlockNumber: searchStartBlock, retrievedData: retrievedNewL1ToL2Messages };
193
+ }
194
+
195
+ /**
196
+ * Fetch newly cancelled L1 to L2 messages.
197
+ * @param publicClient - The viem public client to use for transaction retrieval.
198
+ * @param inboxAddress - The address of the inbox contract to fetch messages from.
199
+ * @param blockUntilSynced - If true, blocks until the archiver has fully synced.
200
+ * @param searchStartBlock - The block number to use for starting the search.
201
+ * @param searchEndBlock - The highest block number that we should search up to.
202
+ * @returns An array of entry keys that were cancelled and next eth block to search from.
203
+ */
204
+ export async function retrieveNewCancelledL1ToL2Messages(
205
+ publicClient: PublicClient,
206
+ inboxAddress: EthAddress,
207
+ blockUntilSynced: boolean,
208
+ searchStartBlock: bigint,
209
+ searchEndBlock: bigint,
210
+ ): Promise<DataRetrieval<[Fr, bigint]>> {
211
+ const retrievedNewCancelledL1ToL2Messages: [Fr, bigint][] = [];
212
+ do {
213
+ if (searchStartBlock > searchEndBlock) {
214
+ break;
215
+ }
216
+ const newL1ToL2MessageCancelledLogs = await getL1ToL2MessageCancelledLogs(
217
+ publicClient,
218
+ inboxAddress,
219
+ searchStartBlock,
220
+ searchEndBlock,
221
+ );
222
+ if (newL1ToL2MessageCancelledLogs.length === 0) {
223
+ break;
224
+ }
225
+ const newCancelledL1ToL2Messages = processCancelledL1ToL2MessagesLogs(newL1ToL2MessageCancelledLogs);
226
+ retrievedNewCancelledL1ToL2Messages.push(...newCancelledL1ToL2Messages);
227
+ // handles the case when there are no new messages:
228
+ searchStartBlock =
229
+ (newL1ToL2MessageCancelledLogs.findLast(msgLog => !!msgLog)?.blockNumber || searchStartBlock) + 1n;
230
+ } while (blockUntilSynced && searchStartBlock <= searchEndBlock);
231
+ return { nextEthBlockNumber: searchStartBlock, retrievedData: retrievedNewCancelledL1ToL2Messages };
232
+ }
@@ -0,0 +1,342 @@
1
+ import {
2
+ Body,
3
+ ContractData,
4
+ EncodedContractFunction,
5
+ ExtendedContractData,
6
+ L1Actor,
7
+ L1ToL2Message,
8
+ L2Actor,
9
+ } from '@aztec/circuit-types';
10
+ import { AppendOnlyTreeSnapshot, Header } from '@aztec/circuits.js';
11
+ import { AztecAddress } from '@aztec/foundation/aztec-address';
12
+ import { EthAddress } from '@aztec/foundation/eth-address';
13
+ import { Fr } from '@aztec/foundation/fields';
14
+ import { BufferReader, numToUInt32BE } from '@aztec/foundation/serialize';
15
+ import { AvailabilityOracleAbi, ContractDeploymentEmitterAbi, InboxAbi, RollupAbi } from '@aztec/l1-artifacts';
16
+
17
+ import { Hex, Log, PublicClient, decodeFunctionData, getAbiItem, getAddress, hexToBytes } from 'viem';
18
+
19
+ /**
20
+ * Processes newly received MessageAdded (L1 to L2) logs.
21
+ * @param logs - MessageAdded logs.
22
+ * @returns Array of all Pending L1 to L2 messages that were processed
23
+ */
24
+ export function processPendingL1ToL2MessageAddedLogs(
25
+ logs: Log<bigint, number, false, undefined, true, typeof InboxAbi, 'MessageAdded'>[],
26
+ ): [L1ToL2Message, bigint][] {
27
+ const l1ToL2Messages: [L1ToL2Message, bigint][] = [];
28
+ for (const log of logs) {
29
+ const { sender, senderChainId, recipient, recipientVersion, content, secretHash, deadline, fee, entryKey } =
30
+ log.args;
31
+ l1ToL2Messages.push([
32
+ new L1ToL2Message(
33
+ new L1Actor(EthAddress.fromString(sender), Number(senderChainId)),
34
+ new L2Actor(AztecAddress.fromString(recipient), Number(recipientVersion)),
35
+ Fr.fromString(content),
36
+ Fr.fromString(secretHash),
37
+ deadline,
38
+ Number(fee),
39
+ Fr.fromString(entryKey),
40
+ ),
41
+ log.blockNumber!,
42
+ ]);
43
+ }
44
+ return l1ToL2Messages;
45
+ }
46
+
47
+ /**
48
+ * Process newly received L1ToL2MessageCancelled logs.
49
+ * @param logs - L1ToL2MessageCancelled logs.
50
+ * @returns Array of entry keys of the L1 to L2 messages that were cancelled
51
+ */
52
+ export function processCancelledL1ToL2MessagesLogs(
53
+ logs: Log<bigint, number, false, undefined, true, typeof InboxAbi, 'L1ToL2MessageCancelled'>[],
54
+ ): [Fr, bigint][] {
55
+ const cancelledL1ToL2Messages: [Fr, bigint][] = [];
56
+ for (const log of logs) {
57
+ cancelledL1ToL2Messages.push([Fr.fromString(log.args.entryKey), log.blockNumber!]);
58
+ }
59
+ return cancelledL1ToL2Messages;
60
+ }
61
+
62
+ /**
63
+ * Processes newly received L2BlockProcessed logs.
64
+ * @param publicClient - The viem public client to use for transaction retrieval.
65
+ * @param expectedL2BlockNumber - The next expected L2 block number.
66
+ * @param logs - L2BlockProcessed logs.
67
+ * @returns - An array of tuples representing block metadata including the header, archive tree snapshot, and associated l1 block number.
68
+ */
69
+ export async function processL2BlockProcessedLogs(
70
+ publicClient: PublicClient,
71
+ expectedL2BlockNumber: bigint,
72
+ logs: Log<bigint, number, false, undefined, true, typeof RollupAbi, 'L2BlockProcessed'>[],
73
+ ): Promise<[Header, AppendOnlyTreeSnapshot, bigint][]> {
74
+ const retrievedBlockMetadata: [Header, AppendOnlyTreeSnapshot, bigint][] = [];
75
+ for (const log of logs) {
76
+ const blockNum = log.args.blockNumber;
77
+ if (blockNum !== expectedL2BlockNumber) {
78
+ throw new Error('Block number mismatch. Expected: ' + expectedL2BlockNumber + ' but got: ' + blockNum + '.');
79
+ }
80
+ // TODO: Fetch blocks from calldata in parallel
81
+ const [header, archive] = await getBlockMetadataFromRollupTx(
82
+ publicClient,
83
+ log.transactionHash!,
84
+ log.args.blockNumber,
85
+ );
86
+
87
+ retrievedBlockMetadata.push([header, archive, log.blockNumber!]);
88
+ expectedL2BlockNumber++;
89
+ }
90
+
91
+ return retrievedBlockMetadata;
92
+ }
93
+
94
+ export async function processTxsPublishedLogs(
95
+ publicClient: PublicClient,
96
+ logs: Log<bigint, number, false, undefined, true, typeof AvailabilityOracleAbi, 'TxsPublished'>[],
97
+ ): Promise<[Body, Buffer][]> {
98
+ const retrievedBlockBodies: [Body, Buffer][] = [];
99
+ for (const log of logs) {
100
+ const newBlockBody = await getBlockBodiesFromAvailabilityOracleTx(publicClient, log.transactionHash!);
101
+ retrievedBlockBodies.push([newBlockBody, Buffer.from(hexToBytes(log.args.txsHash))]);
102
+ }
103
+
104
+ return retrievedBlockBodies;
105
+ }
106
+
107
+ /**
108
+ * Gets block metadata (header and archive snapshot) from the calldata of an L1 transaction.
109
+ * Assumes that the block was published from an EOA.
110
+ * TODO: Add retries and error management.
111
+ * @param publicClient - The viem public client to use for transaction retrieval.
112
+ * @param txHash - Hash of the tx that published it.
113
+ * @param l2BlockNum - L2 block number.
114
+ * @returns L2 block metadata (header and archive) from the calldata, deserialized
115
+ */
116
+ async function getBlockMetadataFromRollupTx(
117
+ publicClient: PublicClient,
118
+ txHash: `0x${string}`,
119
+ l2BlockNum: bigint,
120
+ ): Promise<[Header, AppendOnlyTreeSnapshot]> {
121
+ const { input: data } = await publicClient.getTransaction({ hash: txHash });
122
+ const { functionName, args } = decodeFunctionData({
123
+ abi: RollupAbi,
124
+ data,
125
+ });
126
+
127
+ if (functionName !== 'process') {
128
+ throw new Error(`Unexpected method called ${functionName}`);
129
+ }
130
+ const [headerHex, archiveRootHex] = args! as readonly [Hex, Hex, Hex, Hex];
131
+
132
+ const header = Header.fromBuffer(Buffer.from(hexToBytes(headerHex)));
133
+
134
+ const blockNumberFromHeader = header.globalVariables.blockNumber.toBigInt();
135
+
136
+ if (blockNumberFromHeader !== l2BlockNum) {
137
+ throw new Error(`Block number mismatch: expected ${l2BlockNum} but got ${blockNumberFromHeader}`);
138
+ }
139
+
140
+ const archive = AppendOnlyTreeSnapshot.fromBuffer(
141
+ Buffer.concat([
142
+ Buffer.from(hexToBytes(archiveRootHex)), // L2Block.archive.root
143
+ numToUInt32BE(Number(l2BlockNum)), // L2Block.archive.nextAvailableLeafIndex
144
+ ]),
145
+ );
146
+
147
+ return [header, archive];
148
+ }
149
+
150
+ /**
151
+ * Gets block bodies from calldata of an L1 transaction, and deserializes them into Body objects.
152
+ * Assumes that the block was published from an EOA.
153
+ * TODO: Add retries and error management.
154
+ * @param publicClient - The viem public client to use for transaction retrieval.
155
+ * @param txHash - Hash of the tx that published it.
156
+ * @returns An L2 block body from the calldata, deserialized
157
+ */
158
+ async function getBlockBodiesFromAvailabilityOracleTx(
159
+ publicClient: PublicClient,
160
+ txHash: `0x${string}`,
161
+ ): Promise<Body> {
162
+ const { input: data } = await publicClient.getTransaction({ hash: txHash });
163
+ const { functionName, args } = decodeFunctionData({
164
+ abi: AvailabilityOracleAbi,
165
+ data,
166
+ });
167
+
168
+ if (functionName !== 'publish') {
169
+ throw new Error(`Unexpected method called ${functionName}`);
170
+ }
171
+
172
+ const [bodyHex] = args! as [Hex];
173
+
174
+ const blockBody = Body.fromBuffer(Buffer.from(hexToBytes(bodyHex)));
175
+
176
+ return blockBody;
177
+ }
178
+
179
+ /**
180
+ * Gets relevant `L2BlockProcessed` logs from chain.
181
+ * @param publicClient - The viem public client to use for transaction retrieval.
182
+ * @param rollupAddress - The address of the rollup contract.
183
+ * @param fromBlock - First block to get logs from (inclusive).
184
+ * @param toBlock - Last block to get logs from (inclusive).
185
+ * @returns An array of `L2BlockProcessed` logs.
186
+ */
187
+ export function getL2BlockProcessedLogs(
188
+ publicClient: PublicClient,
189
+ rollupAddress: EthAddress,
190
+ fromBlock: bigint,
191
+ toBlock: bigint,
192
+ ): Promise<Log<bigint, number, false, undefined, true, typeof RollupAbi, 'L2BlockProcessed'>[]> {
193
+ return publicClient.getLogs({
194
+ address: getAddress(rollupAddress.toString()),
195
+ event: getAbiItem({
196
+ abi: RollupAbi,
197
+ name: 'L2BlockProcessed',
198
+ }),
199
+ fromBlock,
200
+ toBlock: toBlock + 1n, // the toBlock argument in getLogs is exclusive
201
+ });
202
+ }
203
+
204
+ /**
205
+ * Gets relevant `TxsPublished` logs from chain.
206
+ * @param publicClient - The viem public client to use for transaction retrieval.
207
+ * @param dataAvailabilityOracleAddress - The address of the availability oracle contract.
208
+ * @param fromBlock - First block to get logs from (inclusive).
209
+ * @param toBlock - Last block to get logs from (inclusive).
210
+ * @returns An array of `TxsPublished` logs.
211
+ */
212
+ export function getTxsPublishedLogs(
213
+ publicClient: PublicClient,
214
+ dataAvailabilityOracleAddress: EthAddress,
215
+ fromBlock: bigint,
216
+ toBlock: bigint,
217
+ ): Promise<Log<bigint, number, false, undefined, true, typeof AvailabilityOracleAbi, 'TxsPublished'>[]> {
218
+ return publicClient.getLogs({
219
+ address: getAddress(dataAvailabilityOracleAddress.toString()),
220
+ event: getAbiItem({
221
+ abi: AvailabilityOracleAbi,
222
+ name: 'TxsPublished',
223
+ }),
224
+ fromBlock,
225
+ toBlock: toBlock + 1n, // the toBlock argument in getLogs is exclusive
226
+ });
227
+ }
228
+
229
+ /**
230
+ * Gets relevant `ContractDeployment` logs from chain.
231
+ * @param publicClient - The viem public client to use for transaction retrieval.
232
+ * @param contractDeploymentEmitterAddress - The address of the L2 contract deployment emitter contract.
233
+ * @param fromBlock - First block to get logs from (inclusive).
234
+ * @param toBlock - Last block to get logs from (inclusive).
235
+ * @returns An array of `ContractDeployment` logs.
236
+ */
237
+ export function getContractDeploymentLogs(
238
+ publicClient: PublicClient,
239
+ contractDeploymentEmitterAddress: EthAddress,
240
+ fromBlock: bigint,
241
+ toBlock: bigint,
242
+ ): Promise<Log<bigint, number, false, undefined, true, typeof ContractDeploymentEmitterAbi, 'ContractDeployment'>[]> {
243
+ return publicClient.getLogs({
244
+ address: getAddress(contractDeploymentEmitterAddress.toString()),
245
+ event: getAbiItem({
246
+ abi: ContractDeploymentEmitterAbi,
247
+ name: 'ContractDeployment',
248
+ }),
249
+ fromBlock,
250
+ toBlock: toBlock + 1n, // the toBlock argument in getLogs is exclusive
251
+ });
252
+ }
253
+
254
+ /**
255
+ * Processes newly received ContractDeployment logs.
256
+ * @param blockNumberToBodyHash - A mapping from block number to relevant body hash.
257
+ * @param logs - ContractDeployment logs.
258
+ * @returns The set of retrieved extended contract data items.
259
+ */
260
+ export function processContractDeploymentLogs(
261
+ blockNumberToBodyHash: { [key: number]: Buffer | undefined },
262
+ logs: Log<bigint, number, false, undefined, true, typeof ContractDeploymentEmitterAbi, 'ContractDeployment'>[],
263
+ ): [ExtendedContractData[], number][] {
264
+ const extendedContractData: [ExtendedContractData[], number][] = [];
265
+ for (let i = 0; i < logs.length; i++) {
266
+ const log = logs[i];
267
+ const l2BlockNum = Number(log.args.l2BlockNum);
268
+ const blockHash = Buffer.from(hexToBytes(log.args.l2BlockHash));
269
+ const expectedBlockHash = blockNumberToBodyHash[l2BlockNum];
270
+ if (expectedBlockHash === undefined || !blockHash.equals(expectedBlockHash)) {
271
+ continue;
272
+ }
273
+ const publicFnsReader = BufferReader.asReader(Buffer.from(log.args.acir.slice(2), 'hex'));
274
+ const contractClassId = Fr.fromBuffer(Buffer.from(hexToBytes(log.args.contractClassId)));
275
+ const saltedInitializationHash = Fr.fromBuffer(Buffer.from(hexToBytes(log.args.saltedInitializationHash)));
276
+ const publicKeyHash = Fr.fromBuffer(Buffer.from(hexToBytes(log.args.publicKeyHash)));
277
+
278
+ const contractData = new ExtendedContractData(
279
+ new ContractData(AztecAddress.fromString(log.args.aztecAddress), EthAddress.fromString(log.args.portalAddress)),
280
+ publicFnsReader.readVector(EncodedContractFunction),
281
+ contractClassId,
282
+ saltedInitializationHash,
283
+ publicKeyHash,
284
+ );
285
+ if (extendedContractData[i]) {
286
+ extendedContractData[i][0].push(contractData);
287
+ } else {
288
+ extendedContractData[i] = [[contractData], l2BlockNum];
289
+ }
290
+ }
291
+ return extendedContractData;
292
+ }
293
+
294
+ /**
295
+ * Get relevant `MessageAdded` logs emitted by Inbox on chain.
296
+ * @param publicClient - The viem public client to use for transaction retrieval.
297
+ * @param inboxAddress - The address of the inbox contract.
298
+ * @param fromBlock - First block to get logs from (inclusive).
299
+ * @param toBlock - Last block to get logs from (inclusive).
300
+ * @returns An array of `MessageAdded` logs.
301
+ */
302
+ export function getPendingL1ToL2MessageLogs(
303
+ publicClient: PublicClient,
304
+ inboxAddress: EthAddress,
305
+ fromBlock: bigint,
306
+ toBlock: bigint,
307
+ ): Promise<Log<bigint, number, false, undefined, true, typeof InboxAbi, 'MessageAdded'>[]> {
308
+ return publicClient.getLogs({
309
+ address: getAddress(inboxAddress.toString()),
310
+ event: getAbiItem({
311
+ abi: InboxAbi,
312
+ name: 'MessageAdded',
313
+ }),
314
+ fromBlock,
315
+ toBlock: toBlock + 1n, // the toBlock argument in getLogs is exclusive
316
+ });
317
+ }
318
+
319
+ /**
320
+ * Get relevant `L1ToL2MessageCancelled` logs emitted by Inbox on chain when pending messages are cancelled
321
+ * @param publicClient - The viem public client to use for transaction retrieval.
322
+ * @param inboxAddress - The address of the inbox contract.
323
+ * @param fromBlock - First block to get logs from (inclusive).
324
+ * @param toBlock - Last block to get logs from (inclusive).
325
+ * @returns An array of `L1ToL2MessageCancelled` logs.
326
+ */
327
+ export function getL1ToL2MessageCancelledLogs(
328
+ publicClient: PublicClient,
329
+ inboxAddress: EthAddress,
330
+ fromBlock: bigint,
331
+ toBlock: bigint,
332
+ ): Promise<Log<bigint, number, false, undefined, true, typeof InboxAbi, 'L1ToL2MessageCancelled'>[]> {
333
+ return publicClient.getLogs({
334
+ address: getAddress(inboxAddress.toString()),
335
+ event: getAbiItem({
336
+ abi: InboxAbi,
337
+ name: 'L1ToL2MessageCancelled',
338
+ }),
339
+ fromBlock,
340
+ toBlock: toBlock + 1n, // the toBlock argument in getLogs is exclusive
341
+ });
342
+ }
@@ -0,0 +1,5 @@
1
+ export * from './archiver.js';
2
+ export * from './config.js';
3
+ export { MemoryArchiverStore } from './memory_archiver_store/memory_archiver_store.js';
4
+ export { ArchiverDataStore } from './archiver_store.js';
5
+ export { KVArchiverDataStore } from './kv_archiver_store/kv_archiver_store.js';