@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.
- package/dest/archiver/archiver.d.ts +6 -6
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +35 -20
- package/dest/archiver/archiver_store.d.ts +6 -6
- package/dest/archiver/archiver_store.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.d.ts +1 -1
- package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.js +103 -65
- package/dest/archiver/config.d.ts +2 -2
- package/dest/archiver/config.d.ts.map +1 -1
- package/dest/archiver/config.js +5 -5
- package/dest/archiver/data_retrieval.d.ts +3 -2
- package/dest/archiver/data_retrieval.d.ts.map +1 -1
- package/dest/archiver/data_retrieval.js +69 -16
- package/dest/archiver/errors.d.ts +4 -0
- package/dest/archiver/errors.d.ts.map +1 -0
- package/dest/archiver/errors.js +6 -0
- package/dest/archiver/kv_archiver_store/block_store.d.ts +16 -16
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +53 -53
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +5 -5
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_class_store.js +13 -12
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +3 -3
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.js +3 -3
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +7 -11
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +41 -63
- package/dest/archiver/kv_archiver_store/log_store.d.ts +8 -8
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/log_store.js +122 -89
- package/dest/archiver/kv_archiver_store/message_store.d.ts +6 -6
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/message_store.js +16 -16
- package/dest/archiver/kv_archiver_store/nullifier_store.d.ts +2 -2
- package/dest/archiver/kv_archiver_store/nullifier_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/nullifier_store.js +31 -22
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +9 -9
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +1 -1
- package/dest/archiver/memory_archiver_store/memory_archiver_store.js +72 -71
- package/dest/factory.js +8 -8
- package/dest/rpc/index.d.ts +1 -1
- package/dest/rpc/index.d.ts.map +1 -1
- package/dest/rpc/index.js +5 -5
- package/dest/test/mock_archiver.d.ts +1 -1
- package/dest/test/mock_archiver.d.ts.map +1 -1
- package/dest/test/mock_archiver.js +2 -1
- package/dest/test/mock_l2_block_source.d.ts +3 -3
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +14 -11
- package/package.json +13 -13
- package/src/archiver/archiver.ts +46 -25
- package/src/archiver/archiver_store.ts +6 -5
- package/src/archiver/archiver_store_test_suite.ts +113 -77
- package/src/archiver/config.ts +6 -6
- package/src/archiver/data_retrieval.ts +94 -12
- package/src/archiver/errors.ts +5 -0
- package/src/archiver/kv_archiver_store/block_store.ts +66 -67
- package/src/archiver/kv_archiver_store/contract_class_store.ts +17 -15
- package/src/archiver/kv_archiver_store/contract_instance_store.ts +5 -5
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +48 -66
- package/src/archiver/kv_archiver_store/log_store.ts +167 -112
- package/src/archiver/kv_archiver_store/message_store.ts +22 -22
- package/src/archiver/kv_archiver_store/nullifier_store.ts +48 -30
- package/src/archiver/memory_archiver_store/memory_archiver_store.ts +100 -96
- package/src/factory.ts +11 -9
- package/src/rpc/index.ts +4 -4
- package/src/test/mock_archiver.ts +1 -0
- 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(
|
|
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(
|
|
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:
|
|
134
|
-
const { functionName, args } = decodeFunctionData({ abi: RollupAbi, data });
|
|
205
|
+
const { input: forwarderData, blockHash } = await publicClient.getTransaction({ hash: txHash });
|
|
135
206
|
|
|
136
|
-
const
|
|
207
|
+
const rollupData = extractRollupProposeCalldata(forwarderData, rollupAddress);
|
|
208
|
+
const { functionName: rollupFunctionName, args: rollupArgs } = decodeFunctionData({
|
|
209
|
+
abi: RollupAbi,
|
|
210
|
+
data: rollupData,
|
|
211
|
+
});
|
|
137
212
|
|
|
138
|
-
if (
|
|
139
|
-
throw new Error(`Unexpected method called ${
|
|
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] =
|
|
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.
|
|
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.
|
|
@@ -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 {
|
|
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:
|
|
22
|
+
#blocks: AztecAsyncMap<number, BlockStorage>;
|
|
22
23
|
|
|
23
24
|
/** Map block hash to block body */
|
|
24
|
-
#blockBodies:
|
|
25
|
+
#blockBodies: AztecAsyncMap<string, Buffer>;
|
|
25
26
|
|
|
26
27
|
/** Stores L1 block number in which the last processed L2 block was included */
|
|
27
|
-
#lastSynchedL1Block:
|
|
28
|
+
#lastSynchedL1Block: AztecAsyncSingleton<bigint>;
|
|
28
29
|
|
|
29
30
|
/** Stores l2 block number of the last proven block */
|
|
30
|
-
#lastProvenL2Block:
|
|
31
|
+
#lastProvenL2Block: AztecAsyncSingleton<number>;
|
|
31
32
|
|
|
32
33
|
/** Stores l2 epoch number of the last proven epoch */
|
|
33
|
-
#lastProvenL2Epoch:
|
|
34
|
+
#lastProvenL2Epoch: AztecAsyncSingleton<number>;
|
|
34
35
|
|
|
35
36
|
/** Index mapping transaction hash (as a string) to its location in a block */
|
|
36
|
-
#txIndex:
|
|
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:
|
|
40
|
+
#contractIndex: AztecAsyncMap<string, BlockIndexValue>;
|
|
40
41
|
|
|
41
42
|
#log = createLogger('archiver:block_store');
|
|
42
43
|
|
|
43
|
-
constructor(private db:
|
|
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
|
|
61
|
+
return true;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
return this.db.
|
|
64
|
+
return await this.db.transactionAsync(async () => {
|
|
64
65
|
for (const block of blocks) {
|
|
65
|
-
|
|
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.
|
|
72
|
-
|
|
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
|
-
|
|
77
|
+
await this.#blockBodies.set((await block.data.hash()).toString(), block.data.body.toBuffer());
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
|
|
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.
|
|
93
|
-
const last = this.getSynchedL2BlockNumber();
|
|
94
|
-
if (from
|
|
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
|
-
|
|
106
|
-
block.data.body.txEffects.
|
|
107
|
-
|
|
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):
|
|
125
|
-
for (const blockStorage of this.#blocks.
|
|
126
|
-
|
|
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.
|
|
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):
|
|
151
|
-
for (const blockStorage of this.#blocks.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
250
|
+
getSynchedL1BlockNumber(): Promise<bigint | undefined> {
|
|
251
|
+
return this.#lastSynchedL1Block.getAsync();
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
setSynchedL1BlockNumber(l1BlockNumber: bigint) {
|
|
255
|
-
|
|
255
|
+
return this.#lastSynchedL1Block.set(l1BlockNumber);
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
getProvenL2BlockNumber(): number {
|
|
259
|
-
return this.#lastProvenL2Block.
|
|
258
|
+
async getProvenL2BlockNumber(): Promise<number> {
|
|
259
|
+
return (await this.#lastProvenL2Block.getAsync()) ?? 0;
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
setProvenL2BlockNumber(blockNumber: number) {
|
|
263
|
-
|
|
263
|
+
return this.#lastProvenL2Block.set(blockNumber);
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
getProvenL2EpochNumber(): number | undefined {
|
|
267
|
-
return this.#lastProvenL2Epoch.
|
|
266
|
+
getProvenL2EpochNumber(): Promise<number | undefined> {
|
|
267
|
+
return this.#lastProvenL2Epoch.getAsync();
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
setProvenL2EpochNumber(epochNumber: number) {
|
|
271
|
-
|
|
271
|
+
return this.#lastProvenL2Epoch.set(epochNumber);
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
-
#computeBlockRange(start: number, limit: number): Required<Pick<Range<number>, 'start' | '
|
|
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
|
-
|
|
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 {
|
|
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:
|
|
18
|
-
#bytecodeCommitments:
|
|
18
|
+
#contractClasses: AztecAsyncMap<string, Buffer>;
|
|
19
|
+
#bytecodeCommitments: AztecAsyncMap<string, Buffer>;
|
|
19
20
|
|
|
20
|
-
constructor(private db:
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
65
|
-
const existingClassBuffer = this.#contractClasses.
|
|
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
|
-
|
|
87
|
+
await this.#contractClasses.set(contractClassId.toString(), serializeContractClassPublic(updatedClass));
|
|
87
88
|
});
|
|
88
|
-
|
|
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 {
|
|
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:
|
|
8
|
+
#contractInstances: AztecAsyncMap<string, Buffer>;
|
|
9
9
|
|
|
10
|
-
constructor(db:
|
|
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.
|
|
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
|
}
|