@aztec/archiver 0.0.1-commit.b468ad8 → 0.0.1-commit.b64cb54f6
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 +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 +10 -5
- package/dest/modules/data_source_base.d.ts.map +1 -1
- package/dest/modules/data_source_base.js +29 -73
- 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 +29 -26
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +130 -78
- package/dest/store/kv_archiver_store.d.ts +34 -11
- package/dest/store/kv_archiver_store.d.ts.map +1 -1
- package/dest/store/kv_archiver_store.js +39 -9
- 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 +55 -35
- 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 +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 +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 +23 -25
- package/src/l1/spire_proposer.ts +7 -15
- package/src/modules/data_source_base.ts +56 -95
- 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 +157 -105
- package/src/store/kv_archiver_store.ts +62 -12
- package/src/store/l2_tips_cache.ts +89 -0
- package/src/store/log_store.ts +93 -31
- 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 +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}`);
|
|
@@ -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,
|
|
@@ -25,7 +31,7 @@ import type { UInt64 } from '@aztec/stdlib/types';
|
|
|
25
31
|
import { join } from 'path';
|
|
26
32
|
|
|
27
33
|
import type { InboxMessage } from '../structs/inbox_message.js';
|
|
28
|
-
import { BlockStore, type
|
|
34
|
+
import { BlockStore, type RemoveCheckpointsResult } from './block_store.js';
|
|
29
35
|
import { ContractClassStore } from './contract_class_store.js';
|
|
30
36
|
import { ContractInstanceStore } from './contract_instance_store.js';
|
|
31
37
|
import { LogStore } from './log_store.js';
|
|
@@ -65,15 +71,19 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
65
71
|
constructor(
|
|
66
72
|
private db: AztecAsyncKVStore,
|
|
67
73
|
logsMaxPageSize: number = 1000,
|
|
68
|
-
l1Constants: Pick<L1RollupConstants, 'epochDuration'>,
|
|
69
74
|
) {
|
|
70
|
-
this.#blockStore = new BlockStore(db
|
|
75
|
+
this.#blockStore = new BlockStore(db);
|
|
71
76
|
this.#logStore = new LogStore(db, this.#blockStore, logsMaxPageSize);
|
|
72
77
|
this.#messageStore = new MessageStore(db);
|
|
73
78
|
this.#contractClassStore = new ContractClassStore(db);
|
|
74
79
|
this.#contractInstanceStore = new ContractInstanceStore(db);
|
|
75
80
|
}
|
|
76
81
|
|
|
82
|
+
/** Returns the underlying block store. Used by L2TipsCache. */
|
|
83
|
+
get blockStore(): BlockStore {
|
|
84
|
+
return this.#blockStore;
|
|
85
|
+
}
|
|
86
|
+
|
|
77
87
|
/** Opens a new transaction to the underlying store and runs all operations within it. */
|
|
78
88
|
public transactionAsync<T>(callback: () => Promise<T>): Promise<T> {
|
|
79
89
|
return this.db.transactionAsync(callback);
|
|
@@ -235,14 +245,14 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
235
245
|
}
|
|
236
246
|
|
|
237
247
|
/**
|
|
238
|
-
* Append new proposed
|
|
239
|
-
*
|
|
248
|
+
* Append a new proposed block to the store.
|
|
249
|
+
* This is an uncheckpointed block that has been proposed by the sequencer but not yet included in a checkpoint on L1.
|
|
240
250
|
* For checkpointed blocks (already published to L1), use addCheckpoints() instead.
|
|
241
|
-
* @param
|
|
251
|
+
* @param block - The proposed L2 block to be added to the store.
|
|
242
252
|
* @returns True if the operation is successful.
|
|
243
253
|
*/
|
|
244
|
-
|
|
245
|
-
return this.#blockStore.
|
|
254
|
+
addProposedBlock(block: L2Block, opts: { force?: boolean } = {}): Promise<boolean> {
|
|
255
|
+
return this.#blockStore.addProposedBlock(block, opts);
|
|
246
256
|
}
|
|
247
257
|
|
|
248
258
|
/**
|
|
@@ -369,6 +379,22 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
369
379
|
return this.#blockStore.getBlockHeaderByArchive(archive);
|
|
370
380
|
}
|
|
371
381
|
|
|
382
|
+
/**
|
|
383
|
+
* Gets block metadata (without tx data) by block number.
|
|
384
|
+
* @param blockNumber - The block number to return.
|
|
385
|
+
*/
|
|
386
|
+
getBlockData(blockNumber: BlockNumber): Promise<BlockData | undefined> {
|
|
387
|
+
return this.#blockStore.getBlockData(blockNumber);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Gets block metadata (without tx data) by archive root.
|
|
392
|
+
* @param archive - The archive root to return.
|
|
393
|
+
*/
|
|
394
|
+
getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
|
|
395
|
+
return this.#blockStore.getBlockDataByArchive(archive);
|
|
396
|
+
}
|
|
397
|
+
|
|
372
398
|
/**
|
|
373
399
|
* Gets a tx effect.
|
|
374
400
|
* @param txHash - The hash of the tx corresponding to the tx effect.
|
|
@@ -383,8 +409,11 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
383
409
|
* @param txHash - The hash of a tx we try to get the receipt for.
|
|
384
410
|
* @returns The requested tx receipt (or undefined if not found).
|
|
385
411
|
*/
|
|
386
|
-
getSettledTxReceipt(
|
|
387
|
-
|
|
412
|
+
getSettledTxReceipt(
|
|
413
|
+
txHash: TxHash,
|
|
414
|
+
l1Constants?: Pick<L1RollupConstants, 'epochDuration'>,
|
|
415
|
+
): Promise<TxReceipt | undefined> {
|
|
416
|
+
return this.#blockStore.getSettledTxReceipt(txHash, l1Constants);
|
|
388
417
|
}
|
|
389
418
|
|
|
390
419
|
/**
|
|
@@ -515,6 +544,22 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
515
544
|
await this.#blockStore.setProvenCheckpointNumber(checkpointNumber);
|
|
516
545
|
}
|
|
517
546
|
|
|
547
|
+
/**
|
|
548
|
+
* Gets the number of the latest finalized checkpoint processed.
|
|
549
|
+
* @returns The number of the latest finalized checkpoint processed.
|
|
550
|
+
*/
|
|
551
|
+
getFinalizedCheckpointNumber(): Promise<CheckpointNumber> {
|
|
552
|
+
return this.#blockStore.getFinalizedCheckpointNumber();
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Stores the number of the latest finalized checkpoint processed.
|
|
557
|
+
* @param checkpointNumber - The number of the latest finalized checkpoint processed.
|
|
558
|
+
*/
|
|
559
|
+
async setFinalizedCheckpointNumber(checkpointNumber: CheckpointNumber) {
|
|
560
|
+
await this.#blockStore.setFinalizedCheckpointNumber(checkpointNumber);
|
|
561
|
+
}
|
|
562
|
+
|
|
518
563
|
async setBlockSynchedL1BlockNumber(l1BlockNumber: bigint) {
|
|
519
564
|
await this.#blockStore.setSynchedL1BlockNumber(l1BlockNumber);
|
|
520
565
|
}
|
|
@@ -618,6 +663,11 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
618
663
|
return this.#blockStore.getCheckpointData(checkpointNumber);
|
|
619
664
|
}
|
|
620
665
|
|
|
666
|
+
/** Returns checkpoint data for all checkpoints whose slot falls within the given range (inclusive). */
|
|
667
|
+
getCheckpointDataForSlotRange(startSlot: SlotNumber, endSlot: SlotNumber): Promise<CheckpointData[]> {
|
|
668
|
+
return this.#blockStore.getCheckpointDataForSlotRange(startSlot, endSlot);
|
|
669
|
+
}
|
|
670
|
+
|
|
621
671
|
/**
|
|
622
672
|
* Gets all blocks that have the given slot number.
|
|
623
673
|
* @param slotNumber - The slot number to search for.
|