@aztec/archiver 0.24.0 → 0.26.2

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 (61) 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 +95 -92
  46. package/src/archiver/archiver_store.ts +42 -15
  47. package/src/archiver/archiver_store_test_suite.ts +73 -58
  48. package/src/archiver/data_retrieval.ts +61 -16
  49. package/src/archiver/eth_log_handlers.ts +142 -68
  50. package/src/archiver/kv_archiver_store/block_body_store.ts +54 -0
  51. package/src/archiver/kv_archiver_store/block_store.ts +71 -36
  52. package/src/archiver/kv_archiver_store/contract_class_store.ts +4 -0
  53. package/src/archiver/kv_archiver_store/contract_store.ts +7 -2
  54. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +56 -20
  55. package/src/archiver/kv_archiver_store/log_store.ts +2 -2
  56. package/src/archiver/kv_archiver_store/message_store.ts +34 -30
  57. package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +17 -17
  58. package/src/archiver/memory_archiver_store/memory_archiver_store.ts +91 -29
  59. package/src/index.ts +1 -0
  60. package/src/rpc/archiver_client.ts +3 -1
  61. package/src/rpc/archiver_server.ts +5 -1
@@ -1,17 +1,18 @@
1
1
  import {
2
+ Body,
2
3
  ContractData,
3
4
  EncodedContractFunction,
4
5
  ExtendedContractData,
5
6
  L1Actor,
6
7
  L1ToL2Message,
7
8
  L2Actor,
8
- L2Block,
9
9
  } from '@aztec/circuit-types';
10
+ import { AppendOnlyTreeSnapshot, Header } from '@aztec/circuits.js';
10
11
  import { AztecAddress } from '@aztec/foundation/aztec-address';
11
12
  import { EthAddress } from '@aztec/foundation/eth-address';
12
13
  import { Fr } from '@aztec/foundation/fields';
13
14
  import { BufferReader, numToUInt32BE } from '@aztec/foundation/serialize';
14
- import { ContractDeploymentEmitterAbi, InboxAbi, RollupAbi } from '@aztec/l1-artifacts';
15
+ import { AvailabilityOracleAbi, ContractDeploymentEmitterAbi, InboxAbi, RollupAbi } from '@aztec/l1-artifacts';
15
16
 
16
17
  import { Hex, Log, PublicClient, decodeFunctionData, getAbiItem, getAddress, hexToBytes } from 'viem';
17
18
 
