@aztec/archiver 0.0.1-commit.18ccd8f0 → 0.0.1-commit.1a421b1a1
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 -4
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +62 -110
- package/dest/errors.d.ts +7 -9
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +9 -14
- package/dest/factory.d.ts +3 -4
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +15 -13
- 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 +190 -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 +22 -20
- 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 +13 -8
- package/dest/modules/data_source_base.d.ts.map +1 -1
- package/dest/modules/data_source_base.js +28 -72
- package/dest/modules/data_store_updater.d.ts +22 -7
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +69 -29
- 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 +41 -10
- package/dest/store/block_store.d.ts +28 -26
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +125 -76
- package/dest/store/contract_class_store.d.ts +1 -1
- package/dest/store/contract_class_store.d.ts.map +1 -1
- package/dest/store/contract_class_store.js +11 -7
- package/dest/store/kv_archiver_store.d.ts +36 -14
- package/dest/store/kv_archiver_store.d.ts.map +1 -1
- package/dest/store/kv_archiver_store.js +39 -10
- 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 +1 -1
- package/dest/store/log_store.d.ts.map +1 -1
- package/dest/store/log_store.js +56 -36
- package/dest/store/message_store.js +1 -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 +84 -20
- 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 +24 -8
- 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 +74 -130
- package/src/errors.ts +10 -24
- package/src/factory.ts +29 -14
- 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 +249 -379
- package/src/l1/data_retrieval.ts +24 -26
- package/src/l1/spire_proposer.ts +7 -15
- package/src/modules/data_source_base.ts +58 -97
- package/src/modules/data_store_updater.ts +71 -30
- package/src/modules/instrumentation.ts +29 -2
- package/src/modules/l1_synchronizer.ts +46 -14
- package/src/store/block_store.ts +148 -105
- package/src/store/contract_class_store.ts +11 -7
- package/src/store/kv_archiver_store.ts +62 -16
- package/src/store/l2_tips_cache.ts +89 -0
- package/src/store/log_store.ts +95 -33
- package/src/store/message_store.ts +1 -1
- package/src/test/fake_l1_state.ts +110 -21
- package/src/test/mock_archiver.ts +3 -2
- package/src/test/mock_l2_block_source.ts +171 -85
- 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,7 @@ import {
|
|
|
18
19
|
deserializeValidateCheckpointResult,
|
|
19
20
|
serializeValidateCheckpointResult,
|
|
20
21
|
} from '@aztec/stdlib/block';
|
|
21
|
-
import { L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
22
|
-
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
|
|
22
|
+
import { type CheckpointData, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
23
23
|
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
24
24
|
import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
|
|
25
25
|
import {
|
|
@@ -34,15 +34,14 @@ import {
|
|
|
34
34
|
} from '@aztec/stdlib/tx';
|
|
35
35
|
|
|
36
36
|
import {
|
|
37
|
+
BlockAlreadyCheckpointedError,
|
|
37
38
|
BlockArchiveNotConsistentError,
|
|
38
39
|
BlockIndexNotSequentialError,
|
|
39
40
|
BlockNotFoundError,
|
|
40
41
|
BlockNumberNotSequentialError,
|
|
41
42
|
CannotOverwriteCheckpointedBlockError,
|
|
42
43
|
CheckpointNotFoundError,
|
|
43
|
-
CheckpointNumberNotConsistentError,
|
|
44
44
|
CheckpointNumberNotSequentialError,
|
|
45
|
-
InitialBlockNumberNotSequentialError,
|
|
46
45
|
InitialCheckpointNumberNotSequentialError,
|
|
47
46
|
} from '../errors.js';
|
|
48
47
|
|
|
@@ -61,23 +60,14 @@ type BlockStorage = {
|
|
|
61
60
|
type CheckpointStorage = {
|
|
62
61
|
header: Buffer;
|
|
63
62
|
archive: Buffer;
|
|
63
|
+
checkpointOutHash: Buffer;
|
|
64
64
|
checkpointNumber: number;
|
|
65
65
|
startBlock: number;
|
|
66
|
-
|
|
66
|
+
blockCount: number;
|
|
67
67
|
l1: Buffer;
|
|
68
68
|
attestations: Buffer[];
|
|
69
69
|
};
|
|
70
70
|
|
|
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
71
|
export type RemoveCheckpointsResult = { blocksRemoved: L2Block[] | undefined };
|
|
82
72
|
|
|
83
73
|
/**
|
|
@@ -90,6 +80,9 @@ export class BlockStore {
|
|
|
90
80
|
/** Map checkpoint number to checkpoint data */
|
|
91
81
|
#checkpoints: AztecAsyncMap<number, CheckpointStorage>;
|
|
92
82
|
|
|
83
|
+
/** Map slot number to checkpoint number, for looking up checkpoints by slot range. */
|
|
84
|
+
#slotToCheckpoint: AztecAsyncMap<number, number>;
|
|
85
|
+
|
|
93
86
|
/** Map block hash to list of tx hashes */
|
|
94
87
|
#blockTxs: AztecAsyncMap<string, Buffer>;
|
|
95
88
|
|
|
@@ -102,6 +95,9 @@ export class BlockStore {
|
|
|
102
95
|
/** Stores last proven checkpoint */
|
|
103
96
|
#lastProvenCheckpoint: AztecAsyncSingleton<number>;
|
|
104
97
|
|
|
98
|
+
/** Stores last finalized checkpoint (proven at or before the finalized L1 block) */
|
|
99
|
+
#lastFinalizedCheckpoint: AztecAsyncSingleton<number>;
|
|
100
|
+
|
|
105
101
|
/** Stores the pending chain validation status */
|
|
106
102
|
#pendingChainValidationStatus: AztecAsyncSingleton<Buffer>;
|
|
107
103
|
|
|
@@ -116,10 +112,7 @@ export class BlockStore {
|
|
|
116
112
|
|
|
117
113
|
#log = createLogger('archiver:block_store');
|
|
118
114
|
|
|
119
|
-
constructor(
|
|
120
|
-
private db: AztecAsyncKVStore,
|
|
121
|
-
private l1Constants: Pick<L1RollupConstants, 'epochDuration'>,
|
|
122
|
-
) {
|
|
115
|
+
constructor(private db: AztecAsyncKVStore) {
|
|
123
116
|
this.#blocks = db.openMap('archiver_blocks');
|
|
124
117
|
this.#blockTxs = db.openMap('archiver_block_txs');
|
|
125
118
|
this.#txEffects = db.openMap('archiver_tx_effects');
|
|
@@ -128,40 +121,42 @@ export class BlockStore {
|
|
|
128
121
|
this.#blockArchiveIndex = db.openMap('archiver_block_archive_index');
|
|
129
122
|
this.#lastSynchedL1Block = db.openSingleton('archiver_last_synched_l1_block');
|
|
130
123
|
this.#lastProvenCheckpoint = db.openSingleton('archiver_last_proven_l2_checkpoint');
|
|
124
|
+
this.#lastFinalizedCheckpoint = db.openSingleton('archiver_last_finalized_l2_checkpoint');
|
|
131
125
|
this.#pendingChainValidationStatus = db.openSingleton('archiver_pending_chain_validation_status');
|
|
132
126
|
this.#checkpoints = db.openMap('archiver_checkpoints');
|
|
127
|
+
this.#slotToCheckpoint = db.openMap('archiver_slot_to_checkpoint');
|
|
133
128
|
}
|
|
134
129
|
|
|
135
130
|
/**
|
|
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
|
|
131
|
+
* Returns the finalized L2 block number. An L2 block is finalized when it was proven
|
|
132
|
+
* in an L1 block that has itself been finalized on Ethereum.
|
|
140
133
|
* @returns The finalized block number.
|
|
141
134
|
*/
|
|
142
135
|
async getFinalizedL2BlockNumber(): Promise<BlockNumber> {
|
|
143
|
-
const
|
|
144
|
-
|
|
136
|
+
const finalizedCheckpointNumber = await this.getFinalizedCheckpointNumber();
|
|
137
|
+
if (finalizedCheckpointNumber === INITIAL_CHECKPOINT_NUMBER - 1) {
|
|
138
|
+
return BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
139
|
+
}
|
|
140
|
+
const checkpointStorage = await this.#checkpoints.getAsync(finalizedCheckpointNumber);
|
|
141
|
+
if (!checkpointStorage) {
|
|
142
|
+
throw new CheckpointNotFoundError(finalizedCheckpointNumber);
|
|
143
|
+
}
|
|
144
|
+
return BlockNumber(checkpointStorage.startBlock + checkpointStorage.blockCount - 1);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
/**
|
|
148
|
-
* Append new proposed
|
|
149
|
-
*
|
|
148
|
+
* Append a new proposed block to the store.
|
|
149
|
+
* This is an uncheckpointed block that has been proposed by the sequencer but not yet included in a checkpoint on L1.
|
|
150
150
|
* For checkpointed blocks (already published to L1), use addCheckpoints() instead.
|
|
151
|
-
* @param
|
|
151
|
+
* @param block - The proposed L2 block to be added to the store.
|
|
152
152
|
* @returns True if the operation is successful.
|
|
153
153
|
*/
|
|
154
|
-
async
|
|
155
|
-
if (blocks.length === 0) {
|
|
156
|
-
return true;
|
|
157
|
-
}
|
|
158
|
-
|
|
154
|
+
async addProposedBlock(block: L2Block, opts: { force?: boolean } = {}): Promise<boolean> {
|
|
159
155
|
return await this.db.transactionAsync(async () => {
|
|
160
|
-
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
const firstBlockLastArchive = blocks[0].header.lastArchive.root;
|
|
156
|
+
const blockNumber = block.number;
|
|
157
|
+
const blockCheckpointNumber = block.checkpointNumber;
|
|
158
|
+
const blockIndex = block.indexWithinCheckpoint;
|
|
159
|
+
const blockLastArchive = block.header.lastArchive.root;
|
|
165
160
|
|
|
166
161
|
// Extract the latest block and checkpoint numbers
|
|
167
162
|
const previousBlockNumber = await this.getLatestBlockNumber();
|
|
@@ -169,71 +164,52 @@ export class BlockStore {
|
|
|
169
164
|
|
|
170
165
|
// Verify we're not overwriting checkpointed blocks
|
|
171
166
|
const lastCheckpointedBlockNumber = await this.getCheckpointedL2BlockNumber();
|
|
172
|
-
if (!opts.force &&
|
|
173
|
-
|
|
167
|
+
if (!opts.force && blockNumber <= lastCheckpointedBlockNumber) {
|
|
168
|
+
// Check if the proposed block matches the already-checkpointed one
|
|
169
|
+
const existingBlock = await this.getBlock(BlockNumber(blockNumber));
|
|
170
|
+
if (existingBlock && existingBlock.archive.root.equals(block.archive.root)) {
|
|
171
|
+
throw new BlockAlreadyCheckpointedError(blockNumber);
|
|
172
|
+
}
|
|
173
|
+
throw new CannotOverwriteCheckpointedBlockError(blockNumber, lastCheckpointedBlockNumber);
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
// Check that the
|
|
177
|
-
if (!opts.force && previousBlockNumber !==
|
|
178
|
-
throw new
|
|
176
|
+
// Check that the block number is the expected one
|
|
177
|
+
if (!opts.force && previousBlockNumber !== blockNumber - 1) {
|
|
178
|
+
throw new BlockNumberNotSequentialError(blockNumber, previousBlockNumber);
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
// The same check as above but for checkpoints
|
|
182
|
-
if (!opts.force && previousCheckpointNumber !==
|
|
183
|
-
throw new
|
|
182
|
+
if (!opts.force && previousCheckpointNumber !== blockCheckpointNumber - 1) {
|
|
183
|
+
throw new CheckpointNumberNotSequentialError(blockCheckpointNumber, previousCheckpointNumber);
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
// Extract the previous block if there is one and see if it is for the same checkpoint or not
|
|
187
187
|
const previousBlockResult = await this.getBlock(previousBlockNumber);
|
|
188
188
|
|
|
189
|
-
let
|
|
189
|
+
let expectedBlockIndex = 0;
|
|
190
190
|
let previousBlockIndex: number | undefined = undefined;
|
|
191
191
|
if (previousBlockResult !== undefined) {
|
|
192
|
-
if (previousBlockResult.checkpointNumber ===
|
|
192
|
+
if (previousBlockResult.checkpointNumber === blockCheckpointNumber) {
|
|
193
193
|
// The previous block is for the same checkpoint, therefore our index should follow it
|
|
194
194
|
previousBlockIndex = previousBlockResult.indexWithinCheckpoint;
|
|
195
|
-
|
|
195
|
+
expectedBlockIndex = previousBlockIndex + 1;
|
|
196
196
|
}
|
|
197
|
-
if (!previousBlockResult.archive.root.equals(
|
|
197
|
+
if (!previousBlockResult.archive.root.equals(blockLastArchive)) {
|
|
198
198
|
throw new BlockArchiveNotConsistentError(
|
|
199
|
-
|
|
199
|
+
blockNumber,
|
|
200
200
|
previousBlockResult.number,
|
|
201
|
-
|
|
201
|
+
blockLastArchive,
|
|
202
202
|
previousBlockResult.archive.root,
|
|
203
203
|
);
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
// Now check that the
|
|
208
|
-
if (!opts.force &&
|
|
209
|
-
throw new BlockIndexNotSequentialError(
|
|
207
|
+
// Now check that the block has the expected index value
|
|
208
|
+
if (!opts.force && expectedBlockIndex !== blockIndex) {
|
|
209
|
+
throw new BlockIndexNotSequentialError(blockIndex, previousBlockIndex);
|
|
210
210
|
}
|
|
211
211
|
|
|
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
|
-
}
|
|
212
|
+
await this.addBlockToDatabase(block, block.checkpointNumber, block.indexWithinCheckpoint);
|
|
237
213
|
|
|
238
214
|
return true;
|
|
239
215
|
});
|
|
@@ -273,7 +249,7 @@ export class BlockStore {
|
|
|
273
249
|
|
|
274
250
|
// If we have a previous checkpoint then we need to get the previous block number
|
|
275
251
|
if (previousCheckpointData !== undefined) {
|
|
276
|
-
previousBlockNumber = BlockNumber(previousCheckpointData.startBlock + previousCheckpointData.
|
|
252
|
+
previousBlockNumber = BlockNumber(previousCheckpointData.startBlock + previousCheckpointData.blockCount - 1);
|
|
277
253
|
previousBlock = await this.getBlock(previousBlockNumber);
|
|
278
254
|
if (previousBlock === undefined) {
|
|
279
255
|
// We should be able to get the required previous block
|
|
@@ -337,12 +313,16 @@ export class BlockStore {
|
|
|
337
313
|
await this.#checkpoints.set(checkpoint.checkpoint.number, {
|
|
338
314
|
header: checkpoint.checkpoint.header.toBuffer(),
|
|
339
315
|
archive: checkpoint.checkpoint.archive.toBuffer(),
|
|
316
|
+
checkpointOutHash: checkpoint.checkpoint.getCheckpointOutHash().toBuffer(),
|
|
340
317
|
l1: checkpoint.l1.toBuffer(),
|
|
341
318
|
attestations: checkpoint.attestations.map(attestation => attestation.toBuffer()),
|
|
342
319
|
checkpointNumber: checkpoint.checkpoint.number,
|
|
343
320
|
startBlock: checkpoint.checkpoint.blocks[0].number,
|
|
344
|
-
|
|
321
|
+
blockCount: checkpoint.checkpoint.blocks.length,
|
|
345
322
|
});
|
|
323
|
+
|
|
324
|
+
// Update slot-to-checkpoint index
|
|
325
|
+
await this.#slotToCheckpoint.set(checkpoint.checkpoint.header.slotNumber, checkpoint.checkpoint.number);
|
|
346
326
|
}
|
|
347
327
|
|
|
348
328
|
await this.#lastSynchedL1Block.set(checkpoints[checkpoints.length - 1].l1.blockNumber);
|
|
@@ -351,7 +331,7 @@ export class BlockStore {
|
|
|
351
331
|
}
|
|
352
332
|
|
|
353
333
|
private async addBlockToDatabase(block: L2Block, checkpointNumber: number, indexWithinCheckpoint: number) {
|
|
354
|
-
const blockHash =
|
|
334
|
+
const blockHash = await block.hash();
|
|
355
335
|
|
|
356
336
|
await this.#blocks.set(block.number, {
|
|
357
337
|
header: block.header.toBuffer(),
|
|
@@ -425,7 +405,7 @@ export class BlockStore {
|
|
|
425
405
|
if (!targetCheckpoint) {
|
|
426
406
|
throw new Error(`Target checkpoint ${checkpointNumber} not found in store`);
|
|
427
407
|
}
|
|
428
|
-
lastBlockToKeep = BlockNumber(targetCheckpoint.startBlock + targetCheckpoint.
|
|
408
|
+
lastBlockToKeep = BlockNumber(targetCheckpoint.startBlock + targetCheckpoint.blockCount - 1);
|
|
429
409
|
}
|
|
430
410
|
|
|
431
411
|
// Remove all blocks after lastBlockToKeep (both checkpointed and uncheckpointed)
|
|
@@ -433,6 +413,11 @@ export class BlockStore {
|
|
|
433
413
|
|
|
434
414
|
// Remove all checkpoints after the target
|
|
435
415
|
for (let c = latestCheckpointNumber; c > checkpointNumber; c = CheckpointNumber(c - 1)) {
|
|
416
|
+
const checkpointStorage = await this.#checkpoints.getAsync(c);
|
|
417
|
+
if (checkpointStorage) {
|
|
418
|
+
const slotNumber = CheckpointHeader.fromBuffer(checkpointStorage.header).slotNumber;
|
|
419
|
+
await this.#slotToCheckpoint.delete(slotNumber);
|
|
420
|
+
}
|
|
436
421
|
await this.#checkpoints.delete(c);
|
|
437
422
|
this.#log.debug(`Removed checkpoint ${c}`);
|
|
438
423
|
}
|
|
@@ -461,17 +446,32 @@ export class BlockStore {
|
|
|
461
446
|
return checkpoints;
|
|
462
447
|
}
|
|
463
448
|
|
|
464
|
-
|
|
465
|
-
|
|
449
|
+
/** Returns checkpoint data for all checkpoints whose slot falls within the given range (inclusive). */
|
|
450
|
+
async getCheckpointDataForSlotRange(startSlot: SlotNumber, endSlot: SlotNumber): Promise<CheckpointData[]> {
|
|
451
|
+
const result: CheckpointData[] = [];
|
|
452
|
+
for await (const [, checkpointNumber] of this.#slotToCheckpoint.entriesAsync({
|
|
453
|
+
start: startSlot,
|
|
454
|
+
end: endSlot + 1,
|
|
455
|
+
})) {
|
|
456
|
+
const checkpointStorage = await this.#checkpoints.getAsync(checkpointNumber);
|
|
457
|
+
if (checkpointStorage) {
|
|
458
|
+
result.push(this.checkpointDataFromCheckpointStorage(checkpointStorage));
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return result;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
private checkpointDataFromCheckpointStorage(checkpointStorage: CheckpointStorage): CheckpointData {
|
|
465
|
+
return {
|
|
466
466
|
header: CheckpointHeader.fromBuffer(checkpointStorage.header),
|
|
467
467
|
archive: AppendOnlyTreeSnapshot.fromBuffer(checkpointStorage.archive),
|
|
468
|
+
checkpointOutHash: Fr.fromBuffer(checkpointStorage.checkpointOutHash),
|
|
468
469
|
checkpointNumber: CheckpointNumber(checkpointStorage.checkpointNumber),
|
|
469
|
-
startBlock: checkpointStorage.startBlock,
|
|
470
|
-
|
|
470
|
+
startBlock: BlockNumber(checkpointStorage.startBlock),
|
|
471
|
+
blockCount: checkpointStorage.blockCount,
|
|
471
472
|
l1: L1PublishedData.fromBuffer(checkpointStorage.l1),
|
|
472
|
-
attestations: checkpointStorage.attestations,
|
|
473
|
+
attestations: checkpointStorage.attestations.map(buf => CommitteeAttestation.fromBuffer(buf)),
|
|
473
474
|
};
|
|
474
|
-
return data;
|
|
475
475
|
}
|
|
476
476
|
|
|
477
477
|
async getBlocksForCheckpoint(checkpointNumber: CheckpointNumber): Promise<L2Block[] | undefined> {
|
|
@@ -483,7 +483,7 @@ export class BlockStore {
|
|
|
483
483
|
const blocksForCheckpoint = await toArray(
|
|
484
484
|
this.#blocks.entriesAsync({
|
|
485
485
|
start: checkpoint.startBlock,
|
|
486
|
-
end: checkpoint.startBlock + checkpoint.
|
|
486
|
+
end: checkpoint.startBlock + checkpoint.blockCount,
|
|
487
487
|
}),
|
|
488
488
|
);
|
|
489
489
|
|
|
@@ -556,7 +556,7 @@ export class BlockStore {
|
|
|
556
556
|
if (!checkpointStorage) {
|
|
557
557
|
throw new CheckpointNotFoundError(provenCheckpointNumber);
|
|
558
558
|
} else {
|
|
559
|
-
return BlockNumber(checkpointStorage.startBlock + checkpointStorage.
|
|
559
|
+
return BlockNumber(checkpointStorage.startBlock + checkpointStorage.blockCount - 1);
|
|
560
560
|
}
|
|
561
561
|
}
|
|
562
562
|
|
|
@@ -624,7 +624,7 @@ export class BlockStore {
|
|
|
624
624
|
}
|
|
625
625
|
}
|
|
626
626
|
|
|
627
|
-
async getCheckpointedBlockByHash(blockHash:
|
|
627
|
+
async getCheckpointedBlockByHash(blockHash: BlockHash): Promise<CheckpointedL2Block | undefined> {
|
|
628
628
|
const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
|
|
629
629
|
if (blockNumber === undefined) {
|
|
630
630
|
return undefined;
|
|
@@ -655,6 +655,32 @@ export class BlockStore {
|
|
|
655
655
|
}
|
|
656
656
|
}
|
|
657
657
|
|
|
658
|
+
/**
|
|
659
|
+
* Gets block metadata (without tx data) by block number.
|
|
660
|
+
* @param blockNumber - The number of the block to return.
|
|
661
|
+
* @returns The requested block data.
|
|
662
|
+
*/
|
|
663
|
+
async getBlockData(blockNumber: BlockNumber): Promise<BlockData | undefined> {
|
|
664
|
+
const blockStorage = await this.#blocks.getAsync(blockNumber);
|
|
665
|
+
if (!blockStorage || !blockStorage.header) {
|
|
666
|
+
return undefined;
|
|
667
|
+
}
|
|
668
|
+
return this.getBlockDataFromBlockStorage(blockStorage);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Gets block metadata (without tx data) by archive root.
|
|
673
|
+
* @param archive - The archive root of the block to return.
|
|
674
|
+
* @returns The requested block data.
|
|
675
|
+
*/
|
|
676
|
+
async getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
|
|
677
|
+
const blockNumber = await this.#blockArchiveIndex.getAsync(archive.toString());
|
|
678
|
+
if (blockNumber === undefined) {
|
|
679
|
+
return undefined;
|
|
680
|
+
}
|
|
681
|
+
return this.getBlockData(BlockNumber(blockNumber));
|
|
682
|
+
}
|
|
683
|
+
|
|
658
684
|
/**
|
|
659
685
|
* Gets an L2 block.
|
|
660
686
|
* @param blockNumber - The number of the block to return.
|
|
@@ -759,15 +785,24 @@ export class BlockStore {
|
|
|
759
785
|
}
|
|
760
786
|
}
|
|
761
787
|
|
|
788
|
+
private getBlockDataFromBlockStorage(blockStorage: BlockStorage): BlockData {
|
|
789
|
+
return {
|
|
790
|
+
header: BlockHeader.fromBuffer(blockStorage.header),
|
|
791
|
+
archive: AppendOnlyTreeSnapshot.fromBuffer(blockStorage.archive),
|
|
792
|
+
blockHash: Fr.fromBuffer(blockStorage.blockHash),
|
|
793
|
+
checkpointNumber: CheckpointNumber(blockStorage.checkpointNumber),
|
|
794
|
+
indexWithinCheckpoint: IndexWithinCheckpoint(blockStorage.indexWithinCheckpoint),
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
|
|
762
798
|
private async getBlockFromBlockStorage(
|
|
763
799
|
blockNumber: number,
|
|
764
800
|
blockStorage: BlockStorage,
|
|
765
801
|
): Promise<L2Block | undefined> {
|
|
766
|
-
const header =
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
const blockHashString = bufferToHex(blockHash);
|
|
802
|
+
const { header, archive, blockHash, checkpointNumber, indexWithinCheckpoint } =
|
|
803
|
+
this.getBlockDataFromBlockStorage(blockStorage);
|
|
804
|
+
header.setHash(blockHash);
|
|
805
|
+
const blockHashString = bufferToHex(blockStorage.blockHash);
|
|
771
806
|
const blockTxsBuffer = await this.#blockTxs.getAsync(blockHashString);
|
|
772
807
|
if (blockTxsBuffer === undefined) {
|
|
773
808
|
this.#log.warn(`Could not find body for block ${header.globalVariables.blockNumber} ${blockHash}`);
|
|
@@ -786,13 +821,7 @@ export class BlockStore {
|
|
|
786
821
|
txEffects.push(deserializeIndexedTxEffect(txEffect).data);
|
|
787
822
|
}
|
|
788
823
|
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
|
-
);
|
|
824
|
+
const block = new L2Block(archive, header, body, checkpointNumber, indexWithinCheckpoint);
|
|
796
825
|
|
|
797
826
|
if (block.number !== blockNumber) {
|
|
798
827
|
throw new Error(
|
|
@@ -892,7 +921,7 @@ export class BlockStore {
|
|
|
892
921
|
if (!checkpoint) {
|
|
893
922
|
return BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
894
923
|
}
|
|
895
|
-
return BlockNumber(checkpoint.startBlock + checkpoint.
|
|
924
|
+
return BlockNumber(checkpoint.startBlock + checkpoint.blockCount - 1);
|
|
896
925
|
}
|
|
897
926
|
|
|
898
927
|
async getLatestL2BlockNumber(): Promise<BlockNumber> {
|
|
@@ -927,6 +956,20 @@ export class BlockStore {
|
|
|
927
956
|
return result;
|
|
928
957
|
}
|
|
929
958
|
|
|
959
|
+
async getFinalizedCheckpointNumber(): Promise<CheckpointNumber> {
|
|
960
|
+
const [latestCheckpointNumber, finalizedCheckpointNumber] = await Promise.all([
|
|
961
|
+
this.getLatestCheckpointNumber(),
|
|
962
|
+
this.#lastFinalizedCheckpoint.getAsync(),
|
|
963
|
+
]);
|
|
964
|
+
return (finalizedCheckpointNumber ?? 0) > latestCheckpointNumber
|
|
965
|
+
? latestCheckpointNumber
|
|
966
|
+
: CheckpointNumber(finalizedCheckpointNumber ?? 0);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
setFinalizedCheckpointNumber(checkpointNumber: CheckpointNumber) {
|
|
970
|
+
return this.#lastFinalizedCheckpoint.set(checkpointNumber);
|
|
971
|
+
}
|
|
972
|
+
|
|
930
973
|
#computeBlockRange(start: BlockNumber, limit: number): Required<Pick<Range<number>, 'start' | 'limit'>> {
|
|
931
974
|
if (limit < 1) {
|
|
932
975
|
throw new Error(`Invalid limit: ${limit}`);
|
|
@@ -28,18 +28,22 @@ export class ContractClassStore {
|
|
|
28
28
|
bytecodeCommitment: Fr,
|
|
29
29
|
blockNumber: number,
|
|
30
30
|
): Promise<void> {
|
|
31
|
-
await this
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
await this.db.transactionAsync(async () => {
|
|
32
|
+
await this.#contractClasses.setIfNotExists(
|
|
33
|
+
contractClass.id.toString(),
|
|
34
|
+
serializeContractClassPublic({ ...contractClass, l2BlockNumber: blockNumber }),
|
|
35
|
+
);
|
|
36
|
+
await this.#bytecodeCommitments.setIfNotExists(contractClass.id.toString(), bytecodeCommitment.toBuffer());
|
|
37
|
+
});
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
async deleteContractClasses(contractClass: ContractClassPublic, blockNumber: number): Promise<void> {
|
|
39
41
|
const restoredContractClass = await this.#contractClasses.getAsync(contractClass.id.toString());
|
|
40
42
|
if (restoredContractClass && deserializeContractClassPublic(restoredContractClass).l2BlockNumber >= blockNumber) {
|
|
41
|
-
await this
|
|
42
|
-
|
|
43
|
+
await this.db.transactionAsync(async () => {
|
|
44
|
+
await this.#contractClasses.delete(contractClass.id.toString());
|
|
45
|
+
await this.#bytecodeCommitments.delete(contractClass.id.toString());
|
|
46
|
+
});
|
|
43
47
|
}
|
|
44
48
|
}
|
|
45
49
|
|
|
@@ -6,8 +6,14 @@ import { createLogger } from '@aztec/foundation/log';
|
|
|
6
6
|
import type { AztecAsyncKVStore, CustomRange, StoreSize } from '@aztec/kv-store';
|
|
7
7
|
import { FunctionSelector } from '@aztec/stdlib/abi';
|
|
8
8
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
9
|
-
import {
|
|
10
|
-
|
|
9
|
+
import {
|
|
10
|
+
type BlockData,
|
|
11
|
+
BlockHash,
|
|
12
|
+
CheckpointedL2Block,
|
|
13
|
+
L2Block,
|
|
14
|
+
type ValidateCheckpointResult,
|
|
15
|
+
} from '@aztec/stdlib/block';
|
|
16
|
+
import type { CheckpointData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
11
17
|
import type {
|
|
12
18
|
ContractClassPublic,
|
|
13
19
|
ContractDataSource,
|
|
@@ -16,7 +22,6 @@ import type {
|
|
|
16
22
|
ExecutablePrivateFunctionWithMembershipProof,
|
|
17
23
|
UtilityFunctionWithMembershipProof,
|
|
18
24
|
} from '@aztec/stdlib/contract';
|
|
19
|
-
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
|
|
20
25
|
import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client';
|
|
21
26
|
import type { LogFilter, SiloedTag, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
|
|
22
27
|
import type { BlockHeader, TxHash, TxReceipt } from '@aztec/stdlib/tx';
|
|
@@ -25,7 +30,7 @@ import type { UInt64 } from '@aztec/stdlib/types';
|
|
|
25
30
|
import { join } from 'path';
|
|
26
31
|
|
|
27
32
|
import type { InboxMessage } from '../structs/inbox_message.js';
|
|
28
|
-
import { BlockStore, type
|
|
33
|
+
import { BlockStore, type RemoveCheckpointsResult } from './block_store.js';
|
|
29
34
|
import { ContractClassStore } from './contract_class_store.js';
|
|
30
35
|
import { ContractInstanceStore } from './contract_instance_store.js';
|
|
31
36
|
import { LogStore } from './log_store.js';
|
|
@@ -65,15 +70,19 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
65
70
|
constructor(
|
|
66
71
|
private db: AztecAsyncKVStore,
|
|
67
72
|
logsMaxPageSize: number = 1000,
|
|
68
|
-
l1Constants: Pick<L1RollupConstants, 'epochDuration'>,
|
|
69
73
|
) {
|
|
70
|
-
this.#blockStore = new BlockStore(db
|
|
74
|
+
this.#blockStore = new BlockStore(db);
|
|
71
75
|
this.#logStore = new LogStore(db, this.#blockStore, logsMaxPageSize);
|
|
72
76
|
this.#messageStore = new MessageStore(db);
|
|
73
77
|
this.#contractClassStore = new ContractClassStore(db);
|
|
74
78
|
this.#contractInstanceStore = new ContractInstanceStore(db);
|
|
75
79
|
}
|
|
76
80
|
|
|
81
|
+
/** Returns the underlying block store. Used by L2TipsCache. */
|
|
82
|
+
get blockStore(): BlockStore {
|
|
83
|
+
return this.#blockStore;
|
|
84
|
+
}
|
|
85
|
+
|
|
77
86
|
/** Opens a new transaction to the underlying store and runs all operations within it. */
|
|
78
87
|
public transactionAsync<T>(callback: () => Promise<T>): Promise<T> {
|
|
79
88
|
return this.db.transactionAsync(callback);
|
|
@@ -235,14 +244,14 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
235
244
|
}
|
|
236
245
|
|
|
237
246
|
/**
|
|
238
|
-
* Append new proposed
|
|
239
|
-
*
|
|
247
|
+
* Append a new proposed block to the store.
|
|
248
|
+
* This is an uncheckpointed block that has been proposed by the sequencer but not yet included in a checkpoint on L1.
|
|
240
249
|
* For checkpointed blocks (already published to L1), use addCheckpoints() instead.
|
|
241
|
-
* @param
|
|
250
|
+
* @param block - The proposed L2 block to be added to the store.
|
|
242
251
|
* @returns True if the operation is successful.
|
|
243
252
|
*/
|
|
244
|
-
|
|
245
|
-
return this.#blockStore.
|
|
253
|
+
addProposedBlock(block: L2Block, opts: { force?: boolean } = {}): Promise<boolean> {
|
|
254
|
+
return this.#blockStore.addProposedBlock(block, opts);
|
|
246
255
|
}
|
|
247
256
|
|
|
248
257
|
/**
|
|
@@ -291,7 +300,7 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
291
300
|
* Returns the block for the given hash, or undefined if not exists.
|
|
292
301
|
* @param blockHash - The block hash to return.
|
|
293
302
|
*/
|
|
294
|
-
getCheckpointedBlockByHash(blockHash:
|
|
303
|
+
getCheckpointedBlockByHash(blockHash: BlockHash): Promise<CheckpointedL2Block | undefined> {
|
|
295
304
|
return this.#blockStore.getCheckpointedBlockByHash(blockHash);
|
|
296
305
|
}
|
|
297
306
|
/**
|
|
@@ -312,8 +321,8 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
312
321
|
* Returns the block for the given hash, or undefined if not exists.
|
|
313
322
|
* @param blockHash - The block hash to return.
|
|
314
323
|
*/
|
|
315
|
-
getBlockByHash(blockHash:
|
|
316
|
-
return this.#blockStore.getBlockByHash(
|
|
324
|
+
getBlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
|
|
325
|
+
return this.#blockStore.getBlockByHash(blockHash);
|
|
317
326
|
}
|
|
318
327
|
/**
|
|
319
328
|
* Returns the block for the given archive root, or undefined if not exists.
|
|
@@ -357,8 +366,8 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
357
366
|
* Returns the block header for the given hash, or undefined if not exists.
|
|
358
367
|
* @param blockHash - The block hash to return.
|
|
359
368
|
*/
|
|
360
|
-
getBlockHeaderByHash(blockHash:
|
|
361
|
-
return this.#blockStore.getBlockHeaderByHash(
|
|
369
|
+
getBlockHeaderByHash(blockHash: BlockHash): Promise<BlockHeader | undefined> {
|
|
370
|
+
return this.#blockStore.getBlockHeaderByHash(blockHash);
|
|
362
371
|
}
|
|
363
372
|
|
|
364
373
|
/**
|
|
@@ -369,6 +378,22 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
369
378
|
return this.#blockStore.getBlockHeaderByArchive(archive);
|
|
370
379
|
}
|
|
371
380
|
|
|
381
|
+
/**
|
|
382
|
+
* Gets block metadata (without tx data) by block number.
|
|
383
|
+
* @param blockNumber - The block number to return.
|
|
384
|
+
*/
|
|
385
|
+
getBlockData(blockNumber: BlockNumber): Promise<BlockData | undefined> {
|
|
386
|
+
return this.#blockStore.getBlockData(blockNumber);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Gets block metadata (without tx data) by archive root.
|
|
391
|
+
* @param archive - The archive root to return.
|
|
392
|
+
*/
|
|
393
|
+
getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
|
|
394
|
+
return this.#blockStore.getBlockDataByArchive(archive);
|
|
395
|
+
}
|
|
396
|
+
|
|
372
397
|
/**
|
|
373
398
|
* Gets a tx effect.
|
|
374
399
|
* @param txHash - The hash of the tx corresponding to the tx effect.
|
|
@@ -515,6 +540,22 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
515
540
|
await this.#blockStore.setProvenCheckpointNumber(checkpointNumber);
|
|
516
541
|
}
|
|
517
542
|
|
|
543
|
+
/**
|
|
544
|
+
* Gets the number of the latest finalized checkpoint processed.
|
|
545
|
+
* @returns The number of the latest finalized checkpoint processed.
|
|
546
|
+
*/
|
|
547
|
+
getFinalizedCheckpointNumber(): Promise<CheckpointNumber> {
|
|
548
|
+
return this.#blockStore.getFinalizedCheckpointNumber();
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Stores the number of the latest finalized checkpoint processed.
|
|
553
|
+
* @param checkpointNumber - The number of the latest finalized checkpoint processed.
|
|
554
|
+
*/
|
|
555
|
+
async setFinalizedCheckpointNumber(checkpointNumber: CheckpointNumber) {
|
|
556
|
+
await this.#blockStore.setFinalizedCheckpointNumber(checkpointNumber);
|
|
557
|
+
}
|
|
558
|
+
|
|
518
559
|
async setBlockSynchedL1BlockNumber(l1BlockNumber: bigint) {
|
|
519
560
|
await this.#blockStore.setSynchedL1BlockNumber(l1BlockNumber);
|
|
520
561
|
}
|
|
@@ -618,6 +659,11 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
618
659
|
return this.#blockStore.getCheckpointData(checkpointNumber);
|
|
619
660
|
}
|
|
620
661
|
|
|
662
|
+
/** Returns checkpoint data for all checkpoints whose slot falls within the given range (inclusive). */
|
|
663
|
+
getCheckpointDataForSlotRange(startSlot: SlotNumber, endSlot: SlotNumber): Promise<CheckpointData[]> {
|
|
664
|
+
return this.#blockStore.getCheckpointDataForSlotRange(startSlot, endSlot);
|
|
665
|
+
}
|
|
666
|
+
|
|
621
667
|
/**
|
|
622
668
|
* Gets all blocks that have the given slot number.
|
|
623
669
|
* @param slotNumber - The slot number to search for.
|