@aztec/archiver 0.71.0 → 0.73.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.
Files changed (70) hide show
  1. package/dest/archiver/archiver.d.ts +6 -6
  2. package/dest/archiver/archiver.d.ts.map +1 -1
  3. package/dest/archiver/archiver.js +35 -20
  4. package/dest/archiver/archiver_store.d.ts +6 -6
  5. package/dest/archiver/archiver_store.d.ts.map +1 -1
  6. package/dest/archiver/archiver_store_test_suite.d.ts +1 -1
  7. package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
  8. package/dest/archiver/archiver_store_test_suite.js +103 -65
  9. package/dest/archiver/config.d.ts +2 -2
  10. package/dest/archiver/config.d.ts.map +1 -1
  11. package/dest/archiver/config.js +5 -5
  12. package/dest/archiver/data_retrieval.d.ts +3 -2
  13. package/dest/archiver/data_retrieval.d.ts.map +1 -1
  14. package/dest/archiver/data_retrieval.js +69 -16
  15. package/dest/archiver/errors.d.ts +4 -0
  16. package/dest/archiver/errors.d.ts.map +1 -0
  17. package/dest/archiver/errors.js +6 -0
  18. package/dest/archiver/kv_archiver_store/block_store.d.ts +16 -16
  19. package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
  20. package/dest/archiver/kv_archiver_store/block_store.js +53 -53
  21. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +5 -5
  22. package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
  23. package/dest/archiver/kv_archiver_store/contract_class_store.js +13 -12
  24. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +3 -3
  25. package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
  26. package/dest/archiver/kv_archiver_store/contract_instance_store.js +3 -3
  27. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +7 -11
  28. package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
  29. package/dest/archiver/kv_archiver_store/kv_archiver_store.js +41 -63
  30. package/dest/archiver/kv_archiver_store/log_store.d.ts +8 -8
  31. package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
  32. package/dest/archiver/kv_archiver_store/log_store.js +122 -89
  33. package/dest/archiver/kv_archiver_store/message_store.d.ts +6 -6
  34. package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
  35. package/dest/archiver/kv_archiver_store/message_store.js +16 -16
  36. package/dest/archiver/kv_archiver_store/nullifier_store.d.ts +2 -2
  37. package/dest/archiver/kv_archiver_store/nullifier_store.d.ts.map +1 -1
  38. package/dest/archiver/kv_archiver_store/nullifier_store.js +31 -22
  39. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +9 -9
  40. package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
  41. package/dest/archiver/memory_archiver_store/memory_archiver_store.js +72 -71
  42. package/dest/factory.js +8 -8
  43. package/dest/rpc/index.d.ts +1 -1
  44. package/dest/rpc/index.d.ts.map +1 -1
  45. package/dest/rpc/index.js +5 -5
  46. package/dest/test/mock_archiver.d.ts +1 -1
  47. package/dest/test/mock_archiver.d.ts.map +1 -1
  48. package/dest/test/mock_archiver.js +2 -1
  49. package/dest/test/mock_l2_block_source.d.ts +3 -3
  50. package/dest/test/mock_l2_block_source.d.ts.map +1 -1
  51. package/dest/test/mock_l2_block_source.js +14 -11
  52. package/package.json +13 -13
  53. package/src/archiver/archiver.ts +46 -25
  54. package/src/archiver/archiver_store.ts +6 -5
  55. package/src/archiver/archiver_store_test_suite.ts +113 -77
  56. package/src/archiver/config.ts +6 -6
  57. package/src/archiver/data_retrieval.ts +94 -12
  58. package/src/archiver/errors.ts +5 -0
  59. package/src/archiver/kv_archiver_store/block_store.ts +66 -67
  60. package/src/archiver/kv_archiver_store/contract_class_store.ts +17 -15
  61. package/src/archiver/kv_archiver_store/contract_instance_store.ts +5 -5
  62. package/src/archiver/kv_archiver_store/kv_archiver_store.ts +48 -66
  63. package/src/archiver/kv_archiver_store/log_store.ts +167 -112
  64. package/src/archiver/kv_archiver_store/message_store.ts +22 -22
  65. package/src/archiver/kv_archiver_store/nullifier_store.ts +48 -30
  66. package/src/archiver/memory_archiver_store/memory_archiver_store.ts +100 -96
  67. package/src/factory.ts +11 -9
  68. package/src/rpc/index.ts +4 -4
  69. package/src/test/mock_archiver.ts +1 -0
  70. package/src/test/mock_l2_block_source.ts +20 -18
@@ -1,3 +1,4 @@
1
+ import { type BlobSinkClientInterface } from '@aztec/blob-sink/client';
1
2
  import { Body, InboxLeaf, L2Block } from '@aztec/circuit-types';
