@aztec/archiver 0.0.1-commit.6d3c34e → 0.0.1-commit.7035c9bd6
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 +156 -22
- package/dest/archiver.d.ts +138 -0
- package/dest/archiver.d.ts.map +1 -0
- package/dest/archiver.js +732 -0
- package/dest/config.d.ts +30 -0
- package/dest/config.d.ts.map +1 -0
- package/dest/{archiver/config.js → config.js} +11 -1
- package/dest/errors.d.ts +53 -0
- package/dest/errors.d.ts.map +1 -0
- package/dest/errors.js +75 -0
- package/dest/factory.d.ts +8 -7
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +90 -8
- package/dest/index.d.ts +11 -4
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +9 -3
- package/dest/interfaces.d.ts +9 -0
- package/dest/interfaces.d.ts.map +1 -0
- package/dest/interfaces.js +3 -0
- package/dest/{archiver/l1 → l1}/bin/retrieve-calldata.d.ts +1 -1
- package/dest/l1/bin/retrieve-calldata.d.ts.map +1 -0
- package/dest/{archiver/l1 → l1}/bin/retrieve-calldata.js +35 -32
- package/dest/l1/calldata_retriever.d.ts +135 -0
- package/dest/l1/calldata_retriever.d.ts.map +1 -0
- package/dest/l1/calldata_retriever.js +402 -0
- package/dest/l1/data_retrieval.d.ts +88 -0
- package/dest/l1/data_retrieval.d.ts.map +1 -0
- package/dest/{archiver/l1 → l1}/data_retrieval.js +54 -71
- package/dest/{archiver/l1 → l1}/debug_tx.d.ts +1 -1
- package/dest/l1/debug_tx.d.ts.map +1 -0
- package/dest/{archiver/l1 → l1}/spire_proposer.d.ts +5 -5
- package/dest/l1/spire_proposer.d.ts.map +1 -0
- package/dest/{archiver/l1 → l1}/spire_proposer.js +9 -17
- package/dest/{archiver/l1 → l1}/trace_tx.d.ts +1 -1
- package/dest/l1/trace_tx.d.ts.map +1 -0
- package/dest/l1/types.d.ts +12 -0
- package/dest/l1/types.d.ts.map +1 -0
- package/dest/{archiver/l1 → l1}/validate_trace.d.ts +6 -3
- package/dest/l1/validate_trace.d.ts.map +1 -0
- package/dest/{archiver/l1 → l1}/validate_trace.js +13 -9
- package/dest/modules/data_source_base.d.ts +89 -0
- package/dest/modules/data_source_base.d.ts.map +1 -0
- package/dest/modules/data_source_base.js +216 -0
- package/dest/modules/data_store_updater.d.ts +88 -0
- package/dest/modules/data_store_updater.d.ts.map +1 -0
- package/dest/modules/data_store_updater.js +342 -0
- package/dest/modules/instrumentation.d.ts +50 -0
- package/dest/modules/instrumentation.d.ts.map +1 -0
- package/dest/{archiver → modules}/instrumentation.js +36 -12
- package/dest/modules/l1_synchronizer.d.ts +72 -0
- package/dest/modules/l1_synchronizer.d.ts.map +1 -0
- package/dest/modules/l1_synchronizer.js +1147 -0
- package/dest/{archiver → modules}/validation.d.ts +1 -1
- package/dest/modules/validation.d.ts.map +1 -0
- package/dest/{archiver → modules}/validation.js +6 -0
- package/dest/store/block_store.d.ts +195 -0
- package/dest/store/block_store.d.ts.map +1 -0
- package/dest/{archiver/kv_archiver_store → store}/block_store.js +248 -101
- package/dest/store/contract_class_store.d.ts +18 -0
- package/dest/store/contract_class_store.d.ts.map +1 -0
- package/dest/{archiver/kv_archiver_store → store}/contract_class_store.js +12 -8
- package/dest/store/contract_instance_store.d.ts +24 -0
- package/dest/store/contract_instance_store.d.ts.map +1 -0
- package/dest/{archiver/kv_archiver_store → store}/contract_instance_store.js +1 -1
- package/dest/store/kv_archiver_store.d.ts +367 -0
- package/dest/store/kv_archiver_store.d.ts.map +1 -0
- package/dest/store/kv_archiver_store.js +481 -0
- 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 +57 -0
- package/dest/store/log_store.d.ts.map +1 -0
- package/dest/store/log_store.js +533 -0
- package/dest/store/message_store.d.ts +44 -0
- package/dest/store/message_store.d.ts.map +1 -0
- package/dest/{archiver/kv_archiver_store → store}/message_store.js +14 -1
- package/dest/{archiver/structs → structs}/data_retrieval.d.ts +1 -1
- package/dest/structs/data_retrieval.d.ts.map +1 -0
- package/dest/structs/inbox_message.d.ts +15 -0
- package/dest/structs/inbox_message.d.ts.map +1 -0
- package/dest/{archiver/structs → structs}/published.d.ts +1 -1
- package/dest/structs/published.d.ts.map +1 -0
- package/dest/test/fake_l1_state.d.ts +202 -0
- package/dest/test/fake_l1_state.d.ts.map +1 -0
- package/dest/test/fake_l1_state.js +455 -0
- package/dest/test/index.d.ts +2 -1
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +4 -1
- package/dest/test/mock_archiver.d.ts +2 -2
- package/dest/test/mock_archiver.d.ts.map +1 -1
- package/dest/test/mock_archiver.js +3 -3
- package/dest/test/mock_l2_block_source.d.ts +38 -19
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +183 -77
- package/dest/test/mock_structs.d.ts +81 -3
- package/dest/test/mock_structs.d.ts.map +1 -1
- package/dest/test/mock_structs.js +152 -7
- 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 +16 -17
- package/src/archiver.ts +486 -0
- package/src/{archiver/config.ts → config.ts} +19 -1
- package/src/{archiver/errors.ts → errors.ts} +52 -24
- package/src/factory.ts +141 -10
- package/src/index.ts +11 -3
- package/src/interfaces.ts +9 -0
- package/src/l1/README.md +55 -0
- package/src/{archiver/l1 → l1}/bin/retrieve-calldata.ts +45 -33
- package/src/l1/calldata_retriever.ts +511 -0
- package/src/{archiver/l1 → l1}/data_retrieval.ts +75 -94
- package/src/{archiver/l1 → l1}/spire_proposer.ts +7 -15
- package/src/{archiver/l1 → l1}/validate_trace.ts +24 -6
- package/src/modules/data_source_base.ts +333 -0
- package/src/modules/data_store_updater.ts +464 -0
- package/src/{archiver → modules}/instrumentation.ts +46 -14
- package/src/modules/l1_synchronizer.ts +967 -0
- package/src/{archiver → modules}/validation.ts +5 -0
- package/src/{archiver/kv_archiver_store → store}/block_store.ts +309 -141
- package/src/{archiver/kv_archiver_store → store}/contract_class_store.ts +12 -8
- package/src/{archiver/kv_archiver_store → store}/contract_instance_store.ts +1 -1
- package/src/{archiver/kv_archiver_store → store}/kv_archiver_store.ts +294 -39
- package/src/store/l2_tips_cache.ts +89 -0
- package/src/store/log_store.ts +736 -0
- package/src/{archiver/kv_archiver_store → store}/message_store.ts +20 -1
- package/src/test/fake_l1_state.ts +698 -0
- package/src/test/index.ts +4 -0
- package/src/test/mock_archiver.ts +4 -3
- package/src/test/mock_l2_block_source.ts +233 -93
- package/src/test/mock_structs.ts +283 -8
- package/src/test/noop_l1_archiver.ts +115 -0
- package/dest/archiver/archiver.d.ts +0 -307
- package/dest/archiver/archiver.d.ts.map +0 -1
- package/dest/archiver/archiver.js +0 -2102
- package/dest/archiver/archiver_store.d.ts +0 -315
- package/dest/archiver/archiver_store.d.ts.map +0 -1
- package/dest/archiver/archiver_store.js +0 -4
- package/dest/archiver/archiver_store_test_suite.d.ts +0 -8
- package/dest/archiver/archiver_store_test_suite.d.ts.map +0 -1
- package/dest/archiver/archiver_store_test_suite.js +0 -2770
- package/dest/archiver/config.d.ts +0 -22
- package/dest/archiver/config.d.ts.map +0 -1
- package/dest/archiver/errors.d.ts +0 -36
- package/dest/archiver/errors.d.ts.map +0 -1
- package/dest/archiver/errors.js +0 -54
- package/dest/archiver/index.d.ts +0 -7
- package/dest/archiver/index.d.ts.map +0 -1
- package/dest/archiver/index.js +0 -4
- package/dest/archiver/instrumentation.d.ts +0 -37
- package/dest/archiver/instrumentation.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/block_store.d.ts +0 -164
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts +0 -18
- package/dest/archiver/kv_archiver_store/contract_class_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts +0 -24
- package/dest/archiver/kv_archiver_store/contract_instance_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +0 -159
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +0 -316
- package/dest/archiver/kv_archiver_store/log_store.d.ts +0 -45
- package/dest/archiver/kv_archiver_store/log_store.d.ts.map +0 -1
- package/dest/archiver/kv_archiver_store/log_store.js +0 -401
- package/dest/archiver/kv_archiver_store/message_store.d.ts +0 -40
- package/dest/archiver/kv_archiver_store/message_store.d.ts.map +0 -1
- package/dest/archiver/l1/bin/retrieve-calldata.d.ts.map +0 -1
- package/dest/archiver/l1/calldata_retriever.d.ts +0 -112
- package/dest/archiver/l1/calldata_retriever.d.ts.map +0 -1
- package/dest/archiver/l1/calldata_retriever.js +0 -471
- package/dest/archiver/l1/data_retrieval.d.ts +0 -90
- package/dest/archiver/l1/data_retrieval.d.ts.map +0 -1
- package/dest/archiver/l1/debug_tx.d.ts.map +0 -1
- package/dest/archiver/l1/spire_proposer.d.ts.map +0 -1
- package/dest/archiver/l1/trace_tx.d.ts.map +0 -1
- package/dest/archiver/l1/types.d.ts +0 -12
- package/dest/archiver/l1/types.d.ts.map +0 -1
- package/dest/archiver/l1/validate_trace.d.ts.map +0 -1
- package/dest/archiver/structs/data_retrieval.d.ts.map +0 -1
- package/dest/archiver/structs/inbox_message.d.ts +0 -15
- package/dest/archiver/structs/inbox_message.d.ts.map +0 -1
- package/dest/archiver/structs/published.d.ts.map +0 -1
- package/dest/archiver/validation.d.ts.map +0 -1
- package/dest/rpc/index.d.ts +0 -9
- package/dest/rpc/index.d.ts.map +0 -1
- package/dest/rpc/index.js +0 -15
- package/src/archiver/archiver.ts +0 -2265
- package/src/archiver/archiver_store.ts +0 -380
- package/src/archiver/archiver_store_test_suite.ts +0 -2842
- package/src/archiver/index.ts +0 -6
- package/src/archiver/kv_archiver_store/log_store.ts +0 -516
- package/src/archiver/l1/README.md +0 -98
- package/src/archiver/l1/calldata_retriever.ts +0 -641
- package/src/rpc/index.ts +0 -16
- /package/dest/{archiver/l1 → l1}/debug_tx.js +0 -0
- /package/dest/{archiver/l1 → l1}/trace_tx.js +0 -0
- /package/dest/{archiver/l1 → l1}/types.js +0 -0
- /package/dest/{archiver/structs → structs}/data_retrieval.js +0 -0
- /package/dest/{archiver/structs → structs}/inbox_message.js +0 -0
- /package/dest/{archiver/structs → structs}/published.js +0 -0
- /package/src/{archiver/l1 → l1}/debug_tx.ts +0 -0
- /package/src/{archiver/l1 → l1}/trace_tx.ts +0 -0
- /package/src/{archiver/l1 → l1}/types.ts +0 -0
- /package/src/{archiver/structs → structs}/data_retrieval.ts +0 -0
- /package/src/{archiver/structs → structs}/inbox_message.ts +0 -0
- /package/src/{archiver/structs → structs}/published.ts +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { INITIAL_CHECKPOINT_NUMBER, INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
|
-
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
2
|
+
import { BlockNumber, CheckpointNumber, IndexWithinCheckpoint, SlotNumber } from '@aztec/foundation/branded-types';
|
|
3
3
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
4
|
import { toArray } from '@aztec/foundation/iterable';
|
|
5
5
|
import { createLogger } from '@aztec/foundation/log';
|
|
@@ -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,19 +29,20 @@ import {
|
|
|
27
29
|
TxEffect,
|
|
28
30
|
TxHash,
|
|
29
31
|
TxReceipt,
|
|
32
|
+
TxStatus,
|
|
30
33
|
deserializeIndexedTxEffect,
|
|
31
34
|
serializeIndexedTxEffect,
|
|
32
35
|
} from '@aztec/stdlib/tx';
|
|
33
36
|
|
|
34
37
|
import {
|
|
38
|
+
BlockAlreadyCheckpointedError,
|
|
35
39
|
BlockArchiveNotConsistentError,
|
|
36
40
|
BlockIndexNotSequentialError,
|
|
37
41
|
BlockNotFoundError,
|
|
38
42
|
BlockNumberNotSequentialError,
|
|
43
|
+
CannotOverwriteCheckpointedBlockError,
|
|
39
44
|
CheckpointNotFoundError,
|
|
40
|
-
CheckpointNumberNotConsistentError,
|
|
41
45
|
CheckpointNumberNotSequentialError,
|
|
42
|
-
InitialBlockNumberNotSequentialError,
|
|
43
46
|
InitialCheckpointNumberNotSequentialError,
|
|
44
47
|
} from '../errors.js';
|
|
45
48
|
|
|
@@ -58,25 +61,18 @@ type BlockStorage = {
|
|
|
58
61
|
type CheckpointStorage = {
|
|
59
62
|
header: Buffer;
|
|
60
63
|
archive: Buffer;
|
|
64
|
+
checkpointOutHash: Buffer;
|
|
61
65
|
checkpointNumber: number;
|
|
62
66
|
startBlock: number;
|
|
63
|
-
|
|
67
|
+
blockCount: number;
|
|
64
68
|
l1: Buffer;
|
|
65
69
|
attestations: Buffer[];
|
|
66
70
|
};
|
|
67
71
|
|
|
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
|
-
};
|
|
72
|
+
export type RemoveCheckpointsResult = { blocksRemoved: L2Block[] | undefined };
|
|
77
73
|
|
|
78
74
|
/**
|
|
79
|
-
* LMDB
|
|
75
|
+
* LMDB-based block storage for the archiver.
|
|
80
76
|
*/
|
|
81
77
|
export class BlockStore {
|
|
82
78
|
/** Map block number to block data */
|
|
@@ -85,6 +81,9 @@ export class BlockStore {
|
|
|
85
81
|
/** Map checkpoint number to checkpoint data */
|
|
86
82
|
#checkpoints: AztecAsyncMap<number, CheckpointStorage>;
|
|
87
83
|
|
|
84
|
+
/** Map slot number to checkpoint number, for looking up checkpoints by slot range. */
|
|
85
|
+
#slotToCheckpoint: AztecAsyncMap<number, number>;
|
|
86
|
+
|
|
88
87
|
/** Map block hash to list of tx hashes */
|
|
89
88
|
#blockTxs: AztecAsyncMap<string, Buffer>;
|
|
90
89
|
|
|
@@ -97,6 +96,9 @@ export class BlockStore {
|
|
|
97
96
|
/** Stores last proven checkpoint */
|
|
98
97
|
#lastProvenCheckpoint: AztecAsyncSingleton<number>;
|
|
99
98
|
|
|
99
|
+
/** Stores last finalized checkpoint (proven at or before the finalized L1 block) */
|
|
100
|
+
#lastFinalizedCheckpoint: AztecAsyncSingleton<number>;
|
|
101
|
+
|
|
100
102
|
/** Stores the pending chain validation status */
|
|
101
103
|
#pendingChainValidationStatus: AztecAsyncSingleton<Buffer>;
|
|
102
104
|
|
|
@@ -120,92 +122,95 @@ export class BlockStore {
|
|
|
120
122
|
this.#blockArchiveIndex = db.openMap('archiver_block_archive_index');
|
|
121
123
|
this.#lastSynchedL1Block = db.openSingleton('archiver_last_synched_l1_block');
|
|
122
124
|
this.#lastProvenCheckpoint = db.openSingleton('archiver_last_proven_l2_checkpoint');
|
|
125
|
+
this.#lastFinalizedCheckpoint = db.openSingleton('archiver_last_finalized_l2_checkpoint');
|
|
123
126
|
this.#pendingChainValidationStatus = db.openSingleton('archiver_pending_chain_validation_status');
|
|
124
127
|
this.#checkpoints = db.openMap('archiver_checkpoints');
|
|
128
|
+
this.#slotToCheckpoint = db.openMap('archiver_slot_to_checkpoint');
|
|
125
129
|
}
|
|
126
130
|
|
|
127
131
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* @returns
|
|
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.
|
|
134
|
+
* @returns The finalized block number.
|
|
131
135
|
*/
|
|
132
|
-
async
|
|
133
|
-
|
|
134
|
-
|
|
136
|
+
async getFinalizedL2BlockNumber(): Promise<BlockNumber> {
|
|
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);
|
|
135
144
|
}
|
|
145
|
+
return BlockNumber(checkpointStorage.startBlock + checkpointStorage.blockCount - 1);
|
|
146
|
+
}
|
|
136
147
|
|
|
148
|
+
/**
|
|
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.
|
|
151
|
+
* For checkpointed blocks (already published to L1), use addCheckpoints() instead.
|
|
152
|
+
* @param block - The proposed L2 block to be added to the store.
|
|
153
|
+
* @returns True if the operation is successful.
|
|
154
|
+
*/
|
|
155
|
+
async addProposedBlock(block: L2Block, opts: { force?: boolean } = {}): Promise<boolean> {
|
|
137
156
|
return await this.db.transactionAsync(async () => {
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
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;
|
|
143
161
|
|
|
144
162
|
// Extract the latest block and checkpoint numbers
|
|
145
163
|
const previousBlockNumber = await this.getLatestBlockNumber();
|
|
146
164
|
const previousCheckpointNumber = await this.getLatestCheckpointNumber();
|
|
147
165
|
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
|
|
166
|
+
// Verify we're not overwriting checkpointed blocks
|
|
167
|
+
const lastCheckpointedBlockNumber = await this.getCheckpointedL2BlockNumber();
|
|
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);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Check that the block number is the expected one
|
|
178
|
+
if (!opts.force && previousBlockNumber !== blockNumber - 1) {
|
|
179
|
+
throw new BlockNumberNotSequentialError(blockNumber, previousBlockNumber);
|
|
151
180
|
}
|
|
152
181
|
|
|
153
182
|
// The same check as above but for checkpoints
|
|
154
|
-
if (!opts.force && previousCheckpointNumber !==
|
|
155
|
-
throw new
|
|
183
|
+
if (!opts.force && previousCheckpointNumber !== blockCheckpointNumber - 1) {
|
|
184
|
+
throw new CheckpointNumberNotSequentialError(blockCheckpointNumber, previousCheckpointNumber);
|
|
156
185
|
}
|
|
157
186
|
|
|
158
187
|
// Extract the previous block if there is one and see if it is for the same checkpoint or not
|
|
159
188
|
const previousBlockResult = await this.getBlock(previousBlockNumber);
|
|
160
189
|
|
|
161
|
-
let
|
|
190
|
+
let expectedBlockIndex = 0;
|
|
162
191
|
let previousBlockIndex: number | undefined = undefined;
|
|
163
192
|
if (previousBlockResult !== undefined) {
|
|
164
|
-
if (previousBlockResult.checkpointNumber ===
|
|
193
|
+
if (previousBlockResult.checkpointNumber === blockCheckpointNumber) {
|
|
165
194
|
// The previous block is for the same checkpoint, therefore our index should follow it
|
|
166
195
|
previousBlockIndex = previousBlockResult.indexWithinCheckpoint;
|
|
167
|
-
|
|
196
|
+
expectedBlockIndex = previousBlockIndex + 1;
|
|
168
197
|
}
|
|
169
|
-
if (!previousBlockResult.archive.root.equals(
|
|
198
|
+
if (!previousBlockResult.archive.root.equals(blockLastArchive)) {
|
|
170
199
|
throw new BlockArchiveNotConsistentError(
|
|
171
|
-
|
|
200
|
+
blockNumber,
|
|
172
201
|
previousBlockResult.number,
|
|
173
|
-
|
|
202
|
+
blockLastArchive,
|
|
174
203
|
previousBlockResult.archive.root,
|
|
175
204
|
);
|
|
176
205
|
}
|
|
177
206
|
}
|
|
178
207
|
|
|
179
|
-
// Now check that the
|
|
180
|
-
if (!opts.force &&
|
|
181
|
-
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);
|
|
182
211
|
}
|
|
183
212
|
|
|
184
|
-
|
|
185
|
-
let previousBlock: L2BlockNew | undefined = undefined;
|
|
186
|
-
for (const block of blocks) {
|
|
187
|
-
if (!opts.force && previousBlock) {
|
|
188
|
-
if (previousBlock.number + 1 !== block.number) {
|
|
189
|
-
throw new BlockNumberNotSequentialError(block.number, previousBlock.number);
|
|
190
|
-
}
|
|
191
|
-
if (previousBlock.indexWithinCheckpoint + 1 !== block.indexWithinCheckpoint) {
|
|
192
|
-
throw new BlockIndexNotSequentialError(block.indexWithinCheckpoint, previousBlock.indexWithinCheckpoint);
|
|
193
|
-
}
|
|
194
|
-
if (!previousBlock.archive.root.equals(block.header.lastArchive.root)) {
|
|
195
|
-
throw new BlockArchiveNotConsistentError(
|
|
196
|
-
block.number,
|
|
197
|
-
previousBlock.number,
|
|
198
|
-
block.header.lastArchive.root,
|
|
199
|
-
previousBlock.archive.root,
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
if (!opts.force && firstBlockCheckpointNumber !== block.checkpointNumber) {
|
|
204
|
-
throw new CheckpointNumberNotConsistentError(block.checkpointNumber, firstBlockCheckpointNumber);
|
|
205
|
-
}
|
|
206
|
-
previousBlock = block;
|
|
207
|
-
await this.addBlockToDatabase(block, block.checkpointNumber, block.indexWithinCheckpoint);
|
|
208
|
-
}
|
|
213
|
+
await this.addBlockToDatabase(block, block.checkpointNumber, block.indexWithinCheckpoint);
|
|
209
214
|
|
|
210
215
|
return true;
|
|
211
216
|
});
|
|
@@ -241,11 +246,11 @@ export class BlockStore {
|
|
|
241
246
|
}
|
|
242
247
|
|
|
243
248
|
let previousBlockNumber: BlockNumber | undefined = undefined;
|
|
244
|
-
let previousBlock:
|
|
249
|
+
let previousBlock: L2Block | undefined = undefined;
|
|
245
250
|
|
|
246
251
|
// If we have a previous checkpoint then we need to get the previous block number
|
|
247
252
|
if (previousCheckpointData !== undefined) {
|
|
248
|
-
previousBlockNumber = BlockNumber(previousCheckpointData.startBlock + previousCheckpointData.
|
|
253
|
+
previousBlockNumber = BlockNumber(previousCheckpointData.startBlock + previousCheckpointData.blockCount - 1);
|
|
249
254
|
previousBlock = await this.getBlock(previousBlockNumber);
|
|
250
255
|
if (previousBlock === undefined) {
|
|
251
256
|
// We should be able to get the required previous block
|
|
@@ -309,12 +314,16 @@ export class BlockStore {
|
|
|
309
314
|
await this.#checkpoints.set(checkpoint.checkpoint.number, {
|
|
310
315
|
header: checkpoint.checkpoint.header.toBuffer(),
|
|
311
316
|
archive: checkpoint.checkpoint.archive.toBuffer(),
|
|
317
|
+
checkpointOutHash: checkpoint.checkpoint.getCheckpointOutHash().toBuffer(),
|
|
312
318
|
l1: checkpoint.l1.toBuffer(),
|
|
313
319
|
attestations: checkpoint.attestations.map(attestation => attestation.toBuffer()),
|
|
314
320
|
checkpointNumber: checkpoint.checkpoint.number,
|
|
315
321
|
startBlock: checkpoint.checkpoint.blocks[0].number,
|
|
316
|
-
|
|
322
|
+
blockCount: checkpoint.checkpoint.blocks.length,
|
|
317
323
|
});
|
|
324
|
+
|
|
325
|
+
// Update slot-to-checkpoint index
|
|
326
|
+
await this.#slotToCheckpoint.set(checkpoint.checkpoint.header.slotNumber, checkpoint.checkpoint.number);
|
|
318
327
|
}
|
|
319
328
|
|
|
320
329
|
await this.#lastSynchedL1Block.set(checkpoints[checkpoints.length - 1].l1.blockNumber);
|
|
@@ -322,8 +331,8 @@ export class BlockStore {
|
|
|
322
331
|
});
|
|
323
332
|
}
|
|
324
333
|
|
|
325
|
-
private async addBlockToDatabase(block:
|
|
326
|
-
const blockHash =
|
|
334
|
+
private async addBlockToDatabase(block: L2Block, checkpointNumber: number, indexWithinCheckpoint: number) {
|
|
335
|
+
const blockHash = await block.hash();
|
|
327
336
|
|
|
328
337
|
await this.#blocks.set(block.number, {
|
|
329
338
|
header: block.header.toBuffer(),
|
|
@@ -350,57 +359,71 @@ export class BlockStore {
|
|
|
350
359
|
await this.#blockArchiveIndex.set(block.archive.root.toString(), block.number);
|
|
351
360
|
}
|
|
352
361
|
|
|
362
|
+
/** Deletes a block and all associated data (tx effects, indices). */
|
|
363
|
+
private async deleteBlock(block: L2Block): Promise<void> {
|
|
364
|
+
// Delete the block from the main blocks map
|
|
365
|
+
await this.#blocks.delete(block.number);
|
|
366
|
+
|
|
367
|
+
// Delete all tx effects for this block
|
|
368
|
+
await Promise.all(block.body.txEffects.map(tx => this.#txEffects.delete(tx.txHash.toString())));
|
|
369
|
+
|
|
370
|
+
// Delete block txs mapping
|
|
371
|
+
const blockHash = (await block.hash()).toString();
|
|
372
|
+
await this.#blockTxs.delete(blockHash);
|
|
373
|
+
|
|
374
|
+
// Clean up indices
|
|
375
|
+
await this.#blockHashIndex.delete(blockHash);
|
|
376
|
+
await this.#blockArchiveIndex.delete(block.archive.root.toString());
|
|
377
|
+
}
|
|
378
|
+
|
|
353
379
|
/**
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
* @param checkpointsToUnwind - The number of checkpoints we are to unwind
|
|
358
|
-
* @returns True if the operation is successful
|
|
380
|
+
* Removes all checkpoints with checkpoint number > checkpointNumber.
|
|
381
|
+
* Also removes ALL blocks (both checkpointed and uncheckpointed) after the last block of the given checkpoint.
|
|
382
|
+
* @param checkpointNumber - Remove all checkpoints strictly after this one.
|
|
359
383
|
*/
|
|
360
|
-
async
|
|
384
|
+
async removeCheckpointsAfter(checkpointNumber: CheckpointNumber): Promise<RemoveCheckpointsResult> {
|
|
361
385
|
return await this.db.transactionAsync(async () => {
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
386
|
+
const latestCheckpointNumber = await this.getLatestCheckpointNumber();
|
|
387
|
+
|
|
388
|
+
if (checkpointNumber >= latestCheckpointNumber) {
|
|
389
|
+
this.#log.warn(`No checkpoints to remove after ${checkpointNumber} (latest is ${latestCheckpointNumber})`);
|
|
390
|
+
return { blocksRemoved: undefined };
|
|
365
391
|
}
|
|
366
392
|
|
|
393
|
+
// If the proven checkpoint is beyond the target, update it
|
|
367
394
|
const proven = await this.getProvenCheckpointNumber();
|
|
368
|
-
if (
|
|
369
|
-
|
|
395
|
+
if (proven > checkpointNumber) {
|
|
396
|
+
this.#log.warn(`Updating proven checkpoint ${proven} to last valid checkpoint ${checkpointNumber}`);
|
|
397
|
+
await this.setProvenCheckpointNumber(checkpointNumber);
|
|
370
398
|
}
|
|
371
399
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
400
|
+
// Find the last block number to keep (last block of the given checkpoint, or 0 if no checkpoint)
|
|
401
|
+
let lastBlockToKeep: BlockNumber;
|
|
402
|
+
if (checkpointNumber <= 0) {
|
|
403
|
+
lastBlockToKeep = BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
404
|
+
} else {
|
|
405
|
+
const targetCheckpoint = await this.#checkpoints.getAsync(checkpointNumber);
|
|
406
|
+
if (!targetCheckpoint) {
|
|
407
|
+
throw new Error(`Target checkpoint ${checkpointNumber} not found in store`);
|
|
379
408
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
for (let blockNumber = checkpoint.startBlock; blockNumber <= maxBlock; blockNumber++) {
|
|
384
|
-
const block = await this.getBlock(BlockNumber(blockNumber));
|
|
385
|
-
|
|
386
|
-
if (block === undefined) {
|
|
387
|
-
this.#log.warn(`Cannot remove block ${blockNumber} from the store since we don't have it`);
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
await this.#blocks.delete(block.number);
|
|
391
|
-
await Promise.all(block.body.txEffects.map(tx => this.#txEffects.delete(tx.txHash.toString())));
|
|
392
|
-
const blockHash = (await block.hash()).toString();
|
|
393
|
-
await this.#blockTxs.delete(blockHash);
|
|
409
|
+
lastBlockToKeep = BlockNumber(targetCheckpoint.startBlock + targetCheckpoint.blockCount - 1);
|
|
410
|
+
}
|
|
394
411
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
await this.#blockArchiveIndex.delete(block.archive.root.toString());
|
|
412
|
+
// Remove all blocks after lastBlockToKeep (both checkpointed and uncheckpointed)
|
|
413
|
+
const blocksRemoved = await this.removeBlocksAfter(lastBlockToKeep);
|
|
398
414
|
|
|
399
|
-
|
|
415
|
+
// Remove all checkpoints after the target
|
|
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);
|
|
400
421
|
}
|
|
422
|
+
await this.#checkpoints.delete(c);
|
|
423
|
+
this.#log.debug(`Removed checkpoint ${c}`);
|
|
401
424
|
}
|
|
402
425
|
|
|
403
|
-
return
|
|
426
|
+
return { blocksRemoved };
|
|
404
427
|
});
|
|
405
428
|
}
|
|
406
429
|
|
|
@@ -424,20 +447,35 @@ export class BlockStore {
|
|
|
424
447
|
return checkpoints;
|
|
425
448
|
}
|
|
426
449
|
|
|
427
|
-
|
|
428
|
-
|
|
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 {
|
|
429
467
|
header: CheckpointHeader.fromBuffer(checkpointStorage.header),
|
|
430
468
|
archive: AppendOnlyTreeSnapshot.fromBuffer(checkpointStorage.archive),
|
|
469
|
+
checkpointOutHash: Fr.fromBuffer(checkpointStorage.checkpointOutHash),
|
|
431
470
|
checkpointNumber: CheckpointNumber(checkpointStorage.checkpointNumber),
|
|
432
|
-
startBlock: checkpointStorage.startBlock,
|
|
433
|
-
|
|
471
|
+
startBlock: BlockNumber(checkpointStorage.startBlock),
|
|
472
|
+
blockCount: checkpointStorage.blockCount,
|
|
434
473
|
l1: L1PublishedData.fromBuffer(checkpointStorage.l1),
|
|
435
|
-
attestations: checkpointStorage.attestations,
|
|
474
|
+
attestations: checkpointStorage.attestations.map(buf => CommitteeAttestation.fromBuffer(buf)),
|
|
436
475
|
};
|
|
437
|
-
return data;
|
|
438
476
|
}
|
|
439
477
|
|
|
440
|
-
async getBlocksForCheckpoint(checkpointNumber: CheckpointNumber): Promise<
|
|
478
|
+
async getBlocksForCheckpoint(checkpointNumber: CheckpointNumber): Promise<L2Block[] | undefined> {
|
|
441
479
|
const checkpoint = await this.#checkpoints.getAsync(checkpointNumber);
|
|
442
480
|
if (!checkpoint) {
|
|
443
481
|
return undefined;
|
|
@@ -446,7 +484,7 @@ export class BlockStore {
|
|
|
446
484
|
const blocksForCheckpoint = await toArray(
|
|
447
485
|
this.#blocks.entriesAsync({
|
|
448
486
|
start: checkpoint.startBlock,
|
|
449
|
-
end: checkpoint.startBlock + checkpoint.
|
|
487
|
+
end: checkpoint.startBlock + checkpoint.blockCount,
|
|
450
488
|
}),
|
|
451
489
|
);
|
|
452
490
|
|
|
@@ -454,6 +492,62 @@ export class BlockStore {
|
|
|
454
492
|
return converted.filter(isDefined);
|
|
455
493
|
}
|
|
456
494
|
|
|
495
|
+
/**
|
|
496
|
+
* Gets all blocks that have the given slot number.
|
|
497
|
+
* Iterates backwards through blocks for efficiency since we usually query for the last slot.
|
|
498
|
+
* @param slotNumber - The slot number to search for.
|
|
499
|
+
* @returns All blocks with the given slot number, in ascending block number order.
|
|
500
|
+
*/
|
|
501
|
+
async getBlocksForSlot(slotNumber: SlotNumber): Promise<L2Block[]> {
|
|
502
|
+
const blocks: L2Block[] = [];
|
|
503
|
+
|
|
504
|
+
// Iterate backwards through all blocks and filter by slot number
|
|
505
|
+
// This is more efficient since we usually query for the most recent slot
|
|
506
|
+
for await (const [blockNumber, blockStorage] of this.#blocks.entriesAsync({ reverse: true })) {
|
|
507
|
+
const block = await this.getBlockFromBlockStorage(blockNumber, blockStorage);
|
|
508
|
+
const blockSlot = block?.header.globalVariables.slotNumber;
|
|
509
|
+
if (block && blockSlot === slotNumber) {
|
|
510
|
+
blocks.push(block);
|
|
511
|
+
} else if (blockSlot && blockSlot < slotNumber) {
|
|
512
|
+
break; // Blocks are stored in slot ascending order, so we can stop searching
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Reverse to return blocks in ascending order (block number order)
|
|
517
|
+
return blocks.reverse();
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Removes all blocks with block number > blockNumber.
|
|
522
|
+
* Does not remove any associated checkpoints.
|
|
523
|
+
* @param blockNumber - The block number to remove after.
|
|
524
|
+
* @returns The removed blocks (for event emission).
|
|
525
|
+
*/
|
|
526
|
+
async removeBlocksAfter(blockNumber: BlockNumber): Promise<L2Block[]> {
|
|
527
|
+
return await this.db.transactionAsync(async () => {
|
|
528
|
+
const removedBlocks: L2Block[] = [];
|
|
529
|
+
|
|
530
|
+
// Get the latest block number to determine the range
|
|
531
|
+
const latestBlockNumber = await this.getLatestBlockNumber();
|
|
532
|
+
|
|
533
|
+
// Iterate from blockNumber + 1 to latestBlockNumber
|
|
534
|
+
for (let bn = blockNumber + 1; bn <= latestBlockNumber; bn++) {
|
|
535
|
+
const block = await this.getBlock(BlockNumber(bn));
|
|
536
|
+
|
|
537
|
+
if (block === undefined) {
|
|
538
|
+
this.#log.warn(`Cannot remove block ${bn} from the store since we don't have it`);
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
removedBlocks.push(block);
|
|
543
|
+
await this.deleteBlock(block);
|
|
544
|
+
this.#log.debug(`Removed block ${bn} ${(await block.hash()).toString()}`);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
return removedBlocks;
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
|
|
457
551
|
async getProvenBlockNumber(): Promise<BlockNumber> {
|
|
458
552
|
const provenCheckpointNumber = await this.getProvenCheckpointNumber();
|
|
459
553
|
if (provenCheckpointNumber === INITIAL_CHECKPOINT_NUMBER - 1) {
|
|
@@ -463,7 +557,7 @@ export class BlockStore {
|
|
|
463
557
|
if (!checkpointStorage) {
|
|
464
558
|
throw new CheckpointNotFoundError(provenCheckpointNumber);
|
|
465
559
|
} else {
|
|
466
|
-
return BlockNumber(checkpointStorage.startBlock + checkpointStorage.
|
|
560
|
+
return BlockNumber(checkpointStorage.startBlock + checkpointStorage.blockCount - 1);
|
|
467
561
|
}
|
|
468
562
|
}
|
|
469
563
|
|
|
@@ -531,13 +625,14 @@ export class BlockStore {
|
|
|
531
625
|
}
|
|
532
626
|
}
|
|
533
627
|
|
|
534
|
-
async getCheckpointedBlockByHash(blockHash:
|
|
628
|
+
async getCheckpointedBlockByHash(blockHash: BlockHash): Promise<CheckpointedL2Block | undefined> {
|
|
535
629
|
const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
|
|
536
630
|
if (blockNumber === undefined) {
|
|
537
631
|
return undefined;
|
|
538
632
|
}
|
|
539
633
|
return this.getCheckpointedBlock(BlockNumber(blockNumber));
|
|
540
634
|
}
|
|
635
|
+
|
|
541
636
|
async getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
542
637
|
const blockNumber = await this.#blockArchiveIndex.getAsync(archive.toString());
|
|
543
638
|
if (blockNumber === undefined) {
|
|
@@ -552,7 +647,7 @@ export class BlockStore {
|
|
|
552
647
|
* @param limit - The number of blocks to return.
|
|
553
648
|
* @returns The requested L2 blocks
|
|
554
649
|
*/
|
|
555
|
-
async *getBlocks(start: BlockNumber, limit: number): AsyncIterableIterator<
|
|
650
|
+
async *getBlocks(start: BlockNumber, limit: number): AsyncIterableIterator<L2Block> {
|
|
556
651
|
for await (const [blockNumber, blockStorage] of this.getBlockStorages(start, limit)) {
|
|
557
652
|
const block = await this.getBlockFromBlockStorage(blockNumber, blockStorage);
|
|
558
653
|
if (block) {
|
|
@@ -561,12 +656,38 @@ export class BlockStore {
|
|
|
561
656
|
}
|
|
562
657
|
}
|
|
563
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
|
+
|
|
564
685
|
/**
|
|
565
686
|
* Gets an L2 block.
|
|
566
687
|
* @param blockNumber - The number of the block to return.
|
|
567
688
|
* @returns The requested L2 block.
|
|
568
689
|
*/
|
|
569
|
-
async getBlock(blockNumber: BlockNumber): Promise<
|
|
690
|
+
async getBlock(blockNumber: BlockNumber): Promise<L2Block | undefined> {
|
|
570
691
|
const blockStorage = await this.#blocks.getAsync(blockNumber);
|
|
571
692
|
if (!blockStorage || !blockStorage.header) {
|
|
572
693
|
return Promise.resolve(undefined);
|
|
@@ -579,7 +700,7 @@ export class BlockStore {
|
|
|
579
700
|
* @param blockHash - The hash of the block to return.
|
|
580
701
|
* @returns The requested L2 block.
|
|
581
702
|
*/
|
|
582
|
-
async getBlockByHash(blockHash:
|
|
703
|
+
async getBlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
|
|
583
704
|
const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
|
|
584
705
|
if (blockNumber === undefined) {
|
|
585
706
|
return undefined;
|
|
@@ -592,7 +713,7 @@ export class BlockStore {
|
|
|
592
713
|
* @param archive - The archive root of the block to return.
|
|
593
714
|
* @returns The requested L2 block.
|
|
594
715
|
*/
|
|
595
|
-
async getBlockByArchive(archive: Fr): Promise<
|
|
716
|
+
async getBlockByArchive(archive: Fr): Promise<L2Block | undefined> {
|
|
596
717
|
const blockNumber = await this.#blockArchiveIndex.getAsync(archive.toString());
|
|
597
718
|
if (blockNumber === undefined) {
|
|
598
719
|
return undefined;
|
|
@@ -605,7 +726,7 @@ export class BlockStore {
|
|
|
605
726
|
* @param blockHash - The hash of the block to return.
|
|
606
727
|
* @returns The requested block header.
|
|
607
728
|
*/
|
|
608
|
-
async getBlockHeaderByHash(blockHash:
|
|
729
|
+
async getBlockHeaderByHash(blockHash: BlockHash): Promise<BlockHeader | undefined> {
|
|
609
730
|
const blockNumber = await this.#blockHashIndex.getAsync(blockHash.toString());
|
|
610
731
|
if (blockNumber === undefined) {
|
|
611
732
|
return undefined;
|
|
@@ -665,14 +786,24 @@ export class BlockStore {
|
|
|
665
786
|
}
|
|
666
787
|
}
|
|
667
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
|
+
|
|
668
799
|
private async getBlockFromBlockStorage(
|
|
669
800
|
blockNumber: number,
|
|
670
801
|
blockStorage: BlockStorage,
|
|
671
|
-
): Promise<
|
|
672
|
-
const header =
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
const blockHashString = bufferToHex(blockHash);
|
|
802
|
+
): Promise<L2Block | undefined> {
|
|
803
|
+
const { header, archive, blockHash, checkpointNumber, indexWithinCheckpoint } =
|
|
804
|
+
this.getBlockDataFromBlockStorage(blockStorage);
|
|
805
|
+
header.setHash(blockHash);
|
|
806
|
+
const blockHashString = bufferToHex(blockStorage.blockHash);
|
|
676
807
|
const blockTxsBuffer = await this.#blockTxs.getAsync(blockHashString);
|
|
677
808
|
if (blockTxsBuffer === undefined) {
|
|
678
809
|
this.#log.warn(`Could not find body for block ${header.globalVariables.blockNumber} ${blockHash}`);
|
|
@@ -691,13 +822,7 @@ export class BlockStore {
|
|
|
691
822
|
txEffects.push(deserializeIndexedTxEffect(txEffect).data);
|
|
692
823
|
}
|
|
693
824
|
const body = new Body(txEffects);
|
|
694
|
-
const block = new
|
|
695
|
-
archive,
|
|
696
|
-
header,
|
|
697
|
-
body,
|
|
698
|
-
CheckpointNumber(blockStorage.checkpointNumber!),
|
|
699
|
-
blockStorage.indexWithinCheckpoint,
|
|
700
|
-
);
|
|
825
|
+
const block = new L2Block(archive, header, body, checkpointNumber, indexWithinCheckpoint);
|
|
701
826
|
|
|
702
827
|
if (block.number !== blockNumber) {
|
|
703
828
|
throw new Error(
|
|
@@ -727,19 +852,48 @@ export class BlockStore {
|
|
|
727
852
|
* @param txHash - The hash of a tx we try to get the receipt for.
|
|
728
853
|
* @returns The requested tx receipt (or undefined if not found).
|
|
729
854
|
*/
|
|
730
|
-
async getSettledTxReceipt(
|
|
855
|
+
async getSettledTxReceipt(
|
|
856
|
+
txHash: TxHash,
|
|
857
|
+
l1Constants?: Pick<L1RollupConstants, 'epochDuration'>,
|
|
858
|
+
): Promise<TxReceipt | undefined> {
|
|
731
859
|
const txEffect = await this.getTxEffect(txHash);
|
|
732
860
|
if (!txEffect) {
|
|
733
861
|
return undefined;
|
|
734
862
|
}
|
|
735
863
|
|
|
864
|
+
const blockNumber = BlockNumber(txEffect.l2BlockNumber);
|
|
865
|
+
|
|
866
|
+
// Use existing archiver methods to determine finalization level
|
|
867
|
+
const [provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber, blockData] = await Promise.all([
|
|
868
|
+
this.getProvenBlockNumber(),
|
|
869
|
+
this.getCheckpointedL2BlockNumber(),
|
|
870
|
+
this.getFinalizedL2BlockNumber(),
|
|
871
|
+
this.getBlockData(blockNumber),
|
|
872
|
+
]);
|
|
873
|
+
|
|
874
|
+
let status: TxStatus;
|
|
875
|
+
if (blockNumber <= finalizedBlockNumber) {
|
|
876
|
+
status = TxStatus.FINALIZED;
|
|
877
|
+
} else if (blockNumber <= provenBlockNumber) {
|
|
878
|
+
status = TxStatus.PROVEN;
|
|
879
|
+
} else if (blockNumber <= checkpointedBlockNumber) {
|
|
880
|
+
status = TxStatus.CHECKPOINTED;
|
|
881
|
+
} else {
|
|
882
|
+
status = TxStatus.PROPOSED;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
const epochNumber =
|
|
886
|
+
blockData && l1Constants ? getEpochAtSlot(blockData.header.globalVariables.slotNumber, l1Constants) : undefined;
|
|
887
|
+
|
|
736
888
|
return new TxReceipt(
|
|
737
889
|
txHash,
|
|
738
|
-
|
|
739
|
-
|
|
890
|
+
status,
|
|
891
|
+
TxReceipt.executionResultFromRevertCode(txEffect.data.revertCode),
|
|
892
|
+
undefined,
|
|
740
893
|
txEffect.data.transactionFee.toBigInt(),
|
|
741
894
|
txEffect.l2BlockHash,
|
|
742
|
-
|
|
895
|
+
blockNumber,
|
|
896
|
+
epochNumber,
|
|
743
897
|
);
|
|
744
898
|
}
|
|
745
899
|
|
|
@@ -776,7 +930,7 @@ export class BlockStore {
|
|
|
776
930
|
if (!checkpoint) {
|
|
777
931
|
return BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
778
932
|
}
|
|
779
|
-
return BlockNumber(checkpoint.startBlock + checkpoint.
|
|
933
|
+
return BlockNumber(checkpoint.startBlock + checkpoint.blockCount - 1);
|
|
780
934
|
}
|
|
781
935
|
|
|
782
936
|
async getLatestL2BlockNumber(): Promise<BlockNumber> {
|
|
@@ -811,6 +965,20 @@ export class BlockStore {
|
|
|
811
965
|
return result;
|
|
812
966
|
}
|
|
813
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
|
+
|
|
814
982
|
#computeBlockRange(start: BlockNumber, limit: number): Required<Pick<Range<number>, 'start' | 'limit'>> {
|
|
815
983
|
if (limit < 1) {
|
|
816
984
|
throw new Error(`Invalid limit: ${limit}`);
|