@aztec/archiver 0.0.0-test.1 → 0.0.1-commit.5476d83
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/README.md +27 -6
- package/dest/archiver/archiver.d.ts +147 -57
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +841 -333
- package/dest/archiver/archiver_store.d.ts +85 -50
- 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 +708 -213
- package/dest/archiver/config.d.ts +5 -21
- package/dest/archiver/config.d.ts.map +1 -1
- package/dest/archiver/config.js +21 -12
- package/dest/archiver/data_retrieval.d.ts +32 -27
- package/dest/archiver/data_retrieval.d.ts.map +1 -1
- package/dest/archiver/data_retrieval.js +197 -94
- package/dest/archiver/errors.d.ts +9 -1
- package/dest/archiver/errors.d.ts.map +1 -1
- package/dest/archiver/errors.js +12 -0
- package/dest/archiver/index.d.ts +3 -4
- package/dest/archiver/index.d.ts.map +1 -1
- package/dest/archiver/index.js +1 -2
- package/dest/archiver/instrumentation.d.ts +12 -6
- package/dest/archiver/instrumentation.d.ts.map +1 -1
- package/dest/archiver/instrumentation.js +58 -17
- package/dest/archiver/kv_archiver_store/block_store.d.ts +48 -11
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +216 -63
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +3 -3
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_class_store.js +12 -18
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +11 -8
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.js +30 -16
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +50 -35
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +88 -46
- package/dest/archiver/kv_archiver_store/log_store.d.ts +2 -2
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/log_store.js +18 -46
- package/dest/archiver/kv_archiver_store/message_store.d.ts +23 -17
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/message_store.js +150 -48
- package/dest/archiver/structs/data_retrieval.d.ts +1 -1
- package/dest/archiver/structs/inbox_message.d.ts +15 -0
- package/dest/archiver/structs/inbox_message.d.ts.map +1 -0
- package/dest/archiver/structs/inbox_message.js +38 -0
- package/dest/archiver/structs/published.d.ts +3 -11
- package/dest/archiver/structs/published.d.ts.map +1 -1
- package/dest/archiver/structs/published.js +1 -1
- package/dest/archiver/validation.d.ts +17 -0
- package/dest/archiver/validation.d.ts.map +1 -0
- package/dest/archiver/validation.js +98 -0
- package/dest/factory.d.ts +8 -13
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +18 -49
- package/dest/index.d.ts +2 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -1
- package/dest/rpc/index.d.ts +2 -3
- package/dest/rpc/index.d.ts.map +1 -1
- package/dest/rpc/index.js +1 -4
- package/dest/test/index.d.ts +1 -1
- package/dest/test/mock_archiver.d.ts +2 -2
- package/dest/test/mock_archiver.d.ts.map +1 -1
- package/dest/test/mock_l1_to_l2_message_source.d.ts +5 -3
- package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
- package/dest/test/mock_l1_to_l2_message_source.js +14 -1
- package/dest/test/mock_l2_block_source.d.ts +38 -10
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +119 -8
- package/dest/test/mock_structs.d.ts +9 -0
- package/dest/test/mock_structs.d.ts.map +1 -0
- package/dest/test/mock_structs.js +37 -0
- package/package.json +28 -30
- package/src/archiver/archiver.ts +1087 -410
- package/src/archiver/archiver_store.ts +97 -55
- package/src/archiver/archiver_store_test_suite.ts +664 -210
- package/src/archiver/config.ts +28 -41
- package/src/archiver/data_retrieval.ts +279 -125
- package/src/archiver/errors.ts +21 -0
- package/src/archiver/index.ts +2 -3
- package/src/archiver/instrumentation.ts +77 -22
- package/src/archiver/kv_archiver_store/block_store.ts +270 -72
- package/src/archiver/kv_archiver_store/contract_class_store.ts +13 -23
- package/src/archiver/kv_archiver_store/contract_instance_store.ts +35 -27
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +127 -63
- package/src/archiver/kv_archiver_store/log_store.ts +24 -62
- package/src/archiver/kv_archiver_store/message_store.ts +209 -53
- package/src/archiver/structs/inbox_message.ts +41 -0
- package/src/archiver/structs/published.ts +2 -11
- package/src/archiver/validation.ts +124 -0
- package/src/factory.ts +24 -66
- package/src/index.ts +1 -1
- package/src/rpc/index.ts +1 -5
- package/src/test/mock_archiver.ts +1 -1
- package/src/test/mock_l1_to_l2_message_source.ts +14 -3
- package/src/test/mock_l2_block_source.ts +158 -13
- package/src/test/mock_structs.ts +49 -0
- package/dest/archiver/kv_archiver_store/nullifier_store.d.ts +0 -12
- package/dest/archiver/kv_archiver_store/nullifier_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/nullifier_store.js +0 -73
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts +0 -23
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.d.ts.map +0 -1
- package/dest/archiver/memory_archiver_store/l1_to_l2_message_store.js +0 -49
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts +0 -175
- package/dest/archiver/memory_archiver_store/memory_archiver_store.d.ts.map +0 -1
- package/dest/archiver/memory_archiver_store/memory_archiver_store.js +0 -636
- package/src/archiver/kv_archiver_store/nullifier_store.ts +0 -97
- package/src/archiver/memory_archiver_store/l1_to_l2_message_store.ts +0 -61
- package/src/archiver/memory_archiver_store/memory_archiver_store.ts +0 -801
|
@@ -1,22 +1,44 @@
|
|
|
1
1
|
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
2
3
|
import { toArray } from '@aztec/foundation/iterable';
|
|
3
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
5
|
+
import { BufferReader } from '@aztec/foundation/serialize';
|
|
6
|
+
import { bufferToHex } from '@aztec/foundation/string';
|
|
4
7
|
import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncSingleton, Range } from '@aztec/kv-store';
|
|
5
8
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
6
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
Body,
|
|
11
|
+
CommitteeAttestation,
|
|
12
|
+
L2Block,
|
|
13
|
+
L2BlockHash,
|
|
14
|
+
PublishedL2Block,
|
|
15
|
+
type ValidateBlockResult,
|
|
16
|
+
} from '@aztec/stdlib/block';
|
|
17
|
+
import { L2BlockHeader, deserializeValidateBlockResult, serializeValidateBlockResult } from '@aztec/stdlib/block';
|
|
7
18
|
import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
|
|
8
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
BlockHeader,
|
|
21
|
+
type IndexedTxEffect,
|
|
22
|
+
TxEffect,
|
|
23
|
+
TxHash,
|
|
24
|
+
TxReceipt,
|
|
25
|
+
deserializeIndexedTxEffect,
|
|
26
|
+
serializeIndexedTxEffect,
|
|
27
|
+
} from '@aztec/stdlib/tx';
|
|
9
28
|
|
|
10
|
-
import
|
|
29
|
+
import { BlockNumberNotSequentialError, InitialBlockNumberNotSequentialError } from '../errors.js';
|
|
30
|
+
import type { L1PublishedData } from '../structs/published.js';
|
|
11
31
|
|
|
12
|
-
export { type TxEffect, type TxHash
|
|
32
|
+
export { TxReceipt, type TxEffect, type TxHash } from '@aztec/stdlib/tx';
|
|
13
33
|
|
|
14
34
|
type BlockIndexValue = [blockNumber: number, index: number];
|
|
15
35
|
|
|
16
36
|
type BlockStorage = {
|
|
17
37
|
header: Buffer;
|
|
38
|
+
blockHash: Buffer;
|
|
18
39
|
archive: Buffer;
|
|
19
40
|
l1: L1PublishedData;
|
|
41
|
+
attestations: Buffer[];
|
|
20
42
|
};
|
|
21
43
|
|
|
22
44
|
/**
|
|
@@ -26,8 +48,11 @@ export class BlockStore {
|
|
|
26
48
|
/** Map block number to block data */
|
|
27
49
|
#blocks: AztecAsyncMap<number, BlockStorage>;
|
|
28
50
|
|
|
29
|
-
/** Map block hash to
|
|
30
|
-
#
|
|
51
|
+
/** Map block hash to list of tx hashes */
|
|
52
|
+
#blockTxs: AztecAsyncMap<string, Buffer>;
|
|
53
|
+
|
|
54
|
+
/** Tx hash to serialized IndexedTxEffect */
|
|
55
|
+
#txEffects: AztecAsyncMap<string, Buffer>;
|
|
31
56
|
|
|
32
57
|
/** Stores L1 block number in which the last processed L2 block was included */
|
|
33
58
|
#lastSynchedL1Block: AztecAsyncSingleton<bigint>;
|
|
@@ -35,25 +60,30 @@ export class BlockStore {
|
|
|
35
60
|
/** Stores l2 block number of the last proven block */
|
|
36
61
|
#lastProvenL2Block: AztecAsyncSingleton<number>;
|
|
37
62
|
|
|
38
|
-
/** Stores
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
/** Index mapping transaction hash (as a string) to its location in a block */
|
|
42
|
-
#txIndex: AztecAsyncMap<string, BlockIndexValue>;
|
|
63
|
+
/** Stores the pending chain validation status */
|
|
64
|
+
#pendingChainValidationStatus: AztecAsyncSingleton<Buffer>;
|
|
43
65
|
|
|
44
66
|
/** Index mapping a contract's address (as a string) to its location in a block */
|
|
45
67
|
#contractIndex: AztecAsyncMap<string, BlockIndexValue>;
|
|
46
68
|
|
|
69
|
+
/** Index mapping block hash to block number */
|
|
70
|
+
#blockHashIndex: AztecAsyncMap<string, number>;
|
|
71
|
+
|
|
72
|
+
/** Index mapping block archive to block number */
|
|
73
|
+
#blockArchiveIndex: AztecAsyncMap<string, number>;
|
|
74
|
+
|
|
47
75
|
#log = createLogger('archiver:block_store');
|
|
48
76
|
|
|
49
77
|
constructor(private db: AztecAsyncKVStore) {
|
|
50
78
|
this.#blocks = db.openMap('archiver_blocks');
|
|
51
|
-
this.#
|
|
52
|
-
this.#
|
|
79
|
+
this.#blockTxs = db.openMap('archiver_block_txs');
|
|
80
|
+
this.#txEffects = db.openMap('archiver_tx_effects');
|
|
53
81
|
this.#contractIndex = db.openMap('archiver_contract_index');
|
|
82
|
+
this.#blockHashIndex = db.openMap('archiver_block_hash_index');
|
|
83
|
+
this.#blockArchiveIndex = db.openMap('archiver_block_archive_index');
|
|
54
84
|
this.#lastSynchedL1Block = db.openSingleton('archiver_last_synched_l1_block');
|
|
55
85
|
this.#lastProvenL2Block = db.openSingleton('archiver_last_proven_l2_block');
|
|
56
|
-
this.#
|
|
86
|
+
this.#pendingChainValidationStatus = db.openSingleton('archiver_pending_chain_validation_status');
|
|
57
87
|
}
|
|
58
88
|
|
|
59
89
|
/**
|
|
@@ -61,25 +91,59 @@ export class BlockStore {
|
|
|
61
91
|
* @param blocks - The L2 blocks to be added to the store.
|
|
62
92
|
* @returns True if the operation is successful.
|
|
63
93
|
*/
|
|
64
|
-
async addBlocks(blocks:
|
|
94
|
+
async addBlocks(blocks: PublishedL2Block[], opts: { force?: boolean } = {}): Promise<boolean> {
|
|
65
95
|
if (blocks.length === 0) {
|
|
66
96
|
return true;
|
|
67
97
|
}
|
|
68
98
|
|
|
69
99
|
return await this.db.transactionAsync(async () => {
|
|
100
|
+
// Check that the block immediately before the first block to be added is present in the store.
|
|
101
|
+
const firstBlockNumber = blocks[0].block.number;
|
|
102
|
+
const [previousBlockNumber] = await toArray(
|
|
103
|
+
this.#blocks.keysAsync({ reverse: true, limit: 1, end: firstBlockNumber - 1 }),
|
|
104
|
+
);
|
|
105
|
+
const hasPreviousBlock =
|
|
106
|
+
firstBlockNumber === INITIAL_L2_BLOCK_NUM ||
|
|
107
|
+
(previousBlockNumber !== undefined && previousBlockNumber === firstBlockNumber - 1);
|
|
108
|
+
if (!opts.force && !hasPreviousBlock) {
|
|
109
|
+
throw new InitialBlockNumberNotSequentialError(firstBlockNumber, previousBlockNumber);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Iterate over blocks array and insert them, checking that the block numbers are sequential.
|
|
113
|
+
let previousBlock: PublishedL2Block | undefined = undefined;
|
|
70
114
|
for (const block of blocks) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
115
|
+
if (!opts.force && previousBlock && previousBlock.block.number + 1 !== block.block.number) {
|
|
116
|
+
throw new BlockNumberNotSequentialError(block.block.number, previousBlock.block.number);
|
|
117
|
+
}
|
|
118
|
+
previousBlock = block;
|
|
119
|
+
const blockHash = L2BlockHash.fromField(await block.block.hash());
|
|
120
|
+
|
|
121
|
+
await this.#blocks.set(block.block.number, {
|
|
122
|
+
header: block.block.header.toBuffer(),
|
|
123
|
+
blockHash: blockHash.toBuffer(),
|
|
124
|
+
archive: block.block.archive.toBuffer(),
|
|
74
125
|
l1: block.l1,
|
|
126
|
+
attestations: block.attestations.map(attestation => attestation.toBuffer()),
|
|
75
127
|
});
|
|
76
128
|
|
|
77
|
-
for (let i = 0; i < block.
|
|
78
|
-
const txEffect =
|
|
79
|
-
|
|
129
|
+
for (let i = 0; i < block.block.body.txEffects.length; i++) {
|
|
130
|
+
const txEffect: IndexedTxEffect = {
|
|
131
|
+
data: block.block.body.txEffects[i],
|
|
132
|
+
l2BlockNumber: block.block.number,
|
|
133
|
+
l2BlockHash: blockHash,
|
|
134
|
+
txIndexInBlock: i,
|
|
135
|
+
};
|
|
136
|
+
await this.#txEffects.set(txEffect.data.txHash.toString(), serializeIndexedTxEffect(txEffect));
|
|
80
137
|
}
|
|
81
138
|
|
|
82
|
-
await this.#
|
|
139
|
+
await this.#blockTxs.set(
|
|
140
|
+
blockHash.toString(),
|
|
141
|
+
Buffer.concat(block.block.body.txEffects.map(tx => tx.txHash.toBuffer())),
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
// Update indices for block hash and archive
|
|
145
|
+
await this.#blockHashIndex.set(blockHash.toString(), block.block.number);
|
|
146
|
+
await this.#blockArchiveIndex.set(block.block.archive.root.toString(), block.block.number);
|
|
83
147
|
}
|
|
84
148
|
|
|
85
149
|
await this.#lastSynchedL1Block.set(blocks[blocks.length - 1].l1.blockNumber);
|
|
@@ -101,6 +165,11 @@ export class BlockStore {
|
|
|
101
165
|
throw new Error(`Can only unwind blocks from the tip (requested ${from} but current tip is ${last})`);
|
|
102
166
|
}
|
|
103
167
|
|
|
168
|
+
const proven = await this.getProvenL2BlockNumber();
|
|
169
|
+
if (from - blocksToUnwind < proven) {
|
|
170
|
+
await this.setProvenL2BlockNumber(from - blocksToUnwind);
|
|
171
|
+
}
|
|
172
|
+
|
|
104
173
|
for (let i = 0; i < blocksToUnwind; i++) {
|
|
105
174
|
const blockNumber = from - i;
|
|
106
175
|
const block = await this.getBlock(blockNumber);
|
|
@@ -109,10 +178,15 @@ export class BlockStore {
|
|
|
109
178
|
this.#log.warn(`Cannot remove block ${blockNumber} from the store since we don't have it`);
|
|
110
179
|
continue;
|
|
111
180
|
}
|
|
112
|
-
await this.#blocks.delete(block.
|
|
113
|
-
await Promise.all(block.
|
|
114
|
-
const blockHash = (await block.
|
|
115
|
-
await this.#
|
|
181
|
+
await this.#blocks.delete(block.block.number);
|
|
182
|
+
await Promise.all(block.block.body.txEffects.map(tx => this.#txEffects.delete(tx.txHash.toString())));
|
|
183
|
+
const blockHash = (await block.block.hash()).toString();
|
|
184
|
+
await this.#blockTxs.delete(blockHash);
|
|
185
|
+
|
|
186
|
+
// Clean up indices
|
|
187
|
+
await this.#blockHashIndex.delete(blockHash);
|
|
188
|
+
await this.#blockArchiveIndex.delete(block.block.archive.root.toString());
|
|
189
|
+
|
|
116
190
|
this.#log.debug(`Unwound block ${blockNumber} ${blockHash}`);
|
|
117
191
|
}
|
|
118
192
|
|
|
@@ -126,10 +200,12 @@ export class BlockStore {
|
|
|
126
200
|
* @param limit - The number of blocks to return.
|
|
127
201
|
* @returns The requested L2 blocks
|
|
128
202
|
*/
|
|
129
|
-
async *getBlocks(start: number, limit: number): AsyncIterableIterator<
|
|
130
|
-
for await (const blockStorage of this
|
|
131
|
-
const block = await this.getBlockFromBlockStorage(blockStorage);
|
|
132
|
-
|
|
203
|
+
async *getBlocks(start: number, limit: number): AsyncIterableIterator<PublishedL2Block> {
|
|
204
|
+
for await (const [blockNumber, blockStorage] of this.getBlockStorages(start, limit)) {
|
|
205
|
+
const block = await this.getBlockFromBlockStorage(blockNumber, blockStorage);
|
|
206
|
+
if (block) {
|
|
207
|
+
yield block;
|
|
208
|
+
}
|
|
133
209
|
}
|
|
134
210
|
}
|
|
135
211
|
|
|
@@ -138,13 +214,72 @@ export class BlockStore {
|
|
|
138
214
|
* @param blockNumber - The number of the block to return.
|
|
139
215
|
* @returns The requested L2 block.
|
|
140
216
|
*/
|
|
141
|
-
async getBlock(blockNumber: number): Promise<
|
|
217
|
+
async getBlock(blockNumber: number): Promise<PublishedL2Block | undefined> {
|
|
142
218
|
const blockStorage = await this.#blocks.getAsync(blockNumber);
|
|
143
219
|
if (!blockStorage || !blockStorage.header) {
|
|
144
220
|
return Promise.resolve(undefined);
|
|
145
221
|
}
|
|
222
|
+
return this.getBlockFromBlockStorage(blockNumber, blockStorage);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Gets an L2 block by its hash.
|
|
227
|
+
* @param blockHash - The hash of the block to return.
|
|
228
|
+
* @returns The requested L2 block.
|
|
229
|
+
*/
|
|
230
|
+
async getBlockByHash(blockHash: L2BlockHash): Promise<PublishedL2Block | undefined> {
|
|
231
|
+
const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
|
|
232
|
+
if (blockNumber === undefined) {
|
|
233
|
+
return undefined;
|
|
234
|
+
}
|
|
235
|
+
return this.getBlock(blockNumber);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Gets an L2 block by its archive root.
|
|
240
|
+
* @param archive - The archive root of the block to return.
|
|
241
|
+
* @returns The requested L2 block.
|
|
242
|
+
*/
|
|
243
|
+
async getBlockByArchive(archive: Fr): Promise<PublishedL2Block | undefined> {
|
|
244
|
+
const blockNumber = await this.#blockArchiveIndex.getAsync(archive.toString());
|
|
245
|
+
if (blockNumber === undefined) {
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
return this.getBlock(blockNumber);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Gets a block header by its hash.
|
|
253
|
+
* @param blockHash - The hash of the block to return.
|
|
254
|
+
* @returns The requested block header.
|
|
255
|
+
*/
|
|
256
|
+
async getBlockHeaderByHash(blockHash: L2BlockHash): Promise<BlockHeader | undefined> {
|
|
257
|
+
const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
|
|
258
|
+
if (blockNumber === undefined) {
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
const blockStorage = await this.#blocks.getAsync(blockNumber);
|
|
262
|
+
if (!blockStorage || !blockStorage.header) {
|
|
263
|
+
return undefined;
|
|
264
|
+
}
|
|
265
|
+
return L2BlockHeader.fromBuffer(blockStorage.header).toBlockHeader();
|
|
266
|
+
}
|
|
146
267
|
|
|
147
|
-
|
|
268
|
+
/**
|
|
269
|
+
* Gets a block header by its archive root.
|
|
270
|
+
* @param archive - The archive root of the block to return.
|
|
271
|
+
* @returns The requested block header.
|
|
272
|
+
*/
|
|
273
|
+
async getBlockHeaderByArchive(archive: Fr): Promise<BlockHeader | undefined> {
|
|
274
|
+
const blockNumber = await this.#blockArchiveIndex.getAsync(archive.toString());
|
|
275
|
+
if (blockNumber === undefined) {
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
const blockStorage = await this.#blocks.getAsync(blockNumber);
|
|
279
|
+
if (!blockStorage || !blockStorage.header) {
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
return L2BlockHeader.fromBuffer(blockStorage.header).toBlockHeader();
|
|
148
283
|
}
|
|
149
284
|
|
|
150
285
|
/**
|
|
@@ -154,48 +289,80 @@ export class BlockStore {
|
|
|
154
289
|
* @returns The requested L2 block headers
|
|
155
290
|
*/
|
|
156
291
|
async *getBlockHeaders(start: number, limit: number): AsyncIterableIterator<BlockHeader> {
|
|
157
|
-
for await (const blockStorage of this
|
|
158
|
-
|
|
292
|
+
for await (const [blockNumber, blockStorage] of this.getBlockStorages(start, limit)) {
|
|
293
|
+
const header = L2BlockHeader.fromBuffer(blockStorage.header).toBlockHeader();
|
|
294
|
+
if (header.getBlockNumber() !== blockNumber) {
|
|
295
|
+
throw new Error(
|
|
296
|
+
`Block number mismatch when retrieving block header from archive (expected ${blockNumber} but got ${header.getBlockNumber()})`,
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
yield header;
|
|
159
300
|
}
|
|
160
301
|
}
|
|
161
302
|
|
|
162
|
-
private async
|
|
163
|
-
|
|
303
|
+
private async *getBlockStorages(start: number, limit: number) {
|
|
304
|
+
let expectedBlockNumber = start;
|
|
305
|
+
for await (const [blockNumber, blockStorage] of this.#blocks.entriesAsync(this.#computeBlockRange(start, limit))) {
|
|
306
|
+
if (blockNumber !== expectedBlockNumber) {
|
|
307
|
+
throw new Error(
|
|
308
|
+
`Block number mismatch when iterating blocks from archive (expected ${expectedBlockNumber} but got ${blockNumber})`,
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
expectedBlockNumber++;
|
|
312
|
+
yield [blockNumber, blockStorage] as const;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private async getBlockFromBlockStorage(
|
|
317
|
+
blockNumber: number,
|
|
318
|
+
blockStorage: BlockStorage,
|
|
319
|
+
): Promise<PublishedL2Block | undefined> {
|
|
320
|
+
const header = L2BlockHeader.fromBuffer(blockStorage.header);
|
|
164
321
|
const archive = AppendOnlyTreeSnapshot.fromBuffer(blockStorage.archive);
|
|
165
|
-
const blockHash =
|
|
166
|
-
const
|
|
167
|
-
|
|
322
|
+
const blockHash = blockStorage.blockHash;
|
|
323
|
+
const blockHashString = bufferToHex(blockHash);
|
|
324
|
+
const blockTxsBuffer = await this.#blockTxs.getAsync(blockHashString);
|
|
325
|
+
if (blockTxsBuffer === undefined) {
|
|
326
|
+
this.#log.warn(`Could not find body for block ${header.globalVariables.blockNumber} ${blockHash}`);
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const txEffects: TxEffect[] = [];
|
|
331
|
+
const reader = BufferReader.asReader(blockTxsBuffer);
|
|
332
|
+
while (!reader.isEmpty()) {
|
|
333
|
+
const txHash = reader.readObject(TxHash);
|
|
334
|
+
const txEffect = await this.#txEffects.getAsync(txHash.toString());
|
|
335
|
+
if (txEffect === undefined) {
|
|
336
|
+
this.#log.warn(`Could not find tx effect for tx ${txHash} in block ${blockNumber}`);
|
|
337
|
+
return undefined;
|
|
338
|
+
}
|
|
339
|
+
txEffects.push(deserializeIndexedTxEffect(txEffect).data);
|
|
340
|
+
}
|
|
341
|
+
const body = new Body(txEffects);
|
|
342
|
+
const block = new L2Block(archive, header, body, Fr.fromBuffer(blockHash));
|
|
343
|
+
|
|
344
|
+
if (block.number !== blockNumber) {
|
|
168
345
|
throw new Error(
|
|
169
|
-
`
|
|
346
|
+
`Block number mismatch when retrieving block from archive (expected ${blockNumber} but got ${
|
|
347
|
+
block.number
|
|
348
|
+
} with hash ${blockHashString})`,
|
|
170
349
|
);
|
|
171
350
|
}
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
const l2Block = new L2Block(archive, header, body);
|
|
175
|
-
return { data: l2Block, l1: blockStorage.l1 };
|
|
351
|
+
const attestations = blockStorage.attestations.map(CommitteeAttestation.fromBuffer);
|
|
352
|
+
return PublishedL2Block.fromFields({ block, l1: blockStorage.l1, attestations });
|
|
176
353
|
}
|
|
177
354
|
|
|
178
355
|
/**
|
|
179
356
|
* Gets a tx effect.
|
|
180
|
-
* @param txHash - The
|
|
181
|
-
* @returns The requested tx effect (or undefined if not found).
|
|
357
|
+
* @param txHash - The hash of the tx corresponding to the tx effect.
|
|
358
|
+
* @returns The requested tx effect with block info (or undefined if not found).
|
|
182
359
|
*/
|
|
183
|
-
async getTxEffect(txHash: TxHash): Promise<
|
|
184
|
-
const
|
|
185
|
-
if (
|
|
360
|
+
async getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
|
|
361
|
+
const buffer = await this.#txEffects.getAsync(txHash.toString());
|
|
362
|
+
if (!buffer) {
|
|
186
363
|
return undefined;
|
|
187
364
|
}
|
|
188
|
-
|
|
189
|
-
const block = await this.getBlock(blockNumber);
|
|
190
|
-
if (!block) {
|
|
191
|
-
return undefined;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
data: block.data.body.txEffects[txIndex],
|
|
196
|
-
l2BlockNumber: block.data.number,
|
|
197
|
-
l2BlockHash: (await block.data.hash()).toString(),
|
|
198
|
-
};
|
|
365
|
+
return deserializeIndexedTxEffect(buffer);
|
|
199
366
|
}
|
|
200
367
|
|
|
201
368
|
/**
|
|
@@ -204,21 +371,18 @@ export class BlockStore {
|
|
|
204
371
|
* @returns The requested tx receipt (or undefined if not found).
|
|
205
372
|
*/
|
|
206
373
|
async getSettledTxReceipt(txHash: TxHash): Promise<TxReceipt | undefined> {
|
|
207
|
-
const
|
|
208
|
-
if (
|
|
374
|
+
const txEffect = await this.getTxEffect(txHash);
|
|
375
|
+
if (!txEffect) {
|
|
209
376
|
return undefined;
|
|
210
377
|
}
|
|
211
378
|
|
|
212
|
-
const block = (await this.getBlock(blockNumber))!;
|
|
213
|
-
const tx = block.data.body.txEffects[txIndex];
|
|
214
|
-
|
|
215
379
|
return new TxReceipt(
|
|
216
380
|
txHash,
|
|
217
|
-
TxReceipt.statusFromRevertCode(
|
|
381
|
+
TxReceipt.statusFromRevertCode(txEffect.data.revertCode),
|
|
218
382
|
'',
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
383
|
+
txEffect.data.transactionFee.toBigInt(),
|
|
384
|
+
txEffect.l2BlockHash,
|
|
385
|
+
txEffect.l2BlockNumber,
|
|
222
386
|
);
|
|
223
387
|
}
|
|
224
388
|
|
|
@@ -227,8 +391,13 @@ export class BlockStore {
|
|
|
227
391
|
* @param txHash - The txHash of the tx.
|
|
228
392
|
* @returns The block number and index of the tx.
|
|
229
393
|
*/
|
|
230
|
-
getTxLocation(txHash: TxHash): Promise<[blockNumber: number, txIndex: number] | undefined> {
|
|
231
|
-
|
|
394
|
+
public async getTxLocation(txHash: TxHash): Promise<[blockNumber: number, txIndex: number] | undefined> {
|
|
395
|
+
const txEffect = await this.#txEffects.getAsync(txHash.toString());
|
|
396
|
+
if (!txEffect) {
|
|
397
|
+
return undefined;
|
|
398
|
+
}
|
|
399
|
+
const { l2BlockNumber, txIndexInBlock } = deserializeIndexedTxEffect(txEffect);
|
|
400
|
+
return [l2BlockNumber, txIndexInBlock];
|
|
232
401
|
}
|
|
233
402
|
|
|
234
403
|
/**
|
|
@@ -262,7 +431,11 @@ export class BlockStore {
|
|
|
262
431
|
}
|
|
263
432
|
|
|
264
433
|
async getProvenL2BlockNumber(): Promise<number> {
|
|
265
|
-
|
|
434
|
+
const [latestBlockNumber, provenBlockNumber] = await Promise.all([
|
|
435
|
+
this.getSynchedL2BlockNumber(),
|
|
436
|
+
this.#lastProvenL2Block.getAsync(),
|
|
437
|
+
]);
|
|
438
|
+
return (provenBlockNumber ?? 0) > latestBlockNumber ? latestBlockNumber : (provenBlockNumber ?? 0);
|
|
266
439
|
}
|
|
267
440
|
|
|
268
441
|
setProvenL2BlockNumber(blockNumber: number) {
|
|
@@ -280,4 +453,29 @@ export class BlockStore {
|
|
|
280
453
|
|
|
281
454
|
return { start, limit };
|
|
282
455
|
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Gets the pending chain validation status.
|
|
459
|
+
* @returns The validation status or undefined if not set.
|
|
460
|
+
*/
|
|
461
|
+
async getPendingChainValidationStatus(): Promise<ValidateBlockResult | undefined> {
|
|
462
|
+
const buffer = await this.#pendingChainValidationStatus.getAsync();
|
|
463
|
+
if (!buffer) {
|
|
464
|
+
return undefined;
|
|
465
|
+
}
|
|
466
|
+
return deserializeValidateBlockResult(buffer);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Sets the pending chain validation status.
|
|
471
|
+
* @param status - The validation status to store.
|
|
472
|
+
*/
|
|
473
|
+
async setPendingChainValidationStatus(status: ValidateBlockResult | undefined): Promise<void> {
|
|
474
|
+
if (status) {
|
|
475
|
+
const buffer = serializeValidateBlockResult(status);
|
|
476
|
+
await this.#pendingChainValidationStatus.set(buffer);
|
|
477
|
+
} else {
|
|
478
|
+
await this.#pendingChainValidationStatus.delete();
|
|
479
|
+
}
|
|
480
|
+
}
|
|
283
481
|
}
|
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
ContractClassPublic,
|
|
8
8
|
ContractClassPublicWithBlockNumber,
|
|
9
9
|
ExecutablePrivateFunctionWithMembershipProof,
|
|
10
|
-
|
|
10
|
+
UtilityFunctionWithMembershipProof,
|
|
11
11
|
} from '@aztec/stdlib/contract';
|
|
12
12
|
import { Vector } from '@aztec/stdlib/types';
|
|
13
13
|
|
|
@@ -60,7 +60,7 @@ export class ContractClassStore {
|
|
|
60
60
|
async addFunctions(
|
|
61
61
|
contractClassId: Fr,
|
|
62
62
|
newPrivateFunctions: ExecutablePrivateFunctionWithMembershipProof[],
|
|
63
|
-
|
|
63
|
+
newUtilityFunctions: UtilityFunctionWithMembershipProof[],
|
|
64
64
|
): Promise<boolean> {
|
|
65
65
|
await this.db.transactionAsync(async () => {
|
|
66
66
|
const existingClassBuffer = await this.#contractClasses.getAsync(contractClassId.toString());
|
|
@@ -69,7 +69,7 @@ export class ContractClassStore {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
const existingClass = deserializeContractClassPublic(existingClassBuffer);
|
|
72
|
-
const { privateFunctions: existingPrivateFns,
|
|
72
|
+
const { privateFunctions: existingPrivateFns, utilityFunctions: existingUtilityFns } = existingClass;
|
|
73
73
|
|
|
74
74
|
const updatedClass: Omit<ContractClassPublicWithBlockNumber, 'id'> = {
|
|
75
75
|
...existingClass,
|
|
@@ -77,11 +77,9 @@ export class ContractClassStore {
|
|
|
77
77
|
...existingPrivateFns,
|
|
78
78
|
...newPrivateFunctions.filter(newFn => !existingPrivateFns.some(f => f.selector.equals(newFn.selector))),
|
|
79
79
|
],
|
|
80
|
-
|
|
81
|
-
...
|
|
82
|
-
...
|
|
83
|
-
newFn => !existingUnconstrainedFns.some(f => f.selector.equals(newFn.selector)),
|
|
84
|
-
),
|
|
80
|
+
utilityFunctions: [
|
|
81
|
+
...existingUtilityFns,
|
|
82
|
+
...newUtilityFunctions.filter(newFn => !existingUtilityFns.some(f => f.selector.equals(newFn.selector))),
|
|
85
83
|
],
|
|
86
84
|
};
|
|
87
85
|
await this.#contractClasses.set(contractClassId.toString(), serializeContractClassPublic(updatedClass));
|
|
@@ -96,12 +94,10 @@ function serializeContractClassPublic(contractClass: Omit<ContractClassPublicWit
|
|
|
96
94
|
contractClass.l2BlockNumber,
|
|
97
95
|
numToUInt8(contractClass.version),
|
|
98
96
|
contractClass.artifactHash,
|
|
99
|
-
contractClass.publicFunctions.length,
|
|
100
|
-
contractClass.publicFunctions?.map(f => serializeToBuffer(f.selector, f.bytecode.length, f.bytecode)) ?? [],
|
|
101
97
|
contractClass.privateFunctions.length,
|
|
102
98
|
contractClass.privateFunctions.map(serializePrivateFunction),
|
|
103
|
-
contractClass.
|
|
104
|
-
contractClass.
|
|
99
|
+
contractClass.utilityFunctions.length,
|
|
100
|
+
contractClass.utilityFunctions.map(serializeUtilityFunction),
|
|
105
101
|
contractClass.packedBytecode.length,
|
|
106
102
|
contractClass.packedBytecode,
|
|
107
103
|
contractClass.privateFunctionsRoot,
|
|
@@ -116,7 +112,7 @@ function serializePrivateFunction(fn: ExecutablePrivateFunctionWithMembershipPro
|
|
|
116
112
|
fn.bytecode,
|
|
117
113
|
fn.functionMetadataHash,
|
|
118
114
|
fn.artifactMetadataHash,
|
|
119
|
-
fn.
|
|
115
|
+
fn.utilityFunctionsTreeRoot,
|
|
120
116
|
new Vector(fn.privateFunctionTreeSiblingPath),
|
|
121
117
|
fn.privateFunctionTreeLeafIndex,
|
|
122
118
|
new Vector(fn.artifactTreeSiblingPath),
|
|
@@ -124,7 +120,7 @@ function serializePrivateFunction(fn: ExecutablePrivateFunctionWithMembershipPro
|
|
|
124
120
|
);
|
|
125
121
|
}
|
|
126
122
|
|
|
127
|
-
function
|
|
123
|
+
function serializeUtilityFunction(fn: UtilityFunctionWithMembershipProof): Buffer {
|
|
128
124
|
return serializeToBuffer(
|
|
129
125
|
fn.selector,
|
|
130
126
|
fn.bytecode.length,
|
|
@@ -143,14 +139,8 @@ function deserializeContractClassPublic(buffer: Buffer): Omit<ContractClassPubli
|
|
|
143
139
|
l2BlockNumber: reader.readNumber(),
|
|
144
140
|
version: reader.readUInt8() as 1,
|
|
145
141
|
artifactHash: reader.readObject(Fr),
|
|
146
|
-
publicFunctions: reader.readVector({
|
|
147
|
-
fromBuffer: reader => ({
|
|
148
|
-
selector: reader.readObject(FunctionSelector),
|
|
149
|
-
bytecode: reader.readBuffer(),
|
|
150
|
-
}),
|
|
151
|
-
}),
|
|
152
142
|
privateFunctions: reader.readVector({ fromBuffer: deserializePrivateFunction }),
|
|
153
|
-
|
|
143
|
+
utilityFunctions: reader.readVector({ fromBuffer: deserializeUtilityFunction }),
|
|
154
144
|
packedBytecode: reader.readBuffer(),
|
|
155
145
|
privateFunctionsRoot: reader.readObject(Fr),
|
|
156
146
|
};
|
|
@@ -164,7 +154,7 @@ function deserializePrivateFunction(buffer: Buffer | BufferReader): ExecutablePr
|
|
|
164
154
|
bytecode: reader.readBuffer(),
|
|
165
155
|
functionMetadataHash: reader.readObject(Fr),
|
|
166
156
|
artifactMetadataHash: reader.readObject(Fr),
|
|
167
|
-
|
|
157
|
+
utilityFunctionsTreeRoot: reader.readObject(Fr),
|
|
168
158
|
privateFunctionTreeSiblingPath: reader.readVector(Fr),
|
|
169
159
|
privateFunctionTreeLeafIndex: reader.readNumber(),
|
|
170
160
|
artifactTreeSiblingPath: reader.readVector(Fr),
|
|
@@ -172,7 +162,7 @@ function deserializePrivateFunction(buffer: Buffer | BufferReader): ExecutablePr
|
|
|
172
162
|
};
|
|
173
163
|
}
|
|
174
164
|
|
|
175
|
-
function
|
|
165
|
+
function deserializeUtilityFunction(buffer: Buffer | BufferReader): UtilityFunctionWithMembershipProof {
|
|
176
166
|
const reader = BufferReader.asReader(buffer);
|
|
177
167
|
return {
|
|
178
168
|
selector: reader.readObject(FunctionSelector),
|