@aztec/archiver 0.0.1-commit.2ed92850 → 0.0.1-commit.343b43af6
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 +9 -5
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +76 -111
- 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 +5 -4
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +17 -16
- 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/l1/validate_trace.d.ts +6 -3
- package/dest/l1/validate_trace.d.ts.map +1 -1
- package/dest/l1/validate_trace.js +13 -9
- 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 +36 -12
- 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 +43 -13
- package/dest/store/block_store.d.ts +30 -28
- 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 +57 -37
- 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/index.js +3 -1
- 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 +25 -9
- 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 +6 -2
- package/dest/test/mock_structs.d.ts.map +1 -1
- package/dest/test/mock_structs.js +20 -6
- package/dest/test/noop_l1_archiver.d.ts +26 -0
- package/dest/test/noop_l1_archiver.d.ts.map +1 -0
- package/dest/test/noop_l1_archiver.js +72 -0
- package/package.json +14 -13
- package/src/archiver.ts +96 -132
- package/src/errors.ts +10 -24
- package/src/factory.ts +32 -17
- 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/l1/validate_trace.ts +24 -6
- package/src/modules/data_source_base.ts +58 -97
- package/src/modules/data_store_updater.ts +71 -30
- package/src/modules/instrumentation.ts +44 -12
- package/src/modules/l1_synchronizer.ts +48 -17
- package/src/store/block_store.ts +151 -108
- 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 +98 -36
- package/src/store/message_store.ts +1 -1
- package/src/test/fake_l1_state.ts +110 -21
- package/src/test/index.ts +3 -0
- package/src/test/mock_archiver.ts +3 -2
- package/src/test/mock_l2_block_source.ts +172 -86
- package/src/test/mock_structs.ts +42 -12
- package/src/test/noop_l1_archiver.ts +115 -0
package/src/store/block_store.ts
CHANGED
|
@@ -9,17 +9,17 @@ 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,
|
|
13
|
+
BlockHash,
|
|
12
14
|
Body,
|
|
13
15
|
CheckpointedL2Block,
|
|
14
16
|
CommitteeAttestation,
|
|
15
17
|
L2Block,
|
|
16
|
-
L2BlockHash,
|
|
17
18
|
type ValidateCheckpointResult,
|
|
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.
|
|
@@ -673,7 +699,7 @@ export class BlockStore {
|
|
|
673
699
|
* @param blockHash - The hash of the block to return.
|
|
674
700
|
* @returns The requested L2 block.
|
|
675
701
|
*/
|
|
676
|
-
async getBlockByHash(blockHash:
|
|
702
|
+
async getBlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
|
|
677
703
|
const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
|
|
678
704
|
if (blockNumber === undefined) {
|
|
679
705
|
return undefined;
|
|
@@ -699,7 +725,7 @@ export class BlockStore {
|
|
|
699
725
|
* @param blockHash - The hash of the block to return.
|
|
700
726
|
* @returns The requested block header.
|
|
701
727
|
*/
|
|
702
|
-
async getBlockHeaderByHash(blockHash:
|
|
728
|
+
async getBlockHeaderByHash(blockHash: BlockHash): Promise<BlockHeader | undefined> {
|
|
703
729
|
const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
|
|
704
730
|
if (blockNumber === undefined) {
|
|
705
731
|
return undefined;
|
|
@@ -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
|
|