2
3
  import { AppendOnlyTreeSnapshot, BlockHeader, Fr, Proof } from '@aztec/circuits.js';
3
4
  import { asyncPool } from '@aztec/foundation/async-pool';
@@ -6,7 +7,7 @@ import { type EthAddress } from '@aztec/foundation/eth-address';
6
7
  import { type ViemSignature } from '@aztec/foundation/eth-signature';
7
8
  import { type Logger, createLogger } from '@aztec/foundation/log';
8
9
  import { numToUInt32BE } from '@aztec/foundation/serialize';
9
- import { type InboxAbi, RollupAbi } from '@aztec/l1-artifacts';
10
+ import { ForwarderAbi, type InboxAbi, RollupAbi } from '@aztec/l1-artifacts';
10
11
 
11
12
  import {
12
13
  type Chain,
@@ -20,6 +21,7 @@ import {
20
21
  hexToBytes,
21
22
  } from 'viem';
22
23
 
24
+ import { NoBlobBodiesFoundError } from './errors.js';
23
25
  import { type DataRetrieval } from './structs/data_retrieval.js';
24
26
  import { type L1Published, type L1PublishedData } from './structs/published.js';
25
27
 
@@ -35,6 +37,7 @@ import { type L1Published, type L1PublishedData } from './structs/published.js';
35
37
  export async function retrieveBlocksFromRollup(
36
38
  rollup: GetContractReturnType<typeof RollupAbi, PublicClient<HttpTransport, Chain>>,
37
39
  publicClient: PublicClient,
40
+ blobSinkClient: BlobSinkClientInterface,
38
41
  searchStartBlock: bigint,
39
42
  searchEndBlock: bigint,
40
43
  logger: Logger = createLogger('archiver'),
@@ -63,7 +66,13 @@ export async function retrieveBlocksFromRollup(
63
66
  `Got ${l2BlockProposedLogs.length} L2 block processed logs for L2 blocks ${l2BlockProposedLogs[0].args.blockNumber}-${lastLog.args.blockNumber} between L1 blocks ${searchStartBlock}-${searchEndBlock}`,
64
67
  );
65
68
 
66
- const newBlocks = await processL2BlockProposedLogs(rollup, publicClient, l2BlockProposedLogs, logger);
69
+ const newBlocks = await processL2BlockProposedLogs(
70
+ rollup,
71
+ publicClient,
72
+ blobSinkClient,
73
+ l2BlockProposedLogs,
74
+ logger,
75
+ );
67
76
  retrievedBlocks.push(...newBlocks);
68
77
  searchStartBlock = lastLog.blockNumber! + 1n;
69
78
  } while (searchStartBlock <= searchEndBlock);
@@ -80,6 +89,7 @@ export async function retrieveBlocksFromRollup(
80
89
  export async function processL2BlockProposedLogs(
81
90
  rollup: GetContractReturnType<typeof RollupAbi, PublicClient<HttpTransport, Chain>>,
82
91
  publicClient: PublicClient,
92
+ blobSinkClient: BlobSinkClientInterface,
83
93
  logs: GetContractEventsReturnType<typeof RollupAbi, 'L2BlockProposed'>,
84
94
  logger: Logger,
85
95
  ): Promise<L1Published<L2Block>[]> {
@@ -88,10 +98,18 @@ export async function processL2BlockProposedLogs(
88
98
  const l2BlockNumber = log.args.blockNumber!;
89
99
  const archive = log.args.archive!;
90
100
  const archiveFromChain = await rollup.read.archiveAt([l2BlockNumber]);
101
+ const blobHashes = log.args.versionedBlobHashes!.map(blobHash => Buffer.from(blobHash.slice(2), 'hex'));
91
102
 
92
103
  // The value from the event and contract will match only if the block is in the chain.
93
104
  if (archive === archiveFromChain) {
94
- const block = await getBlockFromRollupTx(publicClient, log.transactionHash!, l2BlockNumber);
105
+ const block = await getBlockFromRollupTx(
106
+ publicClient,
107
+ blobSinkClient,
108
+ log.transactionHash!,
109
+ blobHashes,
110
+ l2BlockNumber,
111
+ rollup.address,
112
+ );
95
113
 
96
114
  const l1: L1PublishedData = {
97
115
  blockNumber: log.blockNumber,
@@ -116,6 +134,57 @@ export async function getL1BlockTime(publicClient: PublicClient, blockNumber: bi
116
134
  return block.timestamp;
117
135
  }
118
136
 
137
+ /**
138
+ * Extracts the first 'propose' method calldata from a forwarder transaction's data.
139
+ * @param forwarderData - The forwarder transaction input data
140
+ * @param rollupAddress - The address of the rollup contract
141
+ * @returns The calldata for the first 'propose' method call to the rollup contract
142
+ */
143
+ function extractRollupProposeCalldata(forwarderData: Hex, rollupAddress: Hex): Hex {
144
+ // TODO(#11451): custom forwarders
145
+ const { functionName: forwarderFunctionName, args: forwarderArgs } = decodeFunctionData({
146
+ abi: ForwarderAbi,
147
+ data: forwarderData,
148
+ });
149
+
150
+ if (forwarderFunctionName !== 'forward') {
151
+ throw new Error(`Unexpected forwarder method called ${forwarderFunctionName}`);
152
+ }
153
+
154
+ if (forwarderArgs.length !== 2) {
155
+ throw new Error(`Unexpected number of arguments for forwarder`);
156
+ }
157
+
158
+ const [to, data] = forwarderArgs;
159
+
160
+ // Find all rollup calls
161
+ const rollupAddressLower = rollupAddress.toLowerCase();
162
+
163
+ for (let i = 0; i < to.length; i++) {
164
+ const addr = to[i];
165
+ if (addr.toLowerCase() !== rollupAddressLower) {
166
+ continue;
167
+ }
168
+ const callData = data[i];
169
+
170
+ try {
171
+ const { functionName: rollupFunctionName } = decodeFunctionData({
172
+ abi: RollupAbi,
173
+ data: callData,
174
+ });
175
+
176
+ if (rollupFunctionName === 'propose') {
177
+ return callData;
178
+ }
179
+ } catch (err) {
180
+ // Skip invalid function data
181
+ continue;
182
+ }
183
+ }
184
+
185
+ throw new Error(`Rollup address not found in forwarder args`);
186
+ }
187
+
119
188
  /**
120
189
  * Gets block from the calldata of an L1 transaction.
121
190
  * Assumes that the block was published from an EOA.
@@ -127,19 +196,26 @@ export async function getL1BlockTime(publicClient: PublicClient, blockNumber: bi
127
196
  */
128
197
  async function getBlockFromRollupTx(
129
198
  publicClient: PublicClient,
199
+ blobSinkClient: BlobSinkClientInterface,
130
200
  txHash: `0x${string}`,
201
+ blobHashes: Buffer[], // WORKTODO(md): buffer32?
131
202
  l2BlockNum: bigint,
203
+ rollupAddress: Hex,
132
204
  ): Promise<L2Block> {
133
- const { input: data } = await publicClient.getTransaction({ hash: txHash });
134
- const { functionName, args } = decodeFunctionData({ abi: RollupAbi, data });
205
+ const { input: forwarderData, blockHash } = await publicClient.getTransaction({ hash: txHash });
135
206
 
136
- const allowedMethods = ['propose', 'proposeAndClaim'];
207
+ const rollupData = extractRollupProposeCalldata(forwarderData, rollupAddress);
208
+ const { functionName: rollupFunctionName, args: rollupArgs } = decodeFunctionData({
209
+ abi: RollupAbi,
210
+ data: rollupData,
211
+ });
137
212
 
138
- if (!allowedMethods.includes(functionName)) {
139
- throw new Error(`Unexpected method called ${functionName}`);
213
+ if (rollupFunctionName !== 'propose') {
214
+ throw new Error(`Unexpected rollup method called ${rollupFunctionName}`);
140
215
  }
216
+
141
217
  // TODO(#9101): 'bodyHex' will be removed from below
142
- const [decodedArgs, , bodyHex, blobInputs] = args! as readonly [
218
+ const [decodedArgs, , bodyHex, blobInputs] = rollupArgs! as readonly [
143
219
  {
144
220
  header: Hex;
145
221
  archive: Hex;
@@ -156,16 +232,22 @@ async function getBlockFromRollupTx(
156
232
  ];
157
233
 
158
234
  const header = BlockHeader.fromBuffer(Buffer.from(hexToBytes(decodedArgs.header)));
235
+
236
+ const blobBodies = await blobSinkClient.getBlobSidecar(blockHash, blobHashes);
237
+ if (blobBodies.length === 0) {
238
+ throw new NoBlobBodiesFoundError(Number(l2BlockNum));
239
+ }
240
+
241
+ const blockFields = blobBodies.flatMap(b => b.toEncodedFields());
159
242
  // TODO(#9101): Retreiving the block body from calldata is a temporary soln before we have
160
243
  // either a beacon chain client or link to some blob store. Web2 is ok because we will
161
244
  // verify the block body vs the blob as below.
162
245
  const blockBody = Body.fromBuffer(Buffer.from(hexToBytes(bodyHex)));
163
246
 
164
- const blockFields = blockBody.toBlobFields();
165
247
  // TODO(#9101): The below reconstruction is currently redundant, but once we extract blobs will be the way to construct blocks.
166
248
  // The blob source will give us blockFields, and we must construct the body from them:
167
249
  // TODO(#8954): When logs are refactored into fields, we won't need to inject them here.
168
- const reconstructedBlock = Body.fromBlobFields(blockFields, blockBody.unencryptedLogs, blockBody.contractClassLogs);
250
+ const reconstructedBlock = Body.fromBlobFields(blockFields, blockBody.contractClassLogs);
169
251
 
170
252
  if (!reconstructedBlock.toBuffer().equals(blockBody.toBuffer())) {
171
253
  // TODO(#9101): Remove below check (without calldata there will be nothing to check against)
@@ -173,7 +255,7 @@ async function getBlockFromRollupTx(
173
255
  }
174
256
 
175
257
  // TODO(#9101): Once we stop publishing calldata, we will still need the blobCheck below to ensure that the block we are building does correspond to the blob fields
176
- const blobCheck = Blob.getBlobs(blockFields);
258
+ const blobCheck = await Blob.getBlobs(blockFields);
177
259
  if (Blob.getEthBlobEvaluationInputs(blobCheck) !== blobInputs) {
178
260
  // NB: We can just check the blobhash here, which is the first 32 bytes of blobInputs
179
261
  // A mismatch means that the fields published in the blob in propose() do NOT match those in the extracted block.
@@ -0,0 +1,5 @@
1
+ export class NoBlobBodiesFoundError extends Error {
2
+ constructor(l2BlockNum: number) {
3
+ super(`No blob bodies found for block ${l2BlockNum}`);
4
+ }
5
+ }
@@ -1,7 +1,8 @@
1
1
  import { Body, type InBlock, L2Block, L2BlockHash, type TxEffect, type TxHash, TxReceipt } from '@aztec/circuit-types';
2
2
  import { AppendOnlyTreeSnapshot, type AztecAddress, BlockHeader, INITIAL_L2_BLOCK_NUM } from '@aztec/circuits.js';
3
+ import { toArray } from '@aztec/foundation/iterable';
3
4
  import { createLogger } from '@aztec/foundation/log';
4
- import { type AztecKVStore, type AztecMap, type AztecSingleton, type Range } from '@aztec/kv-store';
5
+ import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncSingleton, Range } from '@aztec/kv-store';
5
6
 
6
7
  import { type L1Published, type L1PublishedData } from '../structs/published.js';
7
8
 
@@ -18,29 +19,29 @@ type BlockStorage = {
18
19
  */
19
20
  export class BlockStore {
20
21
  /** Map block number to block data */
21
- #blocks: AztecMap<number, BlockStorage>;
22
+ #blocks: AztecAsyncMap<number, BlockStorage>;
22
23
 
23
24
  /** Map block hash to block body */
24
- #blockBodies: AztecMap<string, Buffer>;
25
+ #blockBodies: AztecAsyncMap<string, Buffer>;
25
26
 
26
27
  /** Stores L1 block number in which the last processed L2 block was included */
27
- #lastSynchedL1Block: AztecSingleton<bigint>;
28
+ #lastSynchedL1Block: AztecAsyncSingleton<bigint>;
28
29
 
29
30
  /** Stores l2 block number of the last proven block */
30
- #lastProvenL2Block: AztecSingleton<number>;
31
+ #lastProvenL2Block: AztecAsyncSingleton<number>;
31
32
 
32
33
  /** Stores l2 epoch number of the last proven epoch */
33
- #lastProvenL2Epoch: AztecSingleton<number>;
34
+ #lastProvenL2Epoch: AztecAsyncSingleton<number>;
34
35
 
35
36
  /** Index mapping transaction hash (as a string) to its location in a block */
36
- #txIndex: AztecMap<string, BlockIndexValue>;
37
+ #txIndex: AztecAsyncMap<string, BlockIndexValue>;
37
38
 
38
39
  /** Index mapping a contract's address (as a string) to its location in a block */
39
- #contractIndex: AztecMap<string, BlockIndexValue>;
40
+ #contractIndex: AztecAsyncMap<string, BlockIndexValue>;
40
41
 
41
42
  #log = createLogger('archiver:block_store');
42
43
 
43
- constructor(private db: AztecKVStore) {
44
+ constructor(private db: AztecAsyncKVStore) {
44
45
  this.#blocks = db.openMap('archiver_blocks');
45
46
  this.#blockBodies = db.openMap('archiver_block_bodies');
46
47
  this.#txIndex = db.openMap('archiver_tx_index');
@@ -55,28 +56,28 @@ export class BlockStore {
55
56
  * @param blocks - The L2 blocks to be added to the store.
56
57
  * @returns True if the operation is successful.
57
58
  */
58
- addBlocks(blocks: L1Published<L2Block>[]): Promise<boolean> {
59
+ async addBlocks(blocks: L1Published<L2Block>[]): Promise<boolean> {
59
60
  if (blocks.length === 0) {
60
- return Promise.resolve(true);
61
+ return true;
61
62
  }
62
63
 
63
- return this.db.transaction(() => {
64
+ return await this.db.transactionAsync(async () => {
64
65
  for (const block of blocks) {
65
- void this.#blocks.set(block.data.number, {
66
+ await this.#blocks.set(block.data.number, {
66
67
  header: block.data.header.toBuffer(),
67
68
  archive: block.data.archive.toBuffer(),
68
69
  l1: block.l1,
69
70
  });
70
71
 
71
- block.data.body.txEffects.forEach((tx, i) => {
72
- void this.#txIndex.set(tx.txHash.toString(), [block.data.number, i]);
73
- });
72
+ for (let i = 0; i < block.data.body.txEffects.length; i++) {
73
+ const txEffect = block.data.body.txEffects[i];
74
+ await this.#txIndex.set(txEffect.txHash.toString(), [block.data.number, i]);
75
+ }
74
76
 
75
- void this.#blockBodies.set(block.data.hash().toString(), block.data.body.toBuffer());
77
+ await this.#blockBodies.set((await block.data.hash()).toString(), block.data.body.toBuffer());
76
78
  }
77
79
 
78
- void this.#lastSynchedL1Block.set(blocks[blocks.length - 1].l1.blockNumber);
79
-
80
+ await this.#lastSynchedL1Block.set(blocks[blocks.length - 1].l1.blockNumber);
80
81
  return true;
81
82
  });
82
83
  }
@@ -88,26 +89,24 @@ export class BlockStore {
88
89
  * @param blocksToUnwind - The number of blocks we are to unwind
89
90
  * @returns True if the operation is successful
90
91
  */
91
- unwindBlocks(from: number, blocksToUnwind: number) {
92
- return this.db.transaction(() => {
93
- const last = this.getSynchedL2BlockNumber();
94
- if (from != last) {
92
+ async unwindBlocks(from: number, blocksToUnwind: number) {
93
+ return await this.db.transactionAsync(async () => {
94
+ const last = await this.getSynchedL2BlockNumber();
95
+ if (from !== last) {
95
96
  throw new Error(`Can only unwind blocks from the tip (requested ${from} but current tip is ${last})`);
96
97
  }
97
98
 
98
99
  for (let i = 0; i < blocksToUnwind; i++) {
99
100
  const blockNumber = from - i;
100
- const block = this.getBlock(blockNumber);
101
+ const block = await this.getBlock(blockNumber);
101
102
 
102
103
  if (block === undefined) {
103
104
  throw new Error(`Cannot remove block ${blockNumber} from the store, we don't have it`);
104
105
  }
105
- void this.#blocks.delete(block.data.number);
106
- block.data.body.txEffects.forEach(tx => {
107
- void this.#txIndex.delete(tx.txHash.toString());
108
- });
109
- const blockHash = block.data.hash().toString();
110
- void this.#blockBodies.delete(blockHash);
106
+ await this.#blocks.delete(block.data.number);
107
+ await Promise.all(block.data.body.txEffects.map(tx => this.#txIndex.delete(tx.txHash.toString())));
108
+ const blockHash = (await block.data.hash()).toString();
109
+ await this.#blockBodies.delete(blockHash);
111
110
  this.#log.debug(`Unwound block ${blockNumber} ${blockHash}`);
112
111
  }
113
112
 
@@ -121,9 +120,10 @@ export class BlockStore {
121
120
  * @param limit - The number of blocks to return.
122
121
  * @returns The requested L2 blocks
123
122
  */
124
- *getBlocks(start: number, limit: number): IterableIterator<L1Published<L2Block>> {
125
- for (const blockStorage of this.#blocks.values(this.#computeBlockRange(start, limit))) {
126
- yield this.getBlockFromBlockStorage(blockStorage);
123
+ async *getBlocks(start: number, limit: number): AsyncIterableIterator<L1Published<L2Block>> {
124
+ for await (const blockStorage of this.#blocks.valuesAsync(this.#computeBlockRange(start, limit))) {
125
+ const block = await this.getBlockFromBlockStorage(blockStorage);
126
+ yield block;
127
127
  }
128
128
  }
129
129
 
@@ -132,10 +132,10 @@ export class BlockStore {
132
132
  * @param blockNumber - The number of the block to return.
133
133
  * @returns The requested L2 block.
134
134
  */
135
- getBlock(blockNumber: number): L1Published<L2Block> | undefined {
136
- const blockStorage = this.#blocks.get(blockNumber);
135
+ async getBlock(blockNumber: number): Promise<L1Published<L2Block> | undefined> {
136
+ const blockStorage = await this.#blocks.getAsync(blockNumber);
137
137
  if (!blockStorage || !blockStorage.header) {
138
- return undefined;
138
+ return Promise.resolve(undefined);
139
139
  }
140
140
 
141
141
  return this.getBlockFromBlockStorage(blockStorage);
@@ -147,17 +147,17 @@ export class BlockStore {
147
147
  * @param limit - The number of blocks to return.
148
148
  * @returns The requested L2 block headers
149
149
  */
150
- *getBlockHeaders(start: number, limit: number): IterableIterator<BlockHeader> {
151
- for (const blockStorage of this.#blocks.values(this.#computeBlockRange(start, limit))) {
150
+ async *getBlockHeaders(start: number, limit: number): AsyncIterableIterator<BlockHeader> {
151
+ for await (const blockStorage of this.#blocks.valuesAsync(this.#computeBlockRange(start, limit))) {
152
152
  yield BlockHeader.fromBuffer(blockStorage.header);
153
153
  }
154
154
  }
155
155
 
156
- private getBlockFromBlockStorage(blockStorage: BlockStorage) {
156
+ private async getBlockFromBlockStorage(blockStorage: BlockStorage) {
157
157
  const header = BlockHeader.fromBuffer(blockStorage.header);
158
158
  const archive = AppendOnlyTreeSnapshot.fromBuffer(blockStorage.archive);
159
- const blockHash = header.hash().toString();
160
- const blockBodyBuffer = this.#blockBodies.get(blockHash);
159
+ const blockHash = (await header.hash()).toString();
160
+ const blockBodyBuffer = await this.#blockBodies.getAsync(blockHash);
161
161
  if (blockBodyBuffer === undefined) {
162
162
  throw new Error(
163
163
  `Could not retrieve body for block ${header.globalVariables.blockNumber.toNumber()} ${blockHash}`,
@@ -174,13 +174,13 @@ export class BlockStore {
174
174
  * @param txHash - The txHash of the tx corresponding to the tx effect.
175
175
  * @returns The requested tx effect (or undefined if not found).
176
176
  */
177
- getTxEffect(txHash: TxHash): InBlock<TxEffect> | undefined {
178
- const [blockNumber, txIndex] = this.getTxLocation(txHash) ?? [];
177
+ async getTxEffect(txHash: TxHash): Promise<InBlock<TxEffect> | undefined> {
178
+ const [blockNumber, txIndex] = (await this.getTxLocation(txHash)) ?? [];
179
179
  if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') {
180
180
  return undefined;
181
181
  }
182
182
 
183
- const block = this.getBlock(blockNumber);
183
+ const block = await this.getBlock(blockNumber);
184
184
  if (!block) {
185
185
  return undefined;
186
186
  }
@@ -188,7 +188,7 @@ export class BlockStore {
188
188
  return {
189
189
  data: block.data.body.txEffects[txIndex],
190
190
  l2BlockNumber: block.data.number,
191
- l2BlockHash: block.data.hash().toString(),
191
+ l2BlockHash: (await block.data.hash()).toString(),
192
192
  };
193
193
  }
194
194
 
@@ -197,13 +197,13 @@ export class BlockStore {
197
197
  * @param txHash - The hash of a tx we try to get the receipt for.
198
198
  * @returns The requested tx receipt (or undefined if not found).
199
199
  */
200
- getSettledTxReceipt(txHash: TxHash): TxReceipt | undefined {
201
- const [blockNumber, txIndex] = this.getTxLocation(txHash) ?? [];
200
+ async getSettledTxReceipt(txHash: TxHash): Promise<TxReceipt | undefined> {
201
+ const [blockNumber, txIndex] = (await this.getTxLocation(txHash)) ?? [];
202
202
  if (typeof blockNumber !== 'number' || typeof txIndex !== 'number') {
203
203
  return undefined;
204
204
  }
205
205
 
206
- const block = this.getBlock(blockNumber)!;
206
+ const block = (await this.getBlock(blockNumber))!;
207
207
  const tx = block.data.body.txEffects[txIndex];
208
208
 
209
209
  return new TxReceipt(
@@ -211,7 +211,7 @@ export class BlockStore {
211
211
  TxReceipt.statusFromRevertCode(tx.revertCode),
212
212
  '',
213
213
  tx.transactionFee.toBigInt(),
214
- L2BlockHash.fromField(block.data.hash()),
214
+ L2BlockHash.fromField(await block.data.hash()),
215
215
  block.data.number,
216
216
  );
217
217
  }
@@ -221,8 +221,8 @@ export class BlockStore {
221
221
  * @param txHash - The txHash of the tx.
222
222
  * @returns The block number and index of the tx.
223
223
  */
224
- getTxLocation(txHash: TxHash): [blockNumber: number, txIndex: number] | undefined {
225
- return this.#txIndex.get(txHash.toString());
224
+ getTxLocation(txHash: TxHash): Promise<[blockNumber: number, txIndex: number] | undefined> {
225
+ return this.#txIndex.getAsync(txHash.toString());
226
226
  }
227
227
 
228
228
  /**
@@ -230,16 +230,16 @@ export class BlockStore {
230
230
  * @param contractAddress - The address of the contract to look up.
231
231
  * @returns The block number and index of the contract.
232
232
  */
233
- getContractLocation(contractAddress: AztecAddress): [blockNumber: number, index: number] | undefined {
234
- return this.#contractIndex.get(contractAddress.toString());
233
+ getContractLocation(contractAddress: AztecAddress): Promise<[blockNumber: number, index: number] | undefined> {
234
+ return this.#contractIndex.getAsync(contractAddress.toString());
235
235
  }
236
236
 
237
237
  /**
238
238
  * Gets the number of the latest L2 block processed.
239
239
  * @returns The number of the latest L2 block processed.
240
240
  */
241
- getSynchedL2BlockNumber(): number {
242
- const [lastBlockNumber] = this.#blocks.keys({ reverse: true, limit: 1 });
241
+ async getSynchedL2BlockNumber(): Promise<number> {
242
+ const [lastBlockNumber] = await toArray(this.#blocks.keysAsync({ reverse: true, limit: 1 }));
243
243
  return typeof lastBlockNumber === 'number' ? lastBlockNumber : INITIAL_L2_BLOCK_NUM - 1;
244
244
  }
245
245
 
@@ -247,31 +247,31 @@ export class BlockStore {
247
247
  * Gets the most recent L1 block processed.
248
248
  * @returns The L1 block that published the latest L2 block
249
249
  */
250
- getSynchedL1BlockNumber(): bigint | undefined {
251
- return this.#lastSynchedL1Block.get();
250
+ getSynchedL1BlockNumber(): Promise<bigint | undefined> {
251
+ return this.#lastSynchedL1Block.getAsync();
252
252
  }
253
253
 
254
254
  setSynchedL1BlockNumber(l1BlockNumber: bigint) {
255
- void this.#lastSynchedL1Block.set(l1BlockNumber);
255
+ return this.#lastSynchedL1Block.set(l1BlockNumber);
256
256
  }
257
257
 
258
- getProvenL2BlockNumber(): number {
259
- return this.#lastProvenL2Block.get() ?? 0;
258
+ async getProvenL2BlockNumber(): Promise<number> {
259
+ return (await this.#lastProvenL2Block.getAsync()) ?? 0;
260
260
  }
261
261
 
262
262
  setProvenL2BlockNumber(blockNumber: number) {
263
- void this.#lastProvenL2Block.set(blockNumber);
263
+ return this.#lastProvenL2Block.set(blockNumber);
264
264
  }
265
265
 
266
- getProvenL2EpochNumber(): number | undefined {
267
- return this.#lastProvenL2Epoch.get();
266
+ getProvenL2EpochNumber(): Promise<number | undefined> {
267
+ return this.#lastProvenL2Epoch.getAsync();
268
268
  }
269
269
 
270
270
  setProvenL2EpochNumber(epochNumber: number) {
271
- void this.#lastProvenL2Epoch.set(epochNumber);
271
+ return this.#lastProvenL2Epoch.set(epochNumber);
272
272
  }
273
273
 
274
- #computeBlockRange(start: number, limit: number): Required<Pick<Range<number>, 'start' | 'end'>> {
274
+ #computeBlockRange(start: number, limit: number): Required<Pick<Range<number>, 'start' | 'limit'>> {
275
275
  if (limit < 1) {
276
276
  throw new Error(`Invalid limit: ${limit}`);
277
277
  }
@@ -280,7 +280,6 @@ export class BlockStore {
280
280
  throw new Error(`Invalid start: ${start}`);
281
281
  }
282
282
 
283
- const end = start + limit;
284
- return { start, end };
283
+ return { start, limit };
285
284
  }
286
285
  }
@@ -7,17 +7,18 @@ import {
7
7
  type UnconstrainedFunctionWithMembershipProof,
8
8
  Vector,
9
9
  } from '@aztec/circuits.js';
10
+ import { toArray } from '@aztec/foundation/iterable';
10
11
  import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize';
11
- import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
12
+ import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
12
13
 
13
14
  /**
14
15
  * LMDB implementation of the ArchiverDataStore interface.
15
16
  */
16
17
  export class ContractClassStore {
17
- #contractClasses: AztecMap<string, Buffer>;
18
- #bytecodeCommitments: AztecMap<string, Buffer>;
18
+ #contractClasses: AztecAsyncMap<string, Buffer>;
19
+ #bytecodeCommitments: AztecAsyncMap<string, Buffer>;
19
20
 
20
- constructor(private db: AztecKVStore) {
21
+ constructor(private db: AztecAsyncKVStore) {
21
22
  this.#contractClasses = db.openMap('archiver_contract_classes');
22
23
  this.#bytecodeCommitments = db.openMap('archiver_bytecode_commitments');
23
24
  }
@@ -35,25 +36,25 @@ export class ContractClassStore {
35
36
  }
36
37
 
37
38
  async deleteContractClasses(contractClass: ContractClassPublic, blockNumber: number): Promise<void> {
38
- const restoredContractClass = this.#contractClasses.get(contractClass.id.toString());
39
+ const restoredContractClass = await this.#contractClasses.getAsync(contractClass.id.toString());
39
40
  if (restoredContractClass && deserializeContractClassPublic(restoredContractClass).l2BlockNumber >= blockNumber) {
40
41
  await this.#contractClasses.delete(contractClass.id.toString());
41
42
  await this.#bytecodeCommitments.delete(contractClass.id.toString());
42
43
  }
43
44
  }
44
45
 
45
- getContractClass(id: Fr): ContractClassPublic | undefined {
46
- const contractClass = this.#contractClasses.get(id.toString());
46
+ async getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
47
+ const contractClass = await this.#contractClasses.getAsync(id.toString());
47
48
  return contractClass && { ...deserializeContractClassPublic(contractClass), id };
48
49
  }
49
50
 
50
- getBytecodeCommitment(id: Fr): Fr | undefined {
51
- const value = this.#bytecodeCommitments.get(id.toString());
51
+ async getBytecodeCommitment(id: Fr): Promise<Fr | undefined> {
52
+ const value = await this.#bytecodeCommitments.getAsync(id.toString());
52
53
  return value === undefined ? undefined : Fr.fromBuffer(value);
53
54
  }
54
55
 
55
- getContractClassIds(): Fr[] {
56
- return Array.from(this.#contractClasses.keys()).map(key => Fr.fromHexString(key));
56
+ async getContractClassIds(): Promise<Fr[]> {
57
+ return (await toArray(this.#contractClasses.keysAsync())).map(key => Fr.fromHexString(key));
57
58
  }
58
59
 
59
60
  async addFunctions(
@@ -61,8 +62,8 @@ export class ContractClassStore {
61
62
  newPrivateFunctions: ExecutablePrivateFunctionWithMembershipProof[],
62
63
  newUnconstrainedFunctions: UnconstrainedFunctionWithMembershipProof[],
63
64
  ): Promise<boolean> {
64
- await this.db.transaction(() => {
65
- const existingClassBuffer = this.#contractClasses.get(contractClassId.toString());
65
+ await this.db.transactionAsync(async () => {
66
+ const existingClassBuffer = await this.#contractClasses.getAsync(contractClassId.toString());
66
67
  if (!existingClassBuffer) {
67
68
  throw new Error(`Unknown contract class ${contractClassId} when adding private functions to store`);
68
69
  }
@@ -83,9 +84,10 @@ export class ContractClassStore {
83
84
  ),
84
85
  ],
85
86
  };
86
- void this.#contractClasses.set(contractClassId.toString(), serializeContractClassPublic(updatedClass));
87
+ await this.#contractClasses.set(contractClassId.toString(), serializeContractClassPublic(updatedClass));
87
88
  });
88
- return Promise.resolve(true);
89
+
90
+ return true;
89
91
  }
90
92
  }
91
93
 
@@ -1,13 +1,13 @@
1
1
  import { type AztecAddress, type ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/circuits.js';
2
- import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
2
+ import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
3
3
 
4
4
  /**
5
5
  * LMDB implementation of the ArchiverDataStore interface.
6
6
  */
7
7
  export class ContractInstanceStore {
8
- #contractInstances: AztecMap<string, Buffer>;
8
+ #contractInstances: AztecAsyncMap<string, Buffer>;
9
9
 
10
- constructor(db: AztecKVStore) {
10
+ constructor(db: AztecAsyncKVStore) {
11
11
  this.#contractInstances = db.openMap('archiver_contract_instances');
12
12
  }
13
13
 
@@ -22,8 +22,8 @@ export class ContractInstanceStore {
22
22
  return this.#contractInstances.delete(contractInstance.address.toString());
23
23
  }
24
24
 
25
- getContractInstance(address: AztecAddress): ContractInstanceWithAddress | undefined {
26
- const contractInstance = this.#contractInstances.get(address.toString());
25
+ async getContractInstance(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
26
+ const contractInstance = await this.#contractInstances.getAsync(address.toString());
27
27
  return contractInstance && SerializableContractInstance.fromBuffer(contractInstance).withAddress(address);
28
28
  }
29
29
  }