@aztec/archiver 0.0.1-commit.3469e52 → 0.0.1-commit.381b1a9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dest/archiver.d.ts +12 -8
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +81 -120
- package/dest/errors.d.ts +6 -1
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +8 -0
- package/dest/factory.d.ts +3 -1
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +14 -11
- 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 +35 -32
- 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 +4 -7
- package/dest/l1/data_retrieval.d.ts.map +1 -1
- package/dest/l1/data_retrieval.js +12 -16
- 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 +25 -21
- package/dest/modules/data_source_base.d.ts.map +1 -1
- package/dest/modules/data_source_base.js +45 -120
- package/dest/modules/data_store_updater.d.ts +40 -21
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +114 -64
- package/dest/modules/instrumentation.d.ts +6 -4
- package/dest/modules/instrumentation.d.ts.map +1 -1
- package/dest/modules/instrumentation.js +26 -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 +46 -20
- package/dest/store/block_store.d.ts +49 -32
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +165 -54
- 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 +53 -25
- package/dest/store/kv_archiver_store.d.ts.map +1 -1
- package/dest/store/kv_archiver_store.js +50 -17
- 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 +4 -4
- package/dest/store/log_store.d.ts.map +1 -1
- package/dest/store/log_store.js +105 -47
- package/dest/store/message_store.js +1 -1
- package/dest/test/fake_l1_state.d.ts +16 -4
- 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 +39 -23
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +157 -112
- 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 +24 -10
- 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 +106 -149
- package/src/errors.ts +12 -0
- package/src/factory.ts +29 -12
- package/src/index.ts +1 -0
- package/src/l1/README.md +25 -68
- package/src/l1/bin/retrieve-calldata.ts +45 -33
- package/src/l1/calldata_retriever.ts +249 -379
- package/src/l1/data_retrieval.ts +10 -20
- package/src/l1/spire_proposer.ts +7 -15
- package/src/l1/validate_trace.ts +24 -6
- package/src/modules/data_source_base.ts +76 -166
- package/src/modules/data_store_updater.ts +130 -66
- package/src/modules/instrumentation.ts +26 -14
- package/src/modules/l1_synchronizer.ts +55 -26
- package/src/store/block_store.ts +216 -92
- package/src/store/contract_class_store.ts +11 -7
- package/src/store/kv_archiver_store.ts +88 -30
- package/src/store/l2_tips_cache.ts +89 -0
- package/src/store/log_store.ts +171 -55
- package/src/store/message_store.ts +1 -1
- package/src/test/fake_l1_state.ts +112 -23
- package/src/test/index.ts +3 -0
- package/src/test/mock_archiver.ts +3 -2
- package/src/test/mock_l2_block_source.ts +211 -129
- package/src/test/mock_structs.ts +45 -15
- package/src/test/noop_l1_archiver.ts +115 -0
package/src/store/block_store.ts
CHANGED
|
@@ -9,16 +9,18 @@ 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
|
-
|
|
16
|
-
L2BlockNew,
|
|
17
|
+
L2Block,
|
|
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 CheckpointData, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
23
|
+
import { type L1RollupConstants, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
22
24
|
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
23
25
|
import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
|
|
24
26
|
import {
|
|
@@ -27,6 +29,7 @@ import {
|
|
|
27
29
|
TxEffect,
|
|
28
30
|
TxHash,
|
|
29
31
|
TxReceipt,
|
|
32
|
+
TxStatus,
|
|
30
33
|
deserializeIndexedTxEffect,
|
|
31
34
|
serializeIndexedTxEffect,
|
|
32
35
|
} from '@aztec/stdlib/tx';
|
|
@@ -36,6 +39,7 @@ import {
|
|
|
36
39
|
BlockIndexNotSequentialError,
|
|
37
40
|
BlockNotFoundError,
|
|
38
41
|
BlockNumberNotSequentialError,
|
|
42
|
+
CannotOverwriteCheckpointedBlockError,
|
|
39
43
|
CheckpointNotFoundError,
|
|
40
44
|
CheckpointNumberNotConsistentError,
|
|
41
45
|
CheckpointNumberNotSequentialError,
|
|
@@ -58,22 +62,15 @@ type BlockStorage = {
|
|
|
58
62
|
type CheckpointStorage = {
|
|
59
63
|
header: Buffer;
|
|
60
64
|
archive: Buffer;
|
|
65
|
+
checkpointOutHash: Buffer;
|
|
61
66
|
checkpointNumber: number;
|
|
62
67
|
startBlock: number;
|
|
63
|
-
|
|
68
|
+
blockCount: number;
|
|
64
69
|
l1: Buffer;
|
|
65
70
|
attestations: Buffer[];
|
|
66
71
|
};
|
|
67
72
|
|
|
68
|
-
export type
|
|
69
|
-
checkpointNumber: CheckpointNumber;
|
|
70
|
-
header: CheckpointHeader;
|
|
71
|
-
archive: AppendOnlyTreeSnapshot;
|
|
72
|
-
startBlock: number;
|
|
73
|
-
numBlocks: number;
|
|
74
|
-
l1: L1PublishedData;
|
|
75
|
-
attestations: Buffer[];
|
|
76
|
-
};
|
|
73
|
+
export type RemoveCheckpointsResult = { blocksRemoved: L2Block[] | undefined };
|
|
77
74
|
|
|
78
75
|
/**
|
|
79
76
|
* LMDB-based block storage for the archiver.
|
|
@@ -85,6 +82,9 @@ export class BlockStore {
|
|
|
85
82
|
/** Map checkpoint number to checkpoint data */
|
|
86
83
|
#checkpoints: AztecAsyncMap<number, CheckpointStorage>;
|
|
87
84
|
|
|
85
|
+
/** Map slot number to checkpoint number, for looking up checkpoints by slot range. */
|
|
86
|
+
#slotToCheckpoint: AztecAsyncMap<number, number>;
|
|
87
|
+
|
|
88
88
|
/** Map block hash to list of tx hashes */
|
|
89
89
|
#blockTxs: AztecAsyncMap<string, Buffer>;
|
|
90
90
|
|
|
@@ -97,6 +97,9 @@ export class BlockStore {
|
|
|
97
97
|
/** Stores last proven checkpoint */
|
|
98
98
|
#lastProvenCheckpoint: AztecAsyncSingleton<number>;
|
|
99
99
|
|
|
100
|
+
/** Stores last finalized checkpoint (proven at or before the finalized L1 block) */
|
|
101
|
+
#lastFinalizedCheckpoint: AztecAsyncSingleton<number>;
|
|
102
|
+
|
|
100
103
|
/** Stores the pending chain validation status */
|
|
101
104
|
#pendingChainValidationStatus: AztecAsyncSingleton<Buffer>;
|
|
102
105
|
|
|
@@ -120,16 +123,37 @@ export class BlockStore {
|
|
|
120
123
|
this.#blockArchiveIndex = db.openMap('archiver_block_archive_index');
|
|
121
124
|
this.#lastSynchedL1Block = db.openSingleton('archiver_last_synched_l1_block');
|
|
122
125
|
this.#lastProvenCheckpoint = db.openSingleton('archiver_last_proven_l2_checkpoint');
|
|
126
|
+
this.#lastFinalizedCheckpoint = db.openSingleton('archiver_last_finalized_l2_checkpoint');
|
|
123
127
|
this.#pendingChainValidationStatus = db.openSingleton('archiver_pending_chain_validation_status');
|
|
124
128
|
this.#checkpoints = db.openMap('archiver_checkpoints');
|
|
129
|
+
this.#slotToCheckpoint = db.openMap('archiver_slot_to_checkpoint');
|
|
125
130
|
}
|
|
126
131
|
|
|
127
132
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
133
|
+
* Returns the finalized L2 block number. An L2 block is finalized when it was proven
|
|
134
|
+
* in an L1 block that has itself been finalized on Ethereum.
|
|
135
|
+
* @returns The finalized block number.
|
|
136
|
+
*/
|
|
137
|
+
async getFinalizedL2BlockNumber(): Promise<BlockNumber> {
|
|
138
|
+
const finalizedCheckpointNumber = await this.getFinalizedCheckpointNumber();
|
|
139
|
+
if (finalizedCheckpointNumber === INITIAL_CHECKPOINT_NUMBER - 1) {
|
|
140
|
+
return BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
141
|
+
}
|
|
142
|
+
const checkpointStorage = await this.#checkpoints.getAsync(finalizedCheckpointNumber);
|
|
143
|
+
if (!checkpointStorage) {
|
|
144
|
+
throw new CheckpointNotFoundError(finalizedCheckpointNumber);
|
|
145
|
+
}
|
|
146
|
+
return BlockNumber(checkpointStorage.startBlock + checkpointStorage.blockCount - 1);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Append new proposed blocks to the store's list. All blocks must be for the 'current' checkpoint.
|
|
151
|
+
* These are uncheckpointed blocks that have been proposed by the sequencer but not yet included in a checkpoint on L1.
|
|
152
|
+
* For checkpointed blocks (already published to L1), use addCheckpoints() instead.
|
|
153
|
+
* @param blocks - The proposed L2 blocks to be added to the store.
|
|
130
154
|
* @returns True if the operation is successful.
|
|
131
155
|
*/
|
|
132
|
-
async
|
|
156
|
+
async addProposedBlocks(blocks: L2Block[], opts: { force?: boolean } = {}): Promise<boolean> {
|
|
133
157
|
if (blocks.length === 0) {
|
|
134
158
|
return true;
|
|
135
159
|
}
|
|
@@ -145,6 +169,12 @@ export class BlockStore {
|
|
|
145
169
|
const previousBlockNumber = await this.getLatestBlockNumber();
|
|
146
170
|
const previousCheckpointNumber = await this.getLatestCheckpointNumber();
|
|
147
171
|
|
|
172
|
+
// Verify we're not overwriting checkpointed blocks
|
|
173
|
+
const lastCheckpointedBlockNumber = await this.getCheckpointedL2BlockNumber();
|
|
174
|
+
if (!opts.force && firstBlockNumber <= lastCheckpointedBlockNumber) {
|
|
175
|
+
throw new CannotOverwriteCheckpointedBlockError(firstBlockNumber, lastCheckpointedBlockNumber);
|
|
176
|
+
}
|
|
177
|
+
|
|
148
178
|
// Check that the first block number is the expected one
|
|
149
179
|
if (!opts.force && previousBlockNumber !== firstBlockNumber - 1) {
|
|
150
180
|
throw new InitialBlockNumberNotSequentialError(firstBlockNumber, previousBlockNumber);
|
|
@@ -182,7 +212,7 @@ export class BlockStore {
|
|
|
182
212
|
}
|
|
183
213
|
|
|
184
214
|
// Iterate over blocks array and insert them, checking that the block numbers and indexes are sequential. Also check they are for the correct checkpoint.
|
|
185
|
-
let previousBlock:
|
|
215
|
+
let previousBlock: L2Block | undefined = undefined;
|
|
186
216
|
for (const block of blocks) {
|
|
187
217
|
if (!opts.force && previousBlock) {
|
|
188
218
|
if (previousBlock.number + 1 !== block.number) {
|
|
@@ -241,11 +271,11 @@ export class BlockStore {
|
|
|
241
271
|
}
|
|
242
272
|
|
|
243
273
|
let previousBlockNumber: BlockNumber | undefined = undefined;
|
|
244
|
-
let previousBlock:
|
|
274
|
+
let previousBlock: L2Block | undefined = undefined;
|
|
245
275
|
|
|
246
276
|
// If we have a previous checkpoint then we need to get the previous block number
|
|
247
277
|
if (previousCheckpointData !== undefined) {
|
|
248
|
-
previousBlockNumber = BlockNumber(previousCheckpointData.startBlock + previousCheckpointData.
|
|
278
|
+
previousBlockNumber = BlockNumber(previousCheckpointData.startBlock + previousCheckpointData.blockCount - 1);
|
|
249
279
|
previousBlock = await this.getBlock(previousBlockNumber);
|
|
250
280
|
if (previousBlock === undefined) {
|
|
251
281
|
// We should be able to get the required previous block
|
|
@@ -309,12 +339,16 @@ export class BlockStore {
|
|
|
309
339
|
await this.#checkpoints.set(checkpoint.checkpoint.number, {
|
|
310
340
|
header: checkpoint.checkpoint.header.toBuffer(),
|
|
311
341
|
archive: checkpoint.checkpoint.archive.toBuffer(),
|
|
342
|
+
checkpointOutHash: checkpoint.checkpoint.getCheckpointOutHash().toBuffer(),
|
|
312
343
|
l1: checkpoint.l1.toBuffer(),
|
|
313
344
|
attestations: checkpoint.attestations.map(attestation => attestation.toBuffer()),
|
|
314
345
|
checkpointNumber: checkpoint.checkpoint.number,
|
|
315
346
|
startBlock: checkpoint.checkpoint.blocks[0].number,
|
|
316
|
-
|
|
347
|
+
blockCount: checkpoint.checkpoint.blocks.length,
|
|
317
348
|
});
|
|
349
|
+
|
|
350
|
+
// Update slot-to-checkpoint index
|
|
351
|
+
await this.#slotToCheckpoint.set(checkpoint.checkpoint.header.slotNumber, checkpoint.checkpoint.number);
|
|
318
352
|
}
|
|
319
353
|
|
|
320
354
|
await this.#lastSynchedL1Block.set(checkpoints[checkpoints.length - 1].l1.blockNumber);
|
|
@@ -322,8 +356,8 @@ export class BlockStore {
|
|
|
322
356
|
});
|
|
323
357
|
}
|
|
324
358
|
|
|
325
|
-
private async addBlockToDatabase(block:
|
|
326
|
-
const blockHash =
|
|
359
|
+
private async addBlockToDatabase(block: L2Block, checkpointNumber: number, indexWithinCheckpoint: number) {
|
|
360
|
+
const blockHash = await block.hash();
|
|
327
361
|
|
|
328
362
|
await this.#blocks.set(block.number, {
|
|
329
363
|
header: block.header.toBuffer(),
|
|
@@ -351,7 +385,7 @@ export class BlockStore {
|
|
|
351
385
|
}
|
|
352
386
|
|
|
353
387
|
/** Deletes a block and all associated data (tx effects, indices). */
|
|
354
|
-
private async deleteBlock(block:
|
|
388
|
+
private async deleteBlock(block: L2Block): Promise<void> {
|
|
355
389
|
// Delete the block from the main blocks map
|
|
356
390
|
await this.#blocks.delete(block.number);
|
|
357
391
|
|
|
@@ -368,51 +402,53 @@ export class BlockStore {
|
|
|
368
402
|
}
|
|
369
403
|
|
|
370
404
|
/**
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
* @param checkpointsToUnwind - The number of checkpoints we are to unwind
|
|
375
|
-
* @returns True if the operation is successful
|
|
405
|
+
* Removes all checkpoints with checkpoint number > checkpointNumber.
|
|
406
|
+
* Also removes ALL blocks (both checkpointed and uncheckpointed) after the last block of the given checkpoint.
|
|
407
|
+
* @param checkpointNumber - Remove all checkpoints strictly after this one.
|
|
376
408
|
*/
|
|
377
|
-
async
|
|
409
|
+
async removeCheckpointsAfter(checkpointNumber: CheckpointNumber): Promise<RemoveCheckpointsResult> {
|
|
378
410
|
return await this.db.transactionAsync(async () => {
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
411
|
+
const latestCheckpointNumber = await this.getLatestCheckpointNumber();
|
|
412
|
+
|
|
413
|
+
if (checkpointNumber >= latestCheckpointNumber) {
|
|
414
|
+
this.#log.warn(`No checkpoints to remove after ${checkpointNumber} (latest is ${latestCheckpointNumber})`);
|
|
415
|
+
return { blocksRemoved: undefined };
|
|
382
416
|
}
|
|
383
417
|
|
|
418
|
+
// If the proven checkpoint is beyond the target, update it
|
|
384
419
|
const proven = await this.getProvenCheckpointNumber();
|
|
385
|
-
if (
|
|
386
|
-
|
|
420
|
+
if (proven > checkpointNumber) {
|
|
421
|
+
this.#log.warn(`Updating proven checkpoint ${proven} to last valid checkpoint ${checkpointNumber}`);
|
|
422
|
+
await this.setProvenCheckpointNumber(checkpointNumber);
|
|
387
423
|
}
|
|
388
424
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
425
|
+
// Find the last block number to keep (last block of the given checkpoint, or 0 if no checkpoint)
|
|
426
|
+
let lastBlockToKeep: BlockNumber;
|
|
427
|
+
if (checkpointNumber <= 0) {
|
|
428
|
+
lastBlockToKeep = BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
429
|
+
} else {
|
|
430
|
+
const targetCheckpoint = await this.#checkpoints.getAsync(checkpointNumber);
|
|
431
|
+
if (!targetCheckpoint) {
|
|
432
|
+
throw new Error(`Target checkpoint ${checkpointNumber} not found in store`);
|
|
396
433
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
for (let blockNumber = checkpoint.startBlock; blockNumber <= maxBlock; blockNumber++) {
|
|
401
|
-
const block = await this.getBlock(BlockNumber(blockNumber));
|
|
434
|
+
lastBlockToKeep = BlockNumber(targetCheckpoint.startBlock + targetCheckpoint.blockCount - 1);
|
|
435
|
+
}
|
|
402
436
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
continue;
|
|
406
|
-
}
|
|
437
|
+
// Remove all blocks after lastBlockToKeep (both checkpointed and uncheckpointed)
|
|
438
|
+
const blocksRemoved = await this.removeBlocksAfter(lastBlockToKeep);
|
|
407
439
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
440
|
+
// Remove all checkpoints after the target
|
|
441
|
+
for (let c = latestCheckpointNumber; c > checkpointNumber; c = CheckpointNumber(c - 1)) {
|
|
442
|
+
const checkpointStorage = await this.#checkpoints.getAsync(c);
|
|
443
|
+
if (checkpointStorage) {
|
|
444
|
+
const slotNumber = CheckpointHeader.fromBuffer(checkpointStorage.header).slotNumber;
|
|
445
|
+
await this.#slotToCheckpoint.delete(slotNumber);
|
|
412
446
|
}
|
|
447
|
+
await this.#checkpoints.delete(c);
|
|
448
|
+
this.#log.debug(`Removed checkpoint ${c}`);
|
|
413
449
|
}
|
|
414
450
|
|
|
415
|
-
return
|
|
451
|
+
return { blocksRemoved };
|
|
416
452
|
});
|
|
417
453
|
}
|
|
418
454
|
|
|
@@ -436,20 +472,35 @@ export class BlockStore {
|
|
|
436
472
|
return checkpoints;
|
|
437
473
|
}
|
|
438
474
|
|
|
439
|
-
|
|
440
|
-
|
|
475
|
+
/** Returns checkpoint data for all checkpoints whose slot falls within the given range (inclusive). */
|
|
476
|
+
async getCheckpointDataForSlotRange(startSlot: SlotNumber, endSlot: SlotNumber): Promise<CheckpointData[]> {
|
|
477
|
+
const result: CheckpointData[] = [];
|
|
478
|
+
for await (const [, checkpointNumber] of this.#slotToCheckpoint.entriesAsync({
|
|
479
|
+
start: startSlot,
|
|
480
|
+
end: endSlot + 1,
|
|
481
|
+
})) {
|
|
482
|
+
const checkpointStorage = await this.#checkpoints.getAsync(checkpointNumber);
|
|
483
|
+
if (checkpointStorage) {
|
|
484
|
+
result.push(this.checkpointDataFromCheckpointStorage(checkpointStorage));
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return result;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
private checkpointDataFromCheckpointStorage(checkpointStorage: CheckpointStorage): CheckpointData {
|
|
491
|
+
return {
|
|
441
492
|
header: CheckpointHeader.fromBuffer(checkpointStorage.header),
|
|
442
493
|
archive: AppendOnlyTreeSnapshot.fromBuffer(checkpointStorage.archive),
|
|
494
|
+
checkpointOutHash: Fr.fromBuffer(checkpointStorage.checkpointOutHash),
|
|
443
495
|
checkpointNumber: CheckpointNumber(checkpointStorage.checkpointNumber),
|
|
444
|
-
startBlock: checkpointStorage.startBlock,
|
|
445
|
-
|
|
496
|
+
startBlock: BlockNumber(checkpointStorage.startBlock),
|
|
497
|
+
blockCount: checkpointStorage.blockCount,
|
|
446
498
|
l1: L1PublishedData.fromBuffer(checkpointStorage.l1),
|
|
447
|
-
attestations: checkpointStorage.attestations,
|
|
499
|
+
attestations: checkpointStorage.attestations.map(buf => CommitteeAttestation.fromBuffer(buf)),
|
|
448
500
|
};
|
|
449
|
-
return data;
|
|
450
501
|
}
|
|
451
502
|
|
|
452
|
-
async getBlocksForCheckpoint(checkpointNumber: CheckpointNumber): Promise<
|
|
503
|
+
async getBlocksForCheckpoint(checkpointNumber: CheckpointNumber): Promise<L2Block[] | undefined> {
|
|
453
504
|
const checkpoint = await this.#checkpoints.getAsync(checkpointNumber);
|
|
454
505
|
if (!checkpoint) {
|
|
455
506
|
return undefined;
|
|
@@ -458,7 +509,7 @@ export class BlockStore {
|
|
|
458
509
|
const blocksForCheckpoint = await toArray(
|
|
459
510
|
this.#blocks.entriesAsync({
|
|
460
511
|
start: checkpoint.startBlock,
|
|
461
|
-
end: checkpoint.startBlock + checkpoint.
|
|
512
|
+
end: checkpoint.startBlock + checkpoint.blockCount,
|
|
462
513
|
}),
|
|
463
514
|
);
|
|
464
515
|
|
|
@@ -472,8 +523,8 @@ export class BlockStore {
|
|
|
472
523
|
* @param slotNumber - The slot number to search for.
|
|
473
524
|
* @returns All blocks with the given slot number, in ascending block number order.
|
|
474
525
|
*/
|
|
475
|
-
async getBlocksForSlot(slotNumber: SlotNumber): Promise<
|
|
476
|
-
const blocks:
|
|
526
|
+
async getBlocksForSlot(slotNumber: SlotNumber): Promise<L2Block[]> {
|
|
527
|
+
const blocks: L2Block[] = [];
|
|
477
528
|
|
|
478
529
|
// Iterate backwards through all blocks and filter by slot number
|
|
479
530
|
// This is more efficient since we usually query for the most recent slot
|
|
@@ -493,12 +544,13 @@ export class BlockStore {
|
|
|
493
544
|
|
|
494
545
|
/**
|
|
495
546
|
* Removes all blocks with block number > blockNumber.
|
|
547
|
+
* Does not remove any associated checkpoints.
|
|
496
548
|
* @param blockNumber - The block number to remove after.
|
|
497
549
|
* @returns The removed blocks (for event emission).
|
|
498
550
|
*/
|
|
499
|
-
async
|
|
551
|
+
async removeBlocksAfter(blockNumber: BlockNumber): Promise<L2Block[]> {
|
|
500
552
|
return await this.db.transactionAsync(async () => {
|
|
501
|
-
const removedBlocks:
|
|
553
|
+
const removedBlocks: L2Block[] = [];
|
|
502
554
|
|
|
503
555
|
// Get the latest block number to determine the range
|
|
504
556
|
const latestBlockNumber = await this.getLatestBlockNumber();
|
|
@@ -530,7 +582,7 @@ export class BlockStore {
|
|
|
530
582
|
if (!checkpointStorage) {
|
|
531
583
|
throw new CheckpointNotFoundError(provenCheckpointNumber);
|
|
532
584
|
} else {
|
|
533
|
-
return BlockNumber(checkpointStorage.startBlock + checkpointStorage.
|
|
585
|
+
return BlockNumber(checkpointStorage.startBlock + checkpointStorage.blockCount - 1);
|
|
534
586
|
}
|
|
535
587
|
}
|
|
536
588
|
|
|
@@ -598,7 +650,7 @@ export class BlockStore {
|
|
|
598
650
|
}
|
|
599
651
|
}
|
|
600
652
|
|
|
601
|
-
async getCheckpointedBlockByHash(blockHash:
|
|
653
|
+
async getCheckpointedBlockByHash(blockHash: BlockHash): Promise<CheckpointedL2Block | undefined> {
|
|
602
654
|
const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
|
|
603
655
|
if (blockNumber === undefined) {
|
|
604
656
|
return undefined;
|
|
@@ -620,7 +672,7 @@ export class BlockStore {
|
|
|
620
672
|
* @param limit - The number of blocks to return.
|
|
621
673
|
* @returns The requested L2 blocks
|
|
622
674
|
*/
|
|
623
|
-
async *getBlocks(start: BlockNumber, limit: number): AsyncIterableIterator<
|
|
675
|
+
async *getBlocks(start: BlockNumber, limit: number): AsyncIterableIterator<L2Block> {
|
|
624
676
|
for await (const [blockNumber, blockStorage] of this.getBlockStorages(start, limit)) {
|
|
625
677
|
const block = await this.getBlockFromBlockStorage(blockNumber, blockStorage);
|
|
626
678
|
if (block) {
|
|
@@ -629,12 +681,38 @@ export class BlockStore {
|
|
|
629
681
|
}
|
|
630
682
|
}
|
|
631
683
|
|
|
684
|
+
/**
|
|
685
|
+
* Gets block metadata (without tx data) by block number.
|
|
686
|
+
* @param blockNumber - The number of the block to return.
|
|
687
|
+
* @returns The requested block data.
|
|
688
|
+
*/
|
|
689
|
+
async getBlockData(blockNumber: BlockNumber): Promise<BlockData | undefined> {
|
|
690
|
+
const blockStorage = await this.#blocks.getAsync(blockNumber);
|
|
691
|
+
if (!blockStorage || !blockStorage.header) {
|
|
692
|
+
return undefined;
|
|
693
|
+
}
|
|
694
|
+
return this.getBlockDataFromBlockStorage(blockStorage);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Gets block metadata (without tx data) by archive root.
|
|
699
|
+
* @param archive - The archive root of the block to return.
|
|
700
|
+
* @returns The requested block data.
|
|
701
|
+
*/
|
|
702
|
+
async getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
|
|
703
|
+
const blockNumber = await this.#blockArchiveIndex.getAsync(archive.toString());
|
|
704
|
+
if (blockNumber === undefined) {
|
|
705
|
+
return undefined;
|
|
706
|
+
}
|
|
707
|
+
return this.getBlockData(BlockNumber(blockNumber));
|
|
708
|
+
}
|
|
709
|
+
|
|
632
710
|
/**
|
|
633
711
|
* Gets an L2 block.
|
|
634
712
|
* @param blockNumber - The number of the block to return.
|
|
635
713
|
* @returns The requested L2 block.
|
|
636
714
|
*/
|
|
637
|
-
async getBlock(blockNumber: BlockNumber): Promise<
|
|
715
|
+
async getBlock(blockNumber: BlockNumber): Promise<L2Block | undefined> {
|
|
638
716
|
const blockStorage = await this.#blocks.getAsync(blockNumber);
|
|
639
717
|
if (!blockStorage || !blockStorage.header) {
|
|
640
718
|
return Promise.resolve(undefined);
|
|
@@ -647,7 +725,7 @@ export class BlockStore {
|
|
|
647
725
|
* @param blockHash - The hash of the block to return.
|
|
648
726
|
* @returns The requested L2 block.
|
|
649
727
|
*/
|
|
650
|
-
async getBlockByHash(blockHash:
|
|
728
|
+
async getBlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
|
|
651
729
|
const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
|
|
652
730
|
if (blockNumber === undefined) {
|
|
653
731
|
return undefined;
|
|
@@ -660,7 +738,7 @@ export class BlockStore {
|
|
|
660
738
|
* @param archive - The archive root of the block to return.
|
|
661
739
|
* @returns The requested L2 block.
|
|
662
740
|
*/
|
|
663
|
-
async getBlockByArchive(archive: Fr): Promise<
|
|
741
|
+
async getBlockByArchive(archive: Fr): Promise<L2Block | undefined> {
|
|
664
742
|
const blockNumber = await this.#blockArchiveIndex.getAsync(archive.toString());
|
|
665
743
|
if (blockNumber === undefined) {
|
|
666
744
|
return undefined;
|
|
@@ -673,7 +751,7 @@ export class BlockStore {
|
|
|
673
751
|
* @param blockHash - The hash of the block to return.
|
|
674
752
|
* @returns The requested block header.
|
|
675
753
|
*/
|
|
676
|
-
async getBlockHeaderByHash(blockHash:
|
|
754
|
+
async getBlockHeaderByHash(blockHash: BlockHash): Promise<BlockHeader | undefined> {
|
|
677
755
|
const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
|
|
678
756
|
if (blockNumber === undefined) {
|
|
679
757
|
return undefined;
|
|
@@ -733,15 +811,24 @@ export class BlockStore {
|
|
|
733
811
|
}
|
|
734
812
|
}
|
|
735
813
|
|
|
814
|
+
private getBlockDataFromBlockStorage(blockStorage: BlockStorage): BlockData {
|
|
815
|
+
return {
|
|
816
|
+
header: BlockHeader.fromBuffer(blockStorage.header),
|
|
817
|
+
archive: AppendOnlyTreeSnapshot.fromBuffer(blockStorage.archive),
|
|
818
|
+
blockHash: Fr.fromBuffer(blockStorage.blockHash),
|
|
819
|
+
checkpointNumber: CheckpointNumber(blockStorage.checkpointNumber),
|
|
820
|
+
indexWithinCheckpoint: IndexWithinCheckpoint(blockStorage.indexWithinCheckpoint),
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
|
|
736
824
|
private async getBlockFromBlockStorage(
|
|
737
825
|
blockNumber: number,
|
|
738
826
|
blockStorage: BlockStorage,
|
|
739
|
-
): Promise<
|
|
740
|
-
const header =
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
const blockHashString = bufferToHex(blockHash);
|
|
827
|
+
): Promise<L2Block | undefined> {
|
|
828
|
+
const { header, archive, blockHash, checkpointNumber, indexWithinCheckpoint } =
|
|
829
|
+
this.getBlockDataFromBlockStorage(blockStorage);
|
|
830
|
+
header.setHash(blockHash);
|
|
831
|
+
const blockHashString = bufferToHex(blockStorage.blockHash);
|
|
745
832
|
const blockTxsBuffer = await this.#blockTxs.getAsync(blockHashString);
|
|
746
833
|
if (blockTxsBuffer === undefined) {
|
|
747
834
|
this.#log.warn(`Could not find body for block ${header.globalVariables.blockNumber} ${blockHash}`);
|
|
@@ -760,13 +847,7 @@ export class BlockStore {
|
|
|
760
847
|
txEffects.push(deserializeIndexedTxEffect(txEffect).data);
|
|
761
848
|
}
|
|
762
849
|
const body = new Body(txEffects);
|
|
763
|
-
const block = new
|
|
764
|
-
archive,
|
|
765
|
-
header,
|
|
766
|
-
body,
|
|
767
|
-
CheckpointNumber(blockStorage.checkpointNumber!),
|
|
768
|
-
IndexWithinCheckpoint(blockStorage.indexWithinCheckpoint),
|
|
769
|
-
);
|
|
850
|
+
const block = new L2Block(archive, header, body, checkpointNumber, indexWithinCheckpoint);
|
|
770
851
|
|
|
771
852
|
if (block.number !== blockNumber) {
|
|
772
853
|
throw new Error(
|
|
@@ -796,19 +877,48 @@ export class BlockStore {
|
|
|
796
877
|
* @param txHash - The hash of a tx we try to get the receipt for.
|
|
797
878
|
* @returns The requested tx receipt (or undefined if not found).
|
|
798
879
|
*/
|
|
799
|
-
async getSettledTxReceipt(
|
|
880
|
+
async getSettledTxReceipt(
|
|
881
|
+
txHash: TxHash,
|
|
882
|
+
l1Constants?: Pick<L1RollupConstants, 'epochDuration'>,
|
|
883
|
+
): Promise<TxReceipt | undefined> {
|
|
800
884
|
const txEffect = await this.getTxEffect(txHash);
|
|
801
885
|
if (!txEffect) {
|
|
802
886
|
return undefined;
|
|
803
887
|
}
|
|
804
888
|
|
|
889
|
+
const blockNumber = BlockNumber(txEffect.l2BlockNumber);
|
|
890
|
+
|
|
891
|
+
// Use existing archiver methods to determine finalization level
|
|
892
|
+
const [provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber, blockData] = await Promise.all([
|
|
893
|
+
this.getProvenBlockNumber(),
|
|
894
|
+
this.getCheckpointedL2BlockNumber(),
|
|
895
|
+
this.getFinalizedL2BlockNumber(),
|
|
896
|
+
this.getBlockData(blockNumber),
|
|
897
|
+
]);
|
|
898
|
+
|
|
899
|
+
let status: TxStatus;
|
|
900
|
+
if (blockNumber <= finalizedBlockNumber) {
|
|
901
|
+
status = TxStatus.FINALIZED;
|
|
902
|
+
} else if (blockNumber <= provenBlockNumber) {
|
|
903
|
+
status = TxStatus.PROVEN;
|
|
904
|
+
} else if (blockNumber <= checkpointedBlockNumber) {
|
|
905
|
+
status = TxStatus.CHECKPOINTED;
|
|
906
|
+
} else {
|
|
907
|
+
status = TxStatus.PROPOSED;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
const epochNumber =
|
|
911
|
+
blockData && l1Constants ? getEpochAtSlot(blockData.header.globalVariables.slotNumber, l1Constants) : undefined;
|
|
912
|
+
|
|
805
913
|
return new TxReceipt(
|
|
806
914
|
txHash,
|
|
807
|
-
|
|
808
|
-
|
|
915
|
+
status,
|
|
916
|
+
TxReceipt.executionResultFromRevertCode(txEffect.data.revertCode),
|
|
917
|
+
undefined,
|
|
809
918
|
txEffect.data.transactionFee.toBigInt(),
|
|
810
919
|
txEffect.l2BlockHash,
|
|
811
|
-
|
|
920
|
+
blockNumber,
|
|
921
|
+
epochNumber,
|
|
812
922
|
);
|
|
813
923
|
}
|
|
814
924
|
|
|
@@ -845,7 +955,7 @@ export class BlockStore {
|
|
|
845
955
|
if (!checkpoint) {
|
|
846
956
|
return BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
847
957
|
}
|
|
848
|
-
return BlockNumber(checkpoint.startBlock + checkpoint.
|
|
958
|
+
return BlockNumber(checkpoint.startBlock + checkpoint.blockCount - 1);
|
|
849
959
|
}
|
|
850
960
|
|
|
851
961
|
async getLatestL2BlockNumber(): Promise<BlockNumber> {
|
|
@@ -880,6 +990,20 @@ export class BlockStore {
|
|
|
880
990
|
return result;
|
|
881
991
|
}
|
|
882
992
|
|
|
993
|
+
async getFinalizedCheckpointNumber(): Promise<CheckpointNumber> {
|
|
994
|
+
const [latestCheckpointNumber, finalizedCheckpointNumber] = await Promise.all([
|
|
995
|
+
this.getLatestCheckpointNumber(),
|
|
996
|
+
this.#lastFinalizedCheckpoint.getAsync(),
|
|
997
|
+
]);
|
|
998
|
+
return (finalizedCheckpointNumber ?? 0) > latestCheckpointNumber
|
|
999
|
+
? latestCheckpointNumber
|
|
1000
|
+
: CheckpointNumber(finalizedCheckpointNumber ?? 0);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
setFinalizedCheckpointNumber(checkpointNumber: CheckpointNumber) {
|
|
1004
|
+
return this.#lastFinalizedCheckpoint.set(checkpointNumber);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
883
1007
|
#computeBlockRange(start: BlockNumber, limit: number): Required<Pick<Range<number>, 'start' | 'limit'>> {
|
|
884
1008
|
if (limit < 1) {
|
|
885
1009
|
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
|
|