@@ -21,7 +22,7 @@ import { Hex, Log, PublicClient, decodeFunctionData, getAbiItem, getAddress, hex
21
22
  * @returns Array of all Pending L1 to L2 messages that were processed
22
23
  */
23
24
  export function processPendingL1ToL2MessageAddedLogs(
24
- logs: Log<bigint, number, undefined, true, typeof InboxAbi, 'MessageAdded'>[],
25
+ logs: Log<bigint, number, false, undefined, true, typeof InboxAbi, 'MessageAdded'>[],
25
26
  ): [L1ToL2Message, bigint][] {
26
27
  const l1ToL2Messages: [L1ToL2Message, bigint][] = [];
27
28
  for (const log of logs) {
@@ -46,10 +47,10 @@ export function processPendingL1ToL2MessageAddedLogs(
46
47
  /**
47
48
  * Process newly received L1ToL2MessageCancelled logs.
48
49
  * @param logs - L1ToL2MessageCancelled logs.
49
- * @returns Array of message keys of the L1 to L2 messages that were cancelled
50
+ * @returns Array of entry keys of the L1 to L2 messages that were cancelled
50
51
  */
51
52
  export function processCancelledL1ToL2MessagesLogs(
52
- logs: Log<bigint, number, undefined, true, typeof InboxAbi, 'L1ToL2MessageCancelled'>[],
53
+ logs: Log<bigint, number, false, undefined, true, typeof InboxAbi, 'L1ToL2MessageCancelled'>[],
53
54
  ): [Fr, bigint][] {
54
55
  const cancelledL1ToL2Messages: [Fr, bigint][] = [];
55
56
  for (const log of logs) {
@@ -63,62 +64,116 @@ export function processCancelledL1ToL2MessagesLogs(
63
64
  * @param publicClient - The viem public client to use for transaction retrieval.
64
65
  * @param expectedL2BlockNumber - The next expected L2 block number.
65
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.
66
68
  */
67
- export async function processBlockLogs(
69
+ export async function processL2BlockProcessedLogs(
68
70
  publicClient: PublicClient,
69
71
  expectedL2BlockNumber: bigint,
70
- logs: Log<bigint, number, undefined, true, typeof RollupAbi, 'L2BlockProcessed'>[],
71
- ): Promise<L2Block[]> {
72
- const retrievedBlocks: L2Block[] = [];
72
+ logs: Log<bigint, number, false, undefined, true, typeof RollupAbi, 'L2BlockProcessed'>[],
73
+ ): Promise<[Header, AppendOnlyTreeSnapshot, bigint][]> {
74
+ const retrievedBlockMetadata: [Header, AppendOnlyTreeSnapshot, bigint][] = [];
73
75
  for (const log of logs) {
74
76
  const blockNum = log.args.blockNumber;
75
77
  if (blockNum !== expectedL2BlockNumber) {
76
78
  throw new Error('Block number mismatch. Expected: ' + expectedL2BlockNumber + ' but got: ' + blockNum + '.');
77
79
  }
78
80
  // TODO: Fetch blocks from calldata in parallel
79
- const newBlock = await getBlockFromCallData(publicClient, log.transactionHash!, log.args.blockNumber);
80
- newBlock.setL1BlockNumber(log.blockNumber!);
81
- retrievedBlocks.push(newBlock);
81
+ const [header, archive] = await getBlockMetadataFromRollupTx(
82
+ publicClient,
83
+ log.transactionHash!,
84
+ log.args.blockNumber,
85
+ );
86
+
87
+ retrievedBlockMetadata.push([header, archive, log.blockNumber!]);
82
88
  expectedL2BlockNumber++;
83
89
  }
84
- return retrievedBlocks;
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;
85
105
  }
86
106
 
87
107
  /**
88
- * Builds an L2 block out of calldata from the tx that published it.
108
+ * Gets block metadata (header and archive snapshot) from the calldata of an L1 transaction.
89
109
  * Assumes that the block was published from an EOA.
90
110
  * TODO: Add retries and error management.
91
111
  * @param publicClient - The viem public client to use for transaction retrieval.
92
112
  * @param txHash - Hash of the tx that published it.
93
113
  * @param l2BlockNum - L2 block number.
94
- * @returns An L2 block deserialized from the calldata.
114
+ * @returns L2 block metadata (header and archive) from the calldata, deserialized
95
115
  */
96
- async function getBlockFromCallData(
116
+ async function getBlockMetadataFromRollupTx(
97
117
  publicClient: PublicClient,
98
118
  txHash: `0x${string}`,
99
119
  l2BlockNum: bigint,
100
- ): Promise<L2Block> {
120
+ ): Promise<[Header, AppendOnlyTreeSnapshot]> {
101
121
  const { input: data } = await publicClient.getTransaction({ hash: txHash });
102
- // TODO: File a bug in viem who complains if we dont remove the ctor from the abi here
103
122
  const { functionName, args } = decodeFunctionData({
104
- abi: RollupAbi.filter(item => item.type.toString() !== 'constructor'),
123
+ abi: RollupAbi,
105
124
  data,
106
125
  });
126
+
107
127
  if (functionName !== 'process') {
108
128
  throw new Error(`Unexpected method called ${functionName}`);
109
129
  }
110
- const [headerHex, archiveRootHex, , bodyHex] = args! as [Hex, Hex, Hex, Hex, Hex];
111
- const blockBuffer = Buffer.concat([
112
- Buffer.from(hexToBytes(headerHex)),
113
- Buffer.from(hexToBytes(archiveRootHex)), // L2Block.archive.root
114
- numToUInt32BE(Number(l2BlockNum)), // L2Block.archive.nextAvailableLeafIndex
115
- Buffer.from(hexToBytes(bodyHex)),
116
- ]);
117
- const block = L2Block.fromBufferWithLogs(blockBuffer);
118
- if (BigInt(block.number) !== l2BlockNum) {
119
- throw new Error(`Block number mismatch: expected ${l2BlockNum} but got ${block.number}`);
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}`);
120
170
  }
121
- return block;
171
+
172
+ const [bodyHex] = args! as [Hex];
173
+
174
+ const blockBody = Body.fromBuffer(Buffer.from(hexToBytes(bodyHex)));
175
+
176
+ return blockBody;
122
177
  }
123
178
 
124
179
  /**
@@ -129,21 +184,43 @@ async function getBlockFromCallData(
129
184
  * @param toBlock - Last block to get logs from (inclusive).
130
185
  * @returns An array of `L2BlockProcessed` logs.
131
186
  */
132
- export async function getL2BlockProcessedLogs(
187
+ export function getL2BlockProcessedLogs(
133
188
  publicClient: PublicClient,
134
189
  rollupAddress: EthAddress,
135
190
  fromBlock: bigint,
136
191
  toBlock: bigint,
137
- ) {
138
- // Note: For some reason the return type of `getLogs` would not get correctly derived if I didn't set the abiItem
139
- // as a standalone constant.
140
- const abiItem = getAbiItem({
141
- abi: RollupAbi,
142
- name: 'L2BlockProcessed',
143
- });
144
- return await publicClient.getLogs<typeof abiItem, true>({
192
+ ): Promise<Log<bigint, number, false, undefined, true, typeof RollupAbi, 'L2BlockProcessed'>[]> {
193
+ return publicClient.getLogs({
145
194
  address: getAddress(rollupAddress.toString()),
146
- event: abiItem,
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
+ }),
147
224
  fromBlock,
148
225
  toBlock: toBlock + 1n, // the toBlock argument in getLogs is exclusive
149
226
  });
@@ -157,19 +234,18 @@ export async function getL2BlockProcessedLogs(
157
234
  * @param toBlock - Last block to get logs from (inclusive).
158
235
  * @returns An array of `ContractDeployment` logs.
159
236
  */
160
- export async function getContractDeploymentLogs(
237
+ export function getContractDeploymentLogs(
161
238
  publicClient: PublicClient,
162
239
  contractDeploymentEmitterAddress: EthAddress,
163
240
  fromBlock: bigint,
164
241
  toBlock: bigint,
165
- ): Promise<Log<bigint, number, undefined, true, typeof ContractDeploymentEmitterAbi, 'ContractDeployment'>[]> {
166
- const abiItem = getAbiItem({
167
- abi: ContractDeploymentEmitterAbi,
168
- name: 'ContractDeployment',
169
- });
170
- return await publicClient.getLogs({
242
+ ): Promise<Log<bigint, number, false, undefined, true, typeof ContractDeploymentEmitterAbi, 'ContractDeployment'>[]> {
243
+ return publicClient.getLogs({
171
244
  address: getAddress(contractDeploymentEmitterAddress.toString()),
172
- event: abiItem,
245
+ event: getAbiItem({
246
+ abi: ContractDeploymentEmitterAbi,
247
+ name: 'ContractDeployment',
248
+ }),
173
249
  fromBlock,
174
250
  toBlock: toBlock + 1n, // the toBlock argument in getLogs is exclusive
175
251
  });
@@ -177,20 +253,20 @@ export async function getContractDeploymentLogs(
177
253
 
178
254
  /**
179
255
  * Processes newly received ContractDeployment logs.
180
- * @param blockHashMapping - A mapping from block number to relevant block hash.
256
+ * @param blockNumberToBodyHash - A mapping from block number to relevant body hash.
181
257
  * @param logs - ContractDeployment logs.
182
258
  * @returns The set of retrieved extended contract data items.
183
259
  */
184
260
  export function processContractDeploymentLogs(
185
- blockHashMapping: { [key: number]: Buffer | undefined },
186
- logs: Log<bigint, number, undefined, true, typeof ContractDeploymentEmitterAbi, 'ContractDeployment'>[],
261
+ blockNumberToBodyHash: { [key: number]: Buffer | undefined },
262
+ logs: Log<bigint, number, false, undefined, true, typeof ContractDeploymentEmitterAbi, 'ContractDeployment'>[],
187
263
  ): [ExtendedContractData[], number][] {
188
264
  const extendedContractData: [ExtendedContractData[], number][] = [];
189
265
  for (let i = 0; i < logs.length; i++) {
190
266
  const log = logs[i];
191
267
  const l2BlockNum = Number(log.args.l2BlockNum);
192
268
  const blockHash = Buffer.from(hexToBytes(log.args.l2BlockHash));
193
- const expectedBlockHash = blockHashMapping[l2BlockNum];
269
+ const expectedBlockHash = blockNumberToBodyHash[l2BlockNum];
194
270
  if (expectedBlockHash === undefined || !blockHash.equals(expectedBlockHash)) {
195
271
  continue;
196
272
  }
@@ -223,19 +299,18 @@ export function processContractDeploymentLogs(
223
299
  * @param toBlock - Last block to get logs from (inclusive).
224
300
  * @returns An array of `MessageAdded` logs.
225
301
  */
226
- export async function getPendingL1ToL2MessageLogs(
302
+ export function getPendingL1ToL2MessageLogs(
227
303
  publicClient: PublicClient,
228
304
  inboxAddress: EthAddress,
229
305
  fromBlock: bigint,
230
306
  toBlock: bigint,
231
- ): Promise<Log<bigint, number, undefined, true, typeof InboxAbi, 'MessageAdded'>[]> {
232
- const abiItem = getAbiItem({
233
- abi: InboxAbi,
234
- name: 'MessageAdded',
235
- });
236
- return await publicClient.getLogs({
307
+ ): Promise<Log<bigint, number, false, undefined, true, typeof InboxAbi, 'MessageAdded'>[]> {
308
+ return publicClient.getLogs({
237
309
  address: getAddress(inboxAddress.toString()),
238
- event: abiItem,
310
+ event: getAbiItem({
311
+ abi: InboxAbi,
312
+ name: 'MessageAdded',
313
+ }),
239
314
  fromBlock,
240
315
  toBlock: toBlock + 1n, // the toBlock argument in getLogs is exclusive
241
316
  });
@@ -249,19 +324,18 @@ export async function getPendingL1ToL2MessageLogs(
249
324
  * @param toBlock - Last block to get logs from (inclusive).
250
325
  * @returns An array of `L1ToL2MessageCancelled` logs.
251
326
  */
252
- export async function getL1ToL2MessageCancelledLogs(
327
+ export function getL1ToL2MessageCancelledLogs(
253
328
  publicClient: PublicClient,
254
329
  inboxAddress: EthAddress,
255
330
  fromBlock: bigint,
256
331
  toBlock: bigint,
257
- ): Promise<Log<bigint, number, undefined, true, typeof InboxAbi, 'L1ToL2MessageCancelled'>[]> {
258
- const abiItem = getAbiItem({
259
- abi: InboxAbi,
260
- name: 'L1ToL2MessageCancelled',
261
- });
262
- return await publicClient.getLogs({
332
+ ): Promise<Log<bigint, number, false, undefined, true, typeof InboxAbi, 'L1ToL2MessageCancelled'>[]> {
333
+ return publicClient.getLogs({
263
334
  address: getAddress(inboxAddress.toString()),
264
- event: abiItem,
335
+ event: getAbiItem({
336
+ abi: InboxAbi,
337
+ name: 'L1ToL2MessageCancelled',
338
+ }),
265
339
  fromBlock,
266
340
  toBlock: toBlock + 1n, // the toBlock argument in getLogs is exclusive
267
341
  });
@@ -0,0 +1,54 @@
1
+ import { Body } from '@aztec/circuit-types';
2
+ import { AztecKVStore, AztecMap } from '@aztec/kv-store';
3
+
4
+ export class BlockBodyStore {
5
+ /** Map block body hash to block body */
6
+ #blockBodies: AztecMap<string, Buffer>;
7
+
8
+ constructor(private db: AztecKVStore) {
9
+ this.#blockBodies = db.openMap('archiver_block_bodies');
10
+ }
11
+
12
+ /**
13
+ * Append new block bodies to the store's map.
14
+ * @param blockBodies - The L2 block bodies to be added to the store.
15
+ * @returns True if the operation is successful.
16
+ */
17
+ addBlockBodies(blockBodies: Body[]): Promise<boolean> {
18
+ return this.db.transaction(() => {
19
+ for (const body of blockBodies) {
20
+ void this.#blockBodies.set(body.getCalldataHash().toString('hex'), body.toBuffer());
21
+ }
22
+
23
+ return true;
24
+ });
25
+ }
26
+
27
+ /**
28
+ * Gets a list of L2 block bodies with its associated txsHashes
29
+ * @param txsHashes - The txsHashes list that corresponds to the blockBodies we want to retrieve
30
+ * @returns The requested L2 block bodies
31
+ */
32
+ async getBlockBodies(txsHashes: Buffer[]): Promise<Body[]> {
33
+ const blockBodiesBuffer = await this.db.transaction(() =>
34
+ txsHashes.map(txsHash => this.#blockBodies.get(txsHash.toString('hex'))),
35
+ );
36
+
37
+ if (blockBodiesBuffer.some(bodyBuffer => bodyBuffer === undefined)) {
38
+ throw new Error('Block body buffer is undefined');
39
+ }
40
+
41
+ return blockBodiesBuffer.map(blockBodyBuffer => Body.fromBuffer(blockBodyBuffer!));
42
+ }
43
+
44
+ /**
45
+ * Gets an L2 block body.
46
+ * @param txsHash - The txHash of the the block body to return
47
+ * @returns The requested L2 block body
48
+ */
49
+ getBlockBody(txsHash: Buffer): Body | undefined {
50
+ const blockBody = this.#blockBodies.get(txsHash.toString('hex'));
51
+
52
+ return blockBody && Body.fromBuffer(blockBody);
53
+ }
54
+ }
@@ -1,14 +1,16 @@
1
- import { INITIAL_L2_BLOCK_NUM, L2Block, L2Tx, TxHash } from '@aztec/circuit-types';
2
- import { AztecAddress } from '@aztec/circuits.js';
1
+ import { L2Block, TxEffect, TxHash, TxReceipt, TxStatus } from '@aztec/circuit-types';
2
+ import { AppendOnlyTreeSnapshot, AztecAddress, Header, INITIAL_L2_BLOCK_NUM } from '@aztec/circuits.js';
3
3
  import { createDebugLogger } from '@aztec/foundation/log';
4
4
  import { AztecKVStore, AztecMap, Range } from '@aztec/kv-store';
5
5
 
6
+ import { BlockBodyStore } from './block_body_store.js';
7
+
6
8
  type BlockIndexValue = [blockNumber: number, index: number];
7
9
 
8
- type BlockContext = {
9
- blockNumber: number;
10
+ type BlockStorage = {
10
11
  l1BlockNumber: bigint;
11
- block: Buffer;
12
+ header: Buffer;
13
+ archive: Buffer;
12
14
  };
13
15
 
14
16
  /**
@@ -16,7 +18,7 @@ type BlockContext = {
16
18
  */
17
19
  export class BlockStore {
18
20
  /** Map block number to block data */
19
- #blocks: AztecMap<number, BlockContext>;
21
+ #blocks: AztecMap<number, BlockStorage>;
20
22
 
21
23
  /** Index mapping transaction hash (as a string) to its location in a block */
22
24
  #txIndex: AztecMap<string, BlockIndexValue>;
@@ -26,9 +28,12 @@ export class BlockStore {
26
28
 
27
29
  #log = createDebugLogger('aztec:archiver:block_store');
28
30
 
29
- constructor(private db: AztecKVStore) {
30
- this.#blocks = db.openMap('archiver_blocks');
31
+ #blockBodyStore: BlockBodyStore;
32
+
33
+ constructor(private db: AztecKVStore, blockBodyStore: BlockBodyStore) {
34
+ this.#blockBodyStore = blockBodyStore;
31
35
 
36
+ this.#blocks = db.openMap('archiver_blocks');
32
37
  this.#txIndex = db.openMap('archiver_tx_index');
33
38
  this.#contractIndex = db.openMap('archiver_contract_index');
34
39
  }
@@ -42,25 +47,24 @@ export class BlockStore {
42
47
  return this.db.transaction(() => {
43
48
  for (const block of blocks) {
44
49
  void this.#blocks.set(block.number, {
45
- blockNumber: block.number,
46
- block: block.toBuffer(),
50
+ header: block.header.toBuffer(),
51
+ archive: block.archive.toBuffer(),
47
52
  l1BlockNumber: block.getL1BlockNumber(),
48
53
  });
49
54
 
50
- for (const [i, tx] of block.getTxs().entries()) {
51
- if (tx.txHash.isZero()) {
52
- continue;
53
- }
55
+ block.getTxs().forEach((tx, i) => {
54
56
  void this.#txIndex.set(tx.txHash.toString(), [block.number, i]);
55
- }
57
+ });
56
58
 
57
- for (const [i, contractData] of block.newContractData.entries()) {
58
- if (contractData.contractAddress.isZero()) {
59
- continue;
60
- }
59
+ block.body.txEffects
60
+ .flatMap(txEffect => txEffect.contractData)
61
+ .forEach((contractData, i) => {
62
+ if (contractData.contractAddress.isZero()) {
63
+ return;
64
+ }
61
65
 
62
- void this.#contractIndex.set(contractData.contractAddress.toString(), [block.number, i]);
63
- }
66
+ void this.#contractIndex.set(contractData.contractAddress.toString(), [block.number, i]);
67
+ });
64
68
  }
65
69
 
66
70
  return true;
@@ -71,35 +75,51 @@ export class BlockStore {
71
75
  * Gets up to `limit` amount of L2 blocks starting from `from`.
72
76
  * @param start - Number of the first block to return (inclusive).
73
77
  * @param limit - The number of blocks to return.
74
- * @returns The requested L2 blocks, without logs attached
78
+ * @returns The requested L2 blocks
75
79
  */
76
80
  *getBlocks(start: number, limit: number): IterableIterator<L2Block> {
77
- for (const blockCtx of this.#blocks.values(this.#computeBlockRange(start, limit))) {
78
- yield L2Block.fromBuffer(blockCtx.block);
81
+ for (const blockStorage of this.#blocks.values(this.#computeBlockRange(start, limit))) {
82
+ yield this.getBlockFromBlockStorage(blockStorage);
79
83
  }
80
84
  }
81
85
 
82
86
  /**
83
87
  * Gets an L2 block.
84
88
  * @param blockNumber - The number of the block to return.
85
- * @returns The requested L2 block, without logs attached
89
+ * @returns The requested L2 block.
86
90
  */
87
91
  getBlock(blockNumber: number): L2Block | undefined {
88
- const blockCtx = this.#blocks.get(blockNumber);
89
- if (!blockCtx || !blockCtx.block) {
92
+ const blockStorage = this.#blocks.get(blockNumber);
93
+ if (!blockStorage || !blockStorage.header) {
90
94
  return undefined;
91
95
  }
92
96
 
93
- return L2Block.fromBuffer(blockCtx.block);
97
+ return this.getBlockFromBlockStorage(blockStorage);
98
+ }
99
+
100
+ private getBlockFromBlockStorage(blockStorage: BlockStorage) {
101
+ const header = Header.fromBuffer(blockStorage.header);
102
+ const archive = AppendOnlyTreeSnapshot.fromBuffer(blockStorage.archive);
103
+ const body = this.#blockBodyStore.getBlockBody(header.contentCommitment.txsHash);
104
+
105
+ if (body === undefined) {
106
+ throw new Error('Body is not able to be retrieved from BodyStore');
107
+ }
108
+
109
+ return L2Block.fromFields({
110
+ header,
111
+ archive,
112
+ body,
113
+ });
94
114
  }
95
115
 
96
116
  /**
97
- * Gets an l2 tx.
98
- * @param txHash - The txHash of the l2 tx.
99
- * @returns The requested L2 tx.
117
+ * Gets a tx effect.
118
+ * @param txHash - The txHash of the tx corresponding to the tx effect.
119
+ * @returns The requested tx effect (or undefined if not found).
100
120
  */
101
- getL2Tx(txHash: TxHash): L2Tx | undefined {
102
- const [blockNumber, txIndex] = this.getL2TxLocation(txHash) ?? [];
121
+ getTxEffect(txHash: TxHash): TxEffect | undefined {
122
+ const [blockNumber, txIndex] = this.getTxLocation(txHash) ?? [];
103
123
  if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') {
104
124
  return undefined;
105
125
  }
@@ -109,11 +129,26 @@ export class BlockStore {
109
129
  }
110
130
 
111
131
  /**
112
- * Looks up which block included the requested L2 tx.
113
- * @param txHash - The txHash of the l2 tx.
132
+ * Gets a receipt of a settled tx.
133
+ * @param txHash - The hash of a tx we try to get the receipt for.
134
+ * @returns The requested tx receipt (or undefined if not found).
135
+ */
136
+ getSettledTxReceipt(txHash: TxHash): TxReceipt | undefined {
137
+ const [blockNumber, txIndex] = this.getTxLocation(txHash) ?? [];
138
+ if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') {
139
+ return undefined;
140
+ }
141
+
142
+ const block = this.getBlock(blockNumber)!;
143
+ return new TxReceipt(txHash, TxStatus.MINED, '', block.hash().toBuffer(), block.number);
144
+ }
145
+
146
+ /**
147
+ * Looks up which block included the requested tx effect.
148
+ * @param txHash - The txHash of the tx.
114
149
  * @returns The block number and index of the tx.
115
150
  */
116
- getL2TxLocation(txHash: TxHash): [blockNumber: number, txIndex: number] | undefined {
151
+ getTxLocation(txHash: TxHash): [blockNumber: number, txIndex: number] | undefined {
117
152
  return this.#txIndex.get(txHash.toString());
118
153
  }
119
154
 
@@ -21,6 +21,10 @@ export class ContractClassStore {
21
21
  const contractClass = this.#contractClasses.get(id.toString());
22
22
  return contractClass && { ...deserializeContractClassPublic(contractClass), id };
23
23
  }
24
+
25
+ getContractClassIds(): Fr[] {
26
+ return Array.from(this.#contractClasses.keys()).map(key => Fr.fromString(key));
27
+ }
24
28
  }
25
29
 
26
30
  export function serializeContractClassPublic(contractClass: ContractClassPublic): Buffer {
@@ -77,7 +77,12 @@ export class ContractStore {
77
77
  }
78
78
 
79
79
  const block = this.#blockStore.getBlock(blockNumber);
80
- return block?.newContractData[index];
80
+
81
+ if (block?.body.txEffects[index].contractData.length !== 1) {
82
+ throw new Error(`Contract data at block: ${blockNumber}, tx: ${index} does not have length of 1`);
83
+ }
84
+
85
+ return block?.body.txEffects[index].contractData[0];
81
86
  }
82
87
 
83
88
  /**
@@ -88,6 +93,6 @@ export class ContractStore {
88
93
  */
89
94
  getContractDataInBlock(blockNumber: number): ContractData[] {
90
95
  const block = this.#blockStore.getBlock(blockNumber);
91
- return block?.newContractData ?? [];
96
+ return block?.body.txEffects.flatMap(txEffect => txEffect.contractData) ?? [];
92
97
  }
93
98
  }