@aztec/archiver 0.0.1-commit.9d2bcf6d → 0.0.1-commit.9ef841308
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.d.ts +7 -5
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +73 -111
- package/dest/config.d.ts +3 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +2 -1
- package/dest/errors.d.ts +21 -9
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +27 -14
- package/dest/factory.d.ts +4 -5
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +31 -26
- package/dest/index.d.ts +2 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/l1/bin/retrieve-calldata.js +36 -33
- package/dest/l1/calldata_retriever.d.ts +73 -50
- package/dest/l1/calldata_retriever.d.ts.map +1 -1
- package/dest/l1/calldata_retriever.js +191 -259
- package/dest/l1/data_retrieval.d.ts +9 -9
- package/dest/l1/data_retrieval.d.ts.map +1 -1
- package/dest/l1/data_retrieval.js +21 -19
- package/dest/l1/spire_proposer.d.ts +5 -5
- package/dest/l1/spire_proposer.d.ts.map +1 -1
- package/dest/l1/spire_proposer.js +9 -17
- package/dest/modules/data_source_base.d.ts +12 -7
- package/dest/modules/data_source_base.d.ts.map +1 -1
- package/dest/modules/data_source_base.js +33 -77
- package/dest/modules/data_store_updater.d.ts +24 -12
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +116 -94
- package/dest/modules/instrumentation.d.ts +15 -2
- package/dest/modules/instrumentation.d.ts.map +1 -1
- package/dest/modules/instrumentation.js +19 -2
- package/dest/modules/l1_synchronizer.d.ts +5 -8
- package/dest/modules/l1_synchronizer.d.ts.map +1 -1
- package/dest/modules/l1_synchronizer.js +50 -15
- package/dest/store/block_store.d.ts +29 -26
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +130 -78
- package/dest/store/contract_class_store.d.ts +2 -3
- package/dest/store/contract_class_store.d.ts.map +1 -1
- package/dest/store/contract_class_store.js +7 -67
- package/dest/store/contract_instance_store.d.ts +1 -1
- package/dest/store/contract_instance_store.d.ts.map +1 -1
- package/dest/store/contract_instance_store.js +6 -2
- package/dest/store/kv_archiver_store.d.ts +45 -21
- package/dest/store/kv_archiver_store.d.ts.map +1 -1
- package/dest/store/kv_archiver_store.js +52 -21
- package/dest/store/l2_tips_cache.d.ts +19 -0
- package/dest/store/l2_tips_cache.d.ts.map +1 -0
- package/dest/store/l2_tips_cache.js +89 -0
- package/dest/store/log_store.d.ts +6 -3
- package/dest/store/log_store.d.ts.map +1 -1
- package/dest/store/log_store.js +148 -51
- package/dest/store/message_store.d.ts +5 -1
- package/dest/store/message_store.d.ts.map +1 -1
- package/dest/store/message_store.js +14 -1
- package/dest/test/fake_l1_state.d.ts +13 -1
- package/dest/test/fake_l1_state.d.ts.map +1 -1
- package/dest/test/fake_l1_state.js +95 -23
- 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 +3 -2
- package/dest/test/mock_l2_block_source.d.ts +21 -5
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +132 -86
- package/dest/test/mock_structs.d.ts +4 -1
- package/dest/test/mock_structs.d.ts.map +1 -1
- package/dest/test/mock_structs.js +13 -1
- package/dest/test/noop_l1_archiver.d.ts +4 -1
- package/dest/test/noop_l1_archiver.d.ts.map +1 -1
- package/dest/test/noop_l1_archiver.js +5 -1
- package/package.json +13 -13
- package/src/archiver.ts +88 -131
- package/src/config.ts +8 -1
- package/src/errors.ts +40 -24
- package/src/factory.ts +46 -24
- package/src/index.ts +1 -0
- package/src/l1/README.md +25 -68
- package/src/l1/bin/retrieve-calldata.ts +46 -39
- package/src/l1/calldata_retriever.ts +250 -379
- package/src/l1/data_retrieval.ts +23 -25
- package/src/l1/spire_proposer.ts +7 -15
- package/src/modules/data_source_base.ts +64 -98
- package/src/modules/data_store_updater.ts +125 -124
- package/src/modules/instrumentation.ts +29 -2
- package/src/modules/l1_synchronizer.ts +61 -24
- package/src/store/block_store.ts +157 -105
- package/src/store/contract_class_store.ts +8 -106
- package/src/store/contract_instance_store.ts +8 -5
- package/src/store/kv_archiver_store.ts +78 -35
- package/src/store/l2_tips_cache.ts +89 -0
- package/src/store/log_store.ts +219 -58
- package/src/store/message_store.ts +20 -1
- package/src/test/fake_l1_state.ts +125 -26
- package/src/test/mock_archiver.ts +3 -2
- package/src/test/mock_l2_block_source.ts +173 -81
- package/src/test/mock_structs.ts +20 -6
- package/src/test/noop_l1_archiver.ts +7 -1
package/src/store/block_store.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { isDefined } from '@aztec/foundation/types';
|
|
|
9
9
|
import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncSingleton, Range } from '@aztec/kv-store';
|
|
10
10
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
11
11
|
import {
|
|
12
|
+
type BlockData,
|
|
12
13
|
BlockHash,
|
|
13
14
|
Body,
|
|
14
15
|
CheckpointedL2Block,
|
|
@@ -18,8 +19,8 @@ import {
|
|
|
18
19
|
deserializeValidateCheckpointResult,
|
|
19
20
|
serializeValidateCheckpointResult,
|
|
20
21
|
} from '@aztec/stdlib/block';
|
|
21
|
-
import { L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
22
|
-
import type
|
|
22
|
+
import { type CheckpointData, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
23
|
+
import { type L1RollupConstants, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
23
24
|
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
24
25
|
import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
|
|
25
26
|
import {
|
|
@@ -34,15 +35,14 @@ import {
|
|
|
34
35
|
} from '@aztec/stdlib/tx';
|
|
35
36
|
|
|
36
37
|
import {
|
|
38
|
+
BlockAlreadyCheckpointedError,
|
|
37
39
|
BlockArchiveNotConsistentError,
|
|
38
40
|
BlockIndexNotSequentialError,
|
|
39
41
|
BlockNotFoundError,
|
|
40
42
|
BlockNumberNotSequentialError,
|
|
41
43
|
CannotOverwriteCheckpointedBlockError,
|
|
42
44
|
CheckpointNotFoundError,
|
|
43
|
-
CheckpointNumberNotConsistentError,
|
|
44
45
|
CheckpointNumberNotSequentialError,
|
|
45
|
-
InitialBlockNumberNotSequentialError,
|
|
46
46
|
InitialCheckpointNumberNotSequentialError,
|
|
47
47
|
} from '../errors.js';
|
|
48
48
|
|
|
@@ -61,23 +61,14 @@ type BlockStorage = {
|
|
|
61
61
|
type CheckpointStorage = {
|
|
62
62
|
header: Buffer;
|
|
63
63
|
archive: Buffer;
|
|
64
|
+
checkpointOutHash: Buffer;
|
|
64
65
|
checkpointNumber: number;
|
|
65
66
|
startBlock: number;
|
|
66
|
-
|
|
67
|
+
blockCount: number;
|
|
67
68
|
l1: Buffer;
|
|
68
69
|
attestations: Buffer[];
|
|
69
70
|
};
|
|
70
71
|
|
|
71
|
-
export type CheckpointData = {
|
|
72
|
-
checkpointNumber: CheckpointNumber;
|
|
73
|
-
header: CheckpointHeader;
|
|
74
|
-
archive: AppendOnlyTreeSnapshot;
|
|
75
|
-
startBlock: number;
|
|
76
|
-
numBlocks: number;
|
|
77
|
-
l1: L1PublishedData;
|
|
78
|
-
attestations: Buffer[];
|
|
79
|
-
};
|
|
80
|
-
|
|
81
72
|
export type RemoveCheckpointsResult = { blocksRemoved: L2Block[] | undefined };
|
|
82
73
|
|
|
83
74
|
/**
|
|
@@ -90,6 +81,9 @@ export class BlockStore {
|
|
|
90
81
|
/** Map checkpoint number to checkpoint data */
|
|
91
82
|
#checkpoints: AztecAsyncMap<number, CheckpointStorage>;
|
|
92
83
|
|
|
84
|
+
/** Map slot number to checkpoint number, for looking up checkpoints by slot range. */
|
|
85
|
+
#slotToCheckpoint: AztecAsyncMap<number, number>;
|
|
86
|
+
|
|
93
87
|
/** Map block hash to list of tx hashes */
|
|
94
88
|
#blockTxs: AztecAsyncMap<string, Buffer>;
|
|
95
89
|
|
|
@@ -102,6 +96,9 @@ export class BlockStore {
|
|
|
102
96
|
/** Stores last proven checkpoint */
|
|
103
97
|
#lastProvenCheckpoint: AztecAsyncSingleton<number>;
|
|
104
98
|
|
|
99
|
+
/** Stores last finalized checkpoint (proven at or before the finalized L1 block) */
|
|
100
|
+
#lastFinalizedCheckpoint: AztecAsyncSingleton<number>;
|
|
101
|
+
|
|
105
102
|
/** Stores the pending chain validation status */
|
|
106
103
|
#pendingChainValidationStatus: AztecAsyncSingleton<Buffer>;
|
|
107
104
|
|
|
@@ -116,10 +113,7 @@ export class BlockStore {
|
|
|
116
113
|
|
|
117
114
|
#log = createLogger('archiver:block_store');
|
|
118
115
|
|
|
119
|
-
constructor(
|
|
120
|
-
private db: AztecAsyncKVStore,
|
|
121
|
-
private l1Constants: Pick<L1RollupConstants, 'epochDuration'>,
|
|
122
|
-
) {
|
|
116
|
+
constructor(private db: AztecAsyncKVStore) {
|
|
123
117
|
this.#blocks = db.openMap('archiver_blocks');
|
|
124
118
|
this.#blockTxs = db.openMap('archiver_block_txs');
|
|
125
119
|
this.#txEffects = db.openMap('archiver_tx_effects');
|
|
@@ -128,40 +122,42 @@ export class BlockStore {
|
|
|
128
122
|
this.#blockArchiveIndex = db.openMap('archiver_block_archive_index');
|
|
129
123
|
this.#lastSynchedL1Block = db.openSingleton('archiver_last_synched_l1_block');
|
|
130
124
|
this.#lastProvenCheckpoint = db.openSingleton('archiver_last_proven_l2_checkpoint');
|
|
125
|
+
this.#lastFinalizedCheckpoint = db.openSingleton('archiver_last_finalized_l2_checkpoint');
|
|
131
126
|
this.#pendingChainValidationStatus = db.openSingleton('archiver_pending_chain_validation_status');
|
|
132
127
|
this.#checkpoints = db.openMap('archiver_checkpoints');
|
|
128
|
+
this.#slotToCheckpoint = db.openMap('archiver_slot_to_checkpoint');
|
|
133
129
|
}
|
|
134
130
|
|
|
135
131
|
/**
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
* TODO(#13569): Compute proper finalized block number based on L1 finalized block.
|
|
139
|
-
* TODO(palla/mbps): Even the provisional computation is wrong, since it should subtract checkpoints, not blocks
|
|
132
|
+
* Returns the finalized L2 block number. An L2 block is finalized when it was proven
|
|
133
|
+
* in an L1 block that has itself been finalized on Ethereum.
|
|
140
134
|
* @returns The finalized block number.
|
|
141
135
|
*/
|
|
142
136
|
async getFinalizedL2BlockNumber(): Promise<BlockNumber> {
|
|
143
|
-
const
|
|
144
|
-
|
|
137
|
+
const finalizedCheckpointNumber = await this.getFinalizedCheckpointNumber();
|
|
138
|
+
if (finalizedCheckpointNumber === INITIAL_CHECKPOINT_NUMBER - 1) {
|
|
139
|
+
return BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
140
|
+
}
|
|
141
|
+
const checkpointStorage = await this.#checkpoints.getAsync(finalizedCheckpointNumber);
|
|
142
|
+
if (!checkpointStorage) {
|
|
143
|
+
throw new CheckpointNotFoundError(finalizedCheckpointNumber);
|
|
144
|
+
}
|
|
145
|
+
return BlockNumber(checkpointStorage.startBlock + checkpointStorage.blockCount - 1);
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
/**
|
|
148
|
-
* Append new proposed
|
|
149
|
-
*
|
|
149
|
+
* Append a new proposed block to the store.
|
|
150
|
+
* This is an uncheckpointed block that has been proposed by the sequencer but not yet included in a checkpoint on L1.
|
|
150
151
|
* For checkpointed blocks (already published to L1), use addCheckpoints() instead.
|
|
151
|
-
* @param
|
|
152
|
+
* @param block - The proposed L2 block to be added to the store.
|
|
152
153
|
* @returns True if the operation is successful.
|
|
153
154
|
*/
|
|
154
|
-
async
|
|
155
|
-
if (blocks.length === 0) {
|
|
156
|
-
return true;
|
|
157
|
-
}
|
|
158
|
-
|
|
155
|
+
async addProposedBlock(block: L2Block, opts: { force?: boolean } = {}): Promise<boolean> {
|
|
159
156
|
return await this.db.transactionAsync(async () => {
|
|
160
|
-
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
const firstBlockLastArchive = blocks[0].header.lastArchive.root;
|
|
157
|
+
const blockNumber = block.number;
|
|
158
|
+
const blockCheckpointNumber = block.checkpointNumber;
|
|
159
|
+
const blockIndex = block.indexWithinCheckpoint;
|
|
160
|
+
const blockLastArchive = block.header.lastArchive.root;
|
|
165
161
|
|
|
166
162
|
// Extract the latest block and checkpoint numbers
|
|
167
163
|
const previousBlockNumber = await this.getLatestBlockNumber();
|
|
@@ -169,71 +165,52 @@ export class BlockStore {
|
|
|
169
165
|
|
|
170
166
|
// Verify we're not overwriting checkpointed blocks
|
|
171
167
|
const lastCheckpointedBlockNumber = await this.getCheckpointedL2BlockNumber();
|
|
172
|
-
if (!opts.force &&
|
|
173
|
-
|
|
168
|
+
if (!opts.force && blockNumber <= lastCheckpointedBlockNumber) {
|
|
169
|
+
// Check if the proposed block matches the already-checkpointed one
|
|
170
|
+
const existingBlock = await this.getBlock(BlockNumber(blockNumber));
|
|
171
|
+
if (existingBlock && existingBlock.archive.root.equals(block.archive.root)) {
|
|
172
|
+
throw new BlockAlreadyCheckpointedError(blockNumber);
|
|
173
|
+
}
|
|
174
|
+
throw new CannotOverwriteCheckpointedBlockError(blockNumber, lastCheckpointedBlockNumber);
|
|
174
175
|
}
|
|
175
176
|
|
|
176
|
-
// Check that the
|
|
177
|
-
if (!opts.force && previousBlockNumber !==
|
|
178
|
-
throw new
|
|
177
|
+
// Check that the block number is the expected one
|
|
178
|
+
if (!opts.force && previousBlockNumber !== blockNumber - 1) {
|
|
179
|
+
throw new BlockNumberNotSequentialError(blockNumber, previousBlockNumber);
|
|
179
180
|
}
|
|
180
181
|
|
|
181
182
|
// The same check as above but for checkpoints
|
|
182
|
-
if (!opts.force && previousCheckpointNumber !==
|
|
183
|
-
throw new
|
|
183
|
+
if (!opts.force && previousCheckpointNumber !== blockCheckpointNumber - 1) {
|
|
184
|
+
throw new CheckpointNumberNotSequentialError(blockCheckpointNumber, previousCheckpointNumber);
|
|
184
185
|
}
|
|
185
186
|
|
|
186
187
|
// Extract the previous block if there is one and see if it is for the same checkpoint or not
|
|
187
188
|
const previousBlockResult = await this.getBlock(previousBlockNumber);
|
|
188
189
|
|
|
189
|
-
let
|
|
190
|
+
let expectedBlockIndex = 0;
|
|
190
191
|
let previousBlockIndex: number | undefined = undefined;
|
|
191
192
|
if (previousBlockResult !== undefined) {
|
|
192
|
-
if (previousBlockResult.checkpointNumber ===
|
|
193
|
+
if (previousBlockResult.checkpointNumber === blockCheckpointNumber) {
|
|
193
194
|
// The previous block is for the same checkpoint, therefore our index should follow it
|
|
194
195
|
previousBlockIndex = previousBlockResult.indexWithinCheckpoint;
|
|
195
|
-
|
|
196
|
+
expectedBlockIndex = previousBlockIndex + 1;
|
|
196
197
|
}
|
|
197
|
-
if (!previousBlockResult.archive.root.equals(
|
|
198
|
+
if (!previousBlockResult.archive.root.equals(blockLastArchive)) {
|
|
198
199
|
throw new BlockArchiveNotConsistentError(
|
|
199
|
-
|
|
200
|
+
blockNumber,
|
|
200
201
|
previousBlockResult.number,
|
|
201
|
-
|
|
202
|
+
blockLastArchive,
|
|
202
203
|
previousBlockResult.archive.root,
|
|
203
204
|
);
|
|
204
205
|
}
|
|
205
206
|
}
|
|
206
207
|
|
|
207
|
-
// Now check that the
|
|
208
|
-
if (!opts.force &&
|
|
209
|
-
throw new BlockIndexNotSequentialError(
|
|
208
|
+
// Now check that the block has the expected index value
|
|
209
|
+
if (!opts.force && expectedBlockIndex !== blockIndex) {
|
|
210
|
+
throw new BlockIndexNotSequentialError(blockIndex, previousBlockIndex);
|
|
210
211
|
}
|
|
211
212
|
|
|
212
|
-
|
|
213
|
-
let previousBlock: L2Block | undefined = undefined;
|
|
214
|
-
for (const block of blocks) {
|
|
215
|
-
if (!opts.force && previousBlock) {
|
|
216
|
-
if (previousBlock.number + 1 !== block.number) {
|
|
217
|
-
throw new BlockNumberNotSequentialError(block.number, previousBlock.number);
|
|
218
|
-
}
|
|
219
|
-
if (previousBlock.indexWithinCheckpoint + 1 !== block.indexWithinCheckpoint) {
|
|
220
|
-
throw new BlockIndexNotSequentialError(block.indexWithinCheckpoint, previousBlock.indexWithinCheckpoint);
|
|
221
|
-
}
|
|
222
|
-
if (!previousBlock.archive.root.equals(block.header.lastArchive.root)) {
|
|
223
|
-
throw new BlockArchiveNotConsistentError(
|
|
224
|
-
block.number,
|
|
225
|
-
previousBlock.number,
|
|
226
|
-
block.header.lastArchive.root,
|
|
227
|
-
previousBlock.archive.root,
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
if (!opts.force && firstBlockCheckpointNumber !== block.checkpointNumber) {
|
|
232
|
-
throw new CheckpointNumberNotConsistentError(block.checkpointNumber, firstBlockCheckpointNumber);
|
|
233
|
-
}
|
|
234
|
-
previousBlock = block;
|
|
235
|
-
await this.addBlockToDatabase(block, block.checkpointNumber, block.indexWithinCheckpoint);
|
|
236
|
-
}
|
|
213
|
+
await this.addBlockToDatabase(block, block.checkpointNumber, block.indexWithinCheckpoint);
|
|
237
214
|
|
|
238
215
|
return true;
|
|
239
216
|
});
|
|
@@ -273,7 +250,7 @@ export class BlockStore {
|
|
|
273
250
|
|
|
274
251
|
// If we have a previous checkpoint then we need to get the previous block number
|
|
275
252
|
if (previousCheckpointData !== undefined) {
|
|
276
|
-
previousBlockNumber = BlockNumber(previousCheckpointData.startBlock + previousCheckpointData.
|
|
253
|
+
previousBlockNumber = BlockNumber(previousCheckpointData.startBlock + previousCheckpointData.blockCount - 1);
|
|
277
254
|
previousBlock = await this.getBlock(previousBlockNumber);
|
|
278
255
|
if (previousBlock === undefined) {
|
|
279
256
|
// We should be able to get the required previous block
|
|
@@ -337,12 +314,16 @@ export class BlockStore {
|
|
|
337
314
|
await this.#checkpoints.set(checkpoint.checkpoint.number, {
|
|
338
315
|
header: checkpoint.checkpoint.header.toBuffer(),
|
|
339
316
|
archive: checkpoint.checkpoint.archive.toBuffer(),
|
|
317
|
+
checkpointOutHash: checkpoint.checkpoint.getCheckpointOutHash().toBuffer(),
|
|
340
318
|
l1: checkpoint.l1.toBuffer(),
|
|
341
319
|
attestations: checkpoint.attestations.map(attestation => attestation.toBuffer()),
|
|
342
320
|
checkpointNumber: checkpoint.checkpoint.number,
|
|
343
321
|
startBlock: checkpoint.checkpoint.blocks[0].number,
|
|
344
|
-
|
|
322
|
+
blockCount: checkpoint.checkpoint.blocks.length,
|
|
345
323
|
});
|
|
324
|
+
|
|
325
|
+
// Update slot-to-checkpoint index
|
|
326
|
+
await this.#slotToCheckpoint.set(checkpoint.checkpoint.header.slotNumber, checkpoint.checkpoint.number);
|
|
346
327
|
}
|
|
347
328
|
|
|
348
329
|
await this.#lastSynchedL1Block.set(checkpoints[checkpoints.length - 1].l1.blockNumber);
|
|
@@ -425,7 +406,7 @@ export class BlockStore {
|
|
|
425
406
|
if (!targetCheckpoint) {
|
|
426
407
|
throw new Error(`Target checkpoint ${checkpointNumber} not found in store`);
|
|
427
408
|
}
|
|
428
|
-
lastBlockToKeep = BlockNumber(targetCheckpoint.startBlock + targetCheckpoint.
|
|
409
|
+
lastBlockToKeep = BlockNumber(targetCheckpoint.startBlock + targetCheckpoint.blockCount - 1);
|
|
429
410
|
}
|
|
430
411
|
|
|
431
412
|
// Remove all blocks after lastBlockToKeep (both checkpointed and uncheckpointed)
|
|
@@ -433,6 +414,11 @@ export class BlockStore {
|
|
|
433
414
|
|
|
434
415
|
// Remove all checkpoints after the target
|
|
435
416
|
for (let c = latestCheckpointNumber; c > checkpointNumber; c = CheckpointNumber(c - 1)) {
|
|
417
|
+
const checkpointStorage = await this.#checkpoints.getAsync(c);
|
|
418
|
+
if (checkpointStorage) {
|
|
419
|
+
const slotNumber = CheckpointHeader.fromBuffer(checkpointStorage.header).slotNumber;
|
|
420
|
+
await this.#slotToCheckpoint.delete(slotNumber);
|
|
421
|
+
}
|
|
436
422
|
await this.#checkpoints.delete(c);
|
|
437
423
|
this.#log.debug(`Removed checkpoint ${c}`);
|
|
438
424
|
}
|
|
@@ -461,17 +447,32 @@ export class BlockStore {
|
|
|
461
447
|
return checkpoints;
|
|
462
448
|
}
|
|
463
449
|
|
|
464
|
-
|
|
465
|
-
|
|
450
|
+
/** Returns checkpoint data for all checkpoints whose slot falls within the given range (inclusive). */
|
|
451
|
+
async getCheckpointDataForSlotRange(startSlot: SlotNumber, endSlot: SlotNumber): Promise<CheckpointData[]> {
|
|
452
|
+
const result: CheckpointData[] = [];
|
|
453
|
+
for await (const [, checkpointNumber] of this.#slotToCheckpoint.entriesAsync({
|
|
454
|
+
start: startSlot,
|
|
455
|
+
end: endSlot + 1,
|
|
456
|
+
})) {
|
|
457
|
+
const checkpointStorage = await this.#checkpoints.getAsync(checkpointNumber);
|
|
458
|
+
if (checkpointStorage) {
|
|
459
|
+
result.push(this.checkpointDataFromCheckpointStorage(checkpointStorage));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return result;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
private checkpointDataFromCheckpointStorage(checkpointStorage: CheckpointStorage): CheckpointData {
|
|
466
|
+
return {
|
|
466
467
|
header: CheckpointHeader.fromBuffer(checkpointStorage.header),
|
|
467
468
|
archive: AppendOnlyTreeSnapshot.fromBuffer(checkpointStorage.archive),
|
|
469
|
+
checkpointOutHash: Fr.fromBuffer(checkpointStorage.checkpointOutHash),
|
|
468
470
|
checkpointNumber: CheckpointNumber(checkpointStorage.checkpointNumber),
|
|
469
|
-
startBlock: checkpointStorage.startBlock,
|
|
470
|
-
|
|
471
|
+
startBlock: BlockNumber(checkpointStorage.startBlock),
|
|
472
|
+
blockCount: checkpointStorage.blockCount,
|
|
471
473
|
l1: L1PublishedData.fromBuffer(checkpointStorage.l1),
|
|
472
|
-
attestations: checkpointStorage.attestations,
|
|
474
|
+
attestations: checkpointStorage.attestations.map(buf => CommitteeAttestation.fromBuffer(buf)),
|
|
473
475
|
};
|
|
474
|
-
return data;
|
|
475
476
|
}
|
|
476
477
|
|
|
477
478
|
async getBlocksForCheckpoint(checkpointNumber: CheckpointNumber): Promise<L2Block[] | undefined> {
|
|
@@ -483,7 +484,7 @@ export class BlockStore {
|
|
|
483
484
|
const blocksForCheckpoint = await toArray(
|
|
484
485
|
this.#blocks.entriesAsync({
|
|
485
486
|
start: checkpoint.startBlock,
|
|
486
|
-
end: checkpoint.startBlock + checkpoint.
|
|
487
|
+
end: checkpoint.startBlock + checkpoint.blockCount,
|
|
487
488
|
}),
|
|
488
489
|
);
|
|
489
490
|
|
|
@@ -556,7 +557,7 @@ export class BlockStore {
|
|
|
556
557
|
if (!checkpointStorage) {
|
|
557
558
|
throw new CheckpointNotFoundError(provenCheckpointNumber);
|
|
558
559
|
} else {
|
|
559
|
-
return BlockNumber(checkpointStorage.startBlock + checkpointStorage.
|
|
560
|
+
return BlockNumber(checkpointStorage.startBlock + checkpointStorage.blockCount - 1);
|
|
560
561
|
}
|
|
561
562
|
}
|
|
562
563
|
|
|
@@ -655,6 +656,32 @@ export class BlockStore {
|
|
|
655
656
|
}
|
|
656
657
|
}
|
|
657
658
|
|
|
659
|
+
/**
|
|
660
|
+
* Gets block metadata (without tx data) by block number.
|
|
661
|
+
* @param blockNumber - The number of the block to return.
|
|
662
|
+
* @returns The requested block data.
|
|
663
|
+
*/
|
|
664
|
+
async getBlockData(blockNumber: BlockNumber): Promise<BlockData | undefined> {
|
|
665
|
+
const blockStorage = await this.#blocks.getAsync(blockNumber);
|
|
666
|
+
if (!blockStorage || !blockStorage.header) {
|
|
667
|
+
return undefined;
|
|
668
|
+
}
|
|
669
|
+
return this.getBlockDataFromBlockStorage(blockStorage);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Gets block metadata (without tx data) by archive root.
|
|
674
|
+
* @param archive - The archive root of the block to return.
|
|
675
|
+
* @returns The requested block data.
|
|
676
|
+
*/
|
|
677
|
+
async getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
|
|
678
|
+
const blockNumber = await this.#blockArchiveIndex.getAsync(archive.toString());
|
|
679
|
+
if (blockNumber === undefined) {
|
|
680
|
+
return undefined;
|
|
681
|
+
}
|
|
682
|
+
return this.getBlockData(BlockNumber(blockNumber));
|
|
683
|
+
}
|
|
684
|
+
|
|
658
685
|
/**
|
|
659
686
|
* Gets an L2 block.
|
|
660
687
|
* @param blockNumber - The number of the block to return.
|
|
@@ -759,15 +786,24 @@ export class BlockStore {
|
|
|
759
786
|
}
|
|
760
787
|
}
|
|
761
788
|
|
|
789
|
+
private getBlockDataFromBlockStorage(blockStorage: BlockStorage): BlockData {
|
|
790
|
+
return {
|
|
791
|
+
header: BlockHeader.fromBuffer(blockStorage.header),
|
|
792
|
+
archive: AppendOnlyTreeSnapshot.fromBuffer(blockStorage.archive),
|
|
793
|
+
blockHash: Fr.fromBuffer(blockStorage.blockHash),
|
|
794
|
+
checkpointNumber: CheckpointNumber(blockStorage.checkpointNumber),
|
|
795
|
+
indexWithinCheckpoint: IndexWithinCheckpoint(blockStorage.indexWithinCheckpoint),
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
|
|
762
799
|
private async getBlockFromBlockStorage(
|
|
763
800
|
blockNumber: number,
|
|
764
801
|
blockStorage: BlockStorage,
|
|
765
802
|
): Promise<L2Block | undefined> {
|
|
766
|
-
const header =
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
const blockHashString = bufferToHex(blockHash);
|
|
803
|
+
const { header, archive, blockHash, checkpointNumber, indexWithinCheckpoint } =
|
|
804
|
+
this.getBlockDataFromBlockStorage(blockStorage);
|
|
805
|
+
header.setHash(blockHash);
|
|
806
|
+
const blockHashString = bufferToHex(blockStorage.blockHash);
|
|
771
807
|
const blockTxsBuffer = await this.#blockTxs.getAsync(blockHashString);
|
|
772
808
|
if (blockTxsBuffer === undefined) {
|
|
773
809
|
this.#log.warn(`Could not find body for block ${header.globalVariables.blockNumber} ${blockHash}`);
|
|
@@ -786,13 +822,7 @@ export class BlockStore {
|
|
|
786
822
|
txEffects.push(deserializeIndexedTxEffect(txEffect).data);
|
|
787
823
|
}
|
|
788
824
|
const body = new Body(txEffects);
|
|
789
|
-
const block = new L2Block(
|
|
790
|
-
archive,
|
|
791
|
-
header,
|
|
792
|
-
body,
|
|
793
|
-
CheckpointNumber(blockStorage.checkpointNumber!),
|
|
794
|
-
IndexWithinCheckpoint(blockStorage.indexWithinCheckpoint),
|
|
795
|
-
);
|
|
825
|
+
const block = new L2Block(archive, header, body, checkpointNumber, indexWithinCheckpoint);
|
|
796
826
|
|
|
797
827
|
if (block.number !== blockNumber) {
|
|
798
828
|
throw new Error(
|
|
@@ -822,7 +852,10 @@ export class BlockStore {
|
|
|
822
852
|
* @param txHash - The hash of a tx we try to get the receipt for.
|
|
823
853
|
* @returns The requested tx receipt (or undefined if not found).
|
|
824
854
|
*/
|
|
825
|
-
async getSettledTxReceipt(
|
|
855
|
+
async getSettledTxReceipt(
|
|
856
|
+
txHash: TxHash,
|
|
857
|
+
l1Constants?: Pick<L1RollupConstants, 'epochDuration'>,
|
|
858
|
+
): Promise<TxReceipt | undefined> {
|
|
826
859
|
const txEffect = await this.getTxEffect(txHash);
|
|
827
860
|
if (!txEffect) {
|
|
828
861
|
return undefined;
|
|
@@ -831,10 +864,11 @@ export class BlockStore {
|
|
|
831
864
|
const blockNumber = BlockNumber(txEffect.l2BlockNumber);
|
|
832
865
|
|
|
833
866
|
// Use existing archiver methods to determine finalization level
|
|
834
|
-
const [provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber] = await Promise.all([
|
|
867
|
+
const [provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber, blockData] = await Promise.all([
|
|
835
868
|
this.getProvenBlockNumber(),
|
|
836
869
|
this.getCheckpointedL2BlockNumber(),
|
|
837
870
|
this.getFinalizedL2BlockNumber(),
|
|
871
|
+
this.getBlockData(blockNumber),
|
|
838
872
|
]);
|
|
839
873
|
|
|
840
874
|
let status: TxStatus;
|
|
@@ -848,6 +882,9 @@ export class BlockStore {
|
|
|
848
882
|
status = TxStatus.PROPOSED;
|
|
849
883
|
}
|
|
850
884
|
|
|
885
|
+
const epochNumber =
|
|
886
|
+
blockData && l1Constants ? getEpochAtSlot(blockData.header.globalVariables.slotNumber, l1Constants) : undefined;
|
|
887
|
+
|
|
851
888
|
return new TxReceipt(
|
|
852
889
|
txHash,
|
|
853
890
|
status,
|
|
@@ -856,6 +893,7 @@ export class BlockStore {
|
|
|
856
893
|
txEffect.data.transactionFee.toBigInt(),
|
|
857
894
|
txEffect.l2BlockHash,
|
|
858
895
|
blockNumber,
|
|
896
|
+
epochNumber,
|
|
859
897
|
);
|
|
860
898
|
}
|
|
861
899
|
|
|
@@ -892,7 +930,7 @@ export class BlockStore {
|
|
|
892
930
|
if (!checkpoint) {
|
|
893
931
|
return BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
894
932
|
}
|
|
895
|
-
return BlockNumber(checkpoint.startBlock + checkpoint.
|
|
933
|
+
return BlockNumber(checkpoint.startBlock + checkpoint.blockCount - 1);
|
|
896
934
|
}
|
|
897
935
|
|
|
898
936
|
async getLatestL2BlockNumber(): Promise<BlockNumber> {
|
|
@@ -927,6 +965,20 @@ export class BlockStore {
|
|
|
927
965
|
return result;
|
|
928
966
|
}
|
|
929
967
|
|
|
968
|
+
async getFinalizedCheckpointNumber(): Promise<CheckpointNumber> {
|
|
969
|
+
const [latestCheckpointNumber, finalizedCheckpointNumber] = await Promise.all([
|
|
970
|
+
this.getLatestCheckpointNumber(),
|
|
971
|
+
this.#lastFinalizedCheckpoint.getAsync(),
|
|
972
|
+
]);
|
|
973
|
+
return (finalizedCheckpointNumber ?? 0) > latestCheckpointNumber
|
|
974
|
+
? latestCheckpointNumber
|
|
975
|
+
: CheckpointNumber(finalizedCheckpointNumber ?? 0);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
setFinalizedCheckpointNumber(checkpointNumber: CheckpointNumber) {
|
|
979
|
+
return this.#lastFinalizedCheckpoint.set(checkpointNumber);
|
|
980
|
+
}
|
|
981
|
+
|
|
930
982
|
#computeBlockRange(start: BlockNumber, limit: number): Required<Pick<Range<number>, 'start' | 'limit'>> {
|
|
931
983
|
if (limit < 1) {
|
|
932
984
|
throw new Error(`Invalid limit: ${limit}`);
|
|
@@ -2,14 +2,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
2
2
|
import { toArray } from '@aztec/foundation/iterable';
|
|
3
3
|
import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
4
4
|
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
5
|
-
import {
|
|
6
|
-
import type {
|
|
7
|
-
ContractClassPublic,
|
|
8
|
-
ContractClassPublicWithBlockNumber,
|
|
9
|
-
ExecutablePrivateFunctionWithMembershipProof,
|
|
10
|
-
UtilityFunctionWithMembershipProof,
|
|
11
|
-
} from '@aztec/stdlib/contract';
|
|
12
|
-
import { Vector } from '@aztec/stdlib/types';
|
|
5
|
+
import type { ContractClassPublic, ContractClassPublicWithBlockNumber } from '@aztec/stdlib/contract';
|
|
13
6
|
|
|
14
7
|
/**
|
|
15
8
|
* LMDB-based contract class storage for the archiver.
|
|
@@ -29,11 +22,15 @@ export class ContractClassStore {
|
|
|
29
22
|
blockNumber: number,
|
|
30
23
|
): Promise<void> {
|
|
31
24
|
await this.db.transactionAsync(async () => {
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
const key = contractClass.id.toString();
|
|
26
|
+
if (await this.#contractClasses.hasAsync(key)) {
|
|
27
|
+
throw new Error(`Contract class ${key} already exists, cannot add again at block ${blockNumber}`);
|
|
28
|
+
}
|
|
29
|
+
await this.#contractClasses.set(
|
|
30
|
+
key,
|
|
34
31
|
serializeContractClassPublic({ ...contractClass, l2BlockNumber: blockNumber }),
|
|
35
32
|
);
|
|
36
|
-
await this.#bytecodeCommitments.
|
|
33
|
+
await this.#bytecodeCommitments.set(key, bytecodeCommitment.toBuffer());
|
|
37
34
|
});
|
|
38
35
|
}
|
|
39
36
|
|
|
@@ -60,37 +57,6 @@ export class ContractClassStore {
|
|
|
60
57
|
async getContractClassIds(): Promise<Fr[]> {
|
|
61
58
|
return (await toArray(this.#contractClasses.keysAsync())).map(key => Fr.fromHexString(key));
|
|
62
59
|
}
|
|
63
|
-
|
|
64
|
-
async addFunctions(
|
|
65
|
-
contractClassId: Fr,
|
|
66
|
-
newPrivateFunctions: ExecutablePrivateFunctionWithMembershipProof[],
|
|
67
|
-
newUtilityFunctions: UtilityFunctionWithMembershipProof[],
|
|
68
|
-
): Promise<boolean> {
|
|
69
|
-
await this.db.transactionAsync(async () => {
|
|
70
|
-
const existingClassBuffer = await this.#contractClasses.getAsync(contractClassId.toString());
|
|
71
|
-
if (!existingClassBuffer) {
|
|
72
|
-
throw new Error(`Unknown contract class ${contractClassId} when adding private functions to store`);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const existingClass = deserializeContractClassPublic(existingClassBuffer);
|
|
76
|
-
const { privateFunctions: existingPrivateFns, utilityFunctions: existingUtilityFns } = existingClass;
|
|
77
|
-
|
|
78
|
-
const updatedClass: Omit<ContractClassPublicWithBlockNumber, 'id'> = {
|
|
79
|
-
...existingClass,
|
|
80
|
-
privateFunctions: [
|
|
81
|
-
...existingPrivateFns,
|
|
82
|
-
...newPrivateFunctions.filter(newFn => !existingPrivateFns.some(f => f.selector.equals(newFn.selector))),
|
|
83
|
-
],
|
|
84
|
-
utilityFunctions: [
|
|
85
|
-
...existingUtilityFns,
|
|
86
|
-
...newUtilityFunctions.filter(newFn => !existingUtilityFns.some(f => f.selector.equals(newFn.selector))),
|
|
87
|
-
],
|
|
88
|
-
};
|
|
89
|
-
await this.#contractClasses.set(contractClassId.toString(), serializeContractClassPublic(updatedClass));
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
return true;
|
|
93
|
-
}
|
|
94
60
|
}
|
|
95
61
|
|
|
96
62
|
function serializeContractClassPublic(contractClass: Omit<ContractClassPublicWithBlockNumber, 'id'>): Buffer {
|
|
@@ -98,83 +64,19 @@ function serializeContractClassPublic(contractClass: Omit<ContractClassPublicWit
|
|
|
98
64
|
contractClass.l2BlockNumber,
|
|
99
65
|
numToUInt8(contractClass.version),
|
|
100
66
|
contractClass.artifactHash,
|
|
101
|
-
contractClass.privateFunctions.length,
|
|
102
|
-
contractClass.privateFunctions.map(serializePrivateFunction),
|
|
103
|
-
contractClass.utilityFunctions.length,
|
|
104
|
-
contractClass.utilityFunctions.map(serializeUtilityFunction),
|
|
105
67
|
contractClass.packedBytecode.length,
|
|
106
68
|
contractClass.packedBytecode,
|
|
107
69
|
contractClass.privateFunctionsRoot,
|
|
108
70
|
);
|
|
109
71
|
}
|
|
110
72
|
|
|
111
|
-
function serializePrivateFunction(fn: ExecutablePrivateFunctionWithMembershipProof): Buffer {
|
|
112
|
-
return serializeToBuffer(
|
|
113
|
-
fn.selector,
|
|
114
|
-
fn.vkHash,
|
|
115
|
-
fn.bytecode.length,
|
|
116
|
-
fn.bytecode,
|
|
117
|
-
fn.functionMetadataHash,
|
|
118
|
-
fn.artifactMetadataHash,
|
|
119
|
-
fn.utilityFunctionsTreeRoot,
|
|
120
|
-
new Vector(fn.privateFunctionTreeSiblingPath),
|
|
121
|
-
fn.privateFunctionTreeLeafIndex,
|
|
122
|
-
new Vector(fn.artifactTreeSiblingPath),
|
|
123
|
-
fn.artifactTreeLeafIndex,
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function serializeUtilityFunction(fn: UtilityFunctionWithMembershipProof): Buffer {
|
|
128
|
-
return serializeToBuffer(
|
|
129
|
-
fn.selector,
|
|
130
|
-
fn.bytecode.length,
|
|
131
|
-
fn.bytecode,
|
|
132
|
-
fn.functionMetadataHash,
|
|
133
|
-
fn.artifactMetadataHash,
|
|
134
|
-
fn.privateFunctionsArtifactTreeRoot,
|
|
135
|
-
new Vector(fn.artifactTreeSiblingPath),
|
|
136
|
-
fn.artifactTreeLeafIndex,
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
73
|
function deserializeContractClassPublic(buffer: Buffer): Omit<ContractClassPublicWithBlockNumber, 'id'> {
|
|
141
74
|
const reader = BufferReader.asReader(buffer);
|
|
142
75
|
return {
|
|
143
76
|
l2BlockNumber: reader.readNumber(),
|
|
144
77
|
version: reader.readUInt8() as 1,
|
|
145
78
|
artifactHash: reader.readObject(Fr),
|
|
146
|
-
privateFunctions: reader.readVector({ fromBuffer: deserializePrivateFunction }),
|
|
147
|
-
utilityFunctions: reader.readVector({ fromBuffer: deserializeUtilityFunction }),
|
|
148
79
|
packedBytecode: reader.readBuffer(),
|
|
149
80
|
privateFunctionsRoot: reader.readObject(Fr),
|
|
150
81
|
};
|
|
151
82
|
}
|
|
152
|
-
|
|
153
|
-
function deserializePrivateFunction(buffer: Buffer | BufferReader): ExecutablePrivateFunctionWithMembershipProof {
|
|
154
|
-
const reader = BufferReader.asReader(buffer);
|
|
155
|
-
return {
|
|
156
|
-
selector: reader.readObject(FunctionSelector),
|
|
157
|
-
vkHash: reader.readObject(Fr),
|
|
158
|
-
bytecode: reader.readBuffer(),
|
|
159
|
-
functionMetadataHash: reader.readObject(Fr),
|
|
160
|
-
artifactMetadataHash: reader.readObject(Fr),
|
|
161
|
-
utilityFunctionsTreeRoot: reader.readObject(Fr),
|
|
162
|
-
privateFunctionTreeSiblingPath: reader.readVector(Fr),
|
|
163
|
-
privateFunctionTreeLeafIndex: reader.readNumber(),
|
|
164
|
-
artifactTreeSiblingPath: reader.readVector(Fr),
|
|
165
|
-
artifactTreeLeafIndex: reader.readNumber(),
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function deserializeUtilityFunction(buffer: Buffer | BufferReader): UtilityFunctionWithMembershipProof {
|
|
170
|
-
const reader = BufferReader.asReader(buffer);
|
|
171
|
-
return {
|
|
172
|
-
selector: reader.readObject(FunctionSelector),
|
|
173
|
-
bytecode: reader.readBuffer(),
|
|
174
|
-
functionMetadataHash: reader.readObject(Fr),
|
|
175
|
-
artifactMetadataHash: reader.readObject(Fr),
|
|
176
|
-
privateFunctionsArtifactTreeRoot: reader.readObject(Fr),
|
|
177
|
-
artifactTreeSiblingPath: reader.readVector(Fr),
|
|
178
|
-
artifactTreeLeafIndex: reader.readNumber(),
|
|
179
|
-
};
|
|
180
|
-
}
|
|
@@ -27,11 +27,14 @@ export class ContractInstanceStore {
|
|
|
27
27
|
|
|
28
28
|
addContractInstance(contractInstance: ContractInstanceWithAddress, blockNumber: number): Promise<void> {
|
|
29
29
|
return this.db.transactionAsync(async () => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
new
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
const key = contractInstance.address.toString();
|
|
31
|
+
if (await this.#contractInstances.hasAsync(key)) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`Contract instance at ${key} already exists (deployed at block ${await this.#contractInstancePublishedAt.getAsync(key)}), cannot add again at block ${blockNumber}`,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
await this.#contractInstances.set(key, new SerializableContractInstance(contractInstance).toBuffer());
|
|
37
|
+
await this.#contractInstancePublishedAt.set(key, blockNumber);
|
|
35
38
|
});
|
|
36
39
|
}
|
|
37
40
|
|