@aztec/archiver 0.0.1-commit.f2ce05ee → 0.0.1-commit.f81dbcf
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/archiver.d.ts +7 -4
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +55 -106
- package/dest/factory.d.ts +2 -3
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +13 -13
- package/dest/index.d.ts +2 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/l1/bin/retrieve-calldata.js +32 -28
- 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 +9 -13
- package/dest/l1/spire_proposer.d.ts +5 -5
- package/dest/l1/spire_proposer.d.ts.map +1 -1
- package/dest/l1/spire_proposer.js +9 -17
- package/dest/modules/data_source_base.d.ts +10 -5
- package/dest/modules/data_source_base.d.ts.map +1 -1
- package/dest/modules/data_source_base.js +29 -73
- package/dest/modules/data_store_updater.d.ts +18 -3
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +75 -23
- package/dest/modules/instrumentation.d.ts +4 -2
- package/dest/modules/instrumentation.d.ts.map +1 -1
- package/dest/modules/instrumentation.js +9 -2
- package/dest/modules/l1_synchronizer.d.ts +5 -8
- package/dest/modules/l1_synchronizer.d.ts.map +1 -1
- package/dest/modules/l1_synchronizer.js +37 -10
- package/dest/store/block_store.d.ts +25 -22
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +100 -30
- package/dest/store/kv_archiver_store.d.ts +30 -6
- package/dest/store/kv_archiver_store.d.ts.map +1 -1
- package/dest/store/kv_archiver_store.js +34 -4
- 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 +48 -10
- package/dest/store/message_store.js +1 -1
- package/dest/test/fake_l1_state.d.ts +13 -1
- package/dest/test/fake_l1_state.d.ts.map +1 -1
- package/dest/test/fake_l1_state.js +84 -20
- package/dest/test/mock_archiver.d.ts +1 -1
- package/dest/test/mock_archiver.d.ts.map +1 -1
- package/dest/test/mock_archiver.js +3 -2
- package/dest/test/mock_l2_block_source.d.ts +21 -5
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +132 -86
- package/dest/test/mock_structs.d.ts +4 -1
- package/dest/test/mock_structs.d.ts.map +1 -1
- package/dest/test/mock_structs.js +13 -1
- package/dest/test/noop_l1_archiver.d.ts +4 -1
- package/dest/test/noop_l1_archiver.d.ts.map +1 -1
- package/dest/test/noop_l1_archiver.js +5 -1
- package/package.json +13 -13
- package/src/archiver.ts +67 -128
- package/src/factory.ts +12 -5
- package/src/index.ts +1 -0
- package/src/l1/README.md +25 -68
- package/src/l1/bin/retrieve-calldata.ts +40 -27
- package/src/l1/calldata_retriever.ts +249 -379
- package/src/l1/data_retrieval.ts +6 -16
- package/src/l1/spire_proposer.ts +7 -15
- package/src/modules/data_source_base.ts +56 -95
- package/src/modules/data_store_updater.ts +82 -22
- package/src/modules/instrumentation.ts +9 -2
- package/src/modules/l1_synchronizer.ts +44 -14
- package/src/store/block_store.ts +126 -49
- package/src/store/kv_archiver_store.ts +57 -7
- package/src/store/l2_tips_cache.ts +89 -0
- package/src/store/log_store.ts +66 -12
- package/src/store/message_store.ts +1 -1
- package/src/test/fake_l1_state.ts +110 -21
- package/src/test/mock_archiver.ts +3 -2
- package/src/test/mock_l2_block_source.ts +173 -81
- package/src/test/mock_structs.ts +20 -6
- package/src/test/noop_l1_archiver.ts +7 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
2
2
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
3
3
|
import { InboxContract, RollupContract } from '@aztec/ethereum/contracts';
|
|
4
|
-
import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
|
|
5
4
|
import type { L1BlockId } from '@aztec/ethereum/l1-types';
|
|
6
5
|
import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
|
|
7
6
|
import { maxBigint } from '@aztec/foundation/bigint';
|
|
@@ -9,7 +8,6 @@ import { BlockNumber, CheckpointNumber, EpochNumber } from '@aztec/foundation/br
|
|
|
9
8
|
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
10
9
|
import { pick } from '@aztec/foundation/collection';
|
|
11
10
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
12
|
-
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
13
11
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
14
12
|
import { count } from '@aztec/foundation/string';
|
|
15
13
|
import { DateProvider, Timer, elapsed } from '@aztec/foundation/timer';
|
|
@@ -28,6 +26,7 @@ import {
|
|
|
28
26
|
retrievedToPublishedCheckpoint,
|
|
29
27
|
} from '../l1/data_retrieval.js';
|
|
30
28
|
import type { KVArchiverDataStore } from '../store/kv_archiver_store.js';
|
|
29
|
+
import type { L2TipsCache } from '../store/l2_tips_cache.js';
|
|
31
30
|
import type { InboxMessage } from '../structs/inbox_message.js';
|
|
32
31
|
import { ArchiverDataStoreUpdater } from './data_store_updater.js';
|
|
33
32
|
import type { ArchiverInstrumentation } from './instrumentation.js';
|
|
@@ -60,10 +59,6 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
60
59
|
private readonly debugClient: ViemPublicDebugClient,
|
|
61
60
|
private readonly rollup: RollupContract,
|
|
62
61
|
private readonly inbox: InboxContract,
|
|
63
|
-
private readonly l1Addresses: Pick<
|
|
64
|
-
L1ContractAddresses,
|
|
65
|
-
'registryAddress' | 'governanceProposerAddress' | 'slashFactoryAddress'
|
|
66
|
-
> & { slashingProposerAddress: EthAddress },
|
|
67
62
|
private readonly store: KVArchiverDataStore,
|
|
68
63
|
private config: {
|
|
69
64
|
batchSize: number;
|
|
@@ -74,12 +69,18 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
74
69
|
private readonly epochCache: EpochCache,
|
|
75
70
|
private readonly dateProvider: DateProvider,
|
|
76
71
|
private readonly instrumentation: ArchiverInstrumentation,
|
|
77
|
-
private readonly l1Constants: L1RollupConstants & {
|
|
72
|
+
private readonly l1Constants: L1RollupConstants & {
|
|
73
|
+
l1StartBlockHash: Buffer32;
|
|
74
|
+
genesisArchiveRoot: Fr;
|
|
75
|
+
},
|
|
78
76
|
private readonly events: ArchiverEmitter,
|
|
79
77
|
tracer: Tracer,
|
|
78
|
+
l2TipsCache?: L2TipsCache,
|
|
80
79
|
private readonly log: Logger = createLogger('archiver:l1-sync'),
|
|
81
80
|
) {
|
|
82
|
-
this.updater = new ArchiverDataStoreUpdater(this.store
|
|
81
|
+
this.updater = new ArchiverDataStoreUpdater(this.store, l2TipsCache, {
|
|
82
|
+
rollupManaLimit: l1Constants.rollupManaLimit,
|
|
83
|
+
});
|
|
83
84
|
this.tracer = tracer;
|
|
84
85
|
}
|
|
85
86
|
|
|
@@ -215,6 +216,9 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
215
216
|
this.instrumentation.updateL1BlockHeight(currentL1BlockNumber);
|
|
216
217
|
}
|
|
217
218
|
|
|
219
|
+
// Update the finalized L2 checkpoint based on L1 finality.
|
|
220
|
+
await this.updateFinalizedCheckpoint();
|
|
221
|
+
|
|
218
222
|
// After syncing has completed, update the current l1 block number and timestamp,
|
|
219
223
|
// otherwise we risk announcing to the world that we've synced to a given point,
|
|
220
224
|
// but the corresponding blocks have not been processed (see #12631).
|
|
@@ -230,6 +234,33 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
230
234
|
});
|
|
231
235
|
}
|
|
232
236
|
|
|
237
|
+
/** Query L1 for its finalized block and update the finalized checkpoint accordingly. */
|
|
238
|
+
private async updateFinalizedCheckpoint(): Promise<void> {
|
|
239
|
+
try {
|
|
240
|
+
const finalizedL1Block = await this.publicClient.getBlock({ blockTag: 'finalized', includeTransactions: false });
|
|
241
|
+
const finalizedL1BlockNumber = finalizedL1Block.number;
|
|
242
|
+
const finalizedCheckpointNumber = await this.rollup.getProvenCheckpointNumber({
|
|
243
|
+
blockNumber: finalizedL1BlockNumber,
|
|
244
|
+
});
|
|
245
|
+
const localFinalizedCheckpointNumber = await this.store.getFinalizedCheckpointNumber();
|
|
246
|
+
if (localFinalizedCheckpointNumber !== finalizedCheckpointNumber) {
|
|
247
|
+
await this.updater.setFinalizedCheckpointNumber(finalizedCheckpointNumber);
|
|
248
|
+
const finalizedL2BlockNumber = await this.store.getFinalizedL2BlockNumber();
|
|
249
|
+
this.log.info(
|
|
250
|
+
`Updated finalized chain to checkpoint ${finalizedCheckpointNumber} (L2 block ${finalizedL2BlockNumber})`,
|
|
251
|
+
{
|
|
252
|
+
finalizedCheckpointNumber,
|
|
253
|
+
previousFinalizedCheckpointNumber: localFinalizedCheckpointNumber,
|
|
254
|
+
finalizedL2BlockNumber,
|
|
255
|
+
finalizedL1BlockNumber,
|
|
256
|
+
},
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
} catch (err) {
|
|
260
|
+
this.log.warn(`Failed to update finalized checkpoint: ${err}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
233
264
|
/** Prune all proposed local blocks that should have been checkpointed by now. */
|
|
234
265
|
private async pruneUncheckpointedBlocks(currentL1Timestamp: bigint) {
|
|
235
266
|
const [lastCheckpointedBlockNumber, lastProposedBlockNumber] = await Promise.all([
|
|
@@ -550,7 +581,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
550
581
|
if (provenCheckpointNumber === 0) {
|
|
551
582
|
const localProvenCheckpointNumber = await this.store.getProvenCheckpointNumber();
|
|
552
583
|
if (localProvenCheckpointNumber !== provenCheckpointNumber) {
|
|
553
|
-
await this.
|
|
584
|
+
await this.updater.setProvenCheckpointNumber(provenCheckpointNumber);
|
|
554
585
|
this.log.info(`Rolled back proven chain to checkpoint ${provenCheckpointNumber}`, { provenCheckpointNumber });
|
|
555
586
|
}
|
|
556
587
|
}
|
|
@@ -582,13 +613,13 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
582
613
|
) {
|
|
583
614
|
const localProvenCheckpointNumber = await this.store.getProvenCheckpointNumber();
|
|
584
615
|
if (localProvenCheckpointNumber !== provenCheckpointNumber) {
|
|
585
|
-
await this.
|
|
616
|
+
await this.updater.setProvenCheckpointNumber(provenCheckpointNumber);
|
|
586
617
|
this.log.info(`Updated proven chain to checkpoint ${provenCheckpointNumber}`, { provenCheckpointNumber });
|
|
587
618
|
const provenSlotNumber = localCheckpointForDestinationProvenCheckpointNumber.header.slotNumber;
|
|
588
619
|
const provenEpochNumber: EpochNumber = getEpochAtSlot(provenSlotNumber, this.l1Constants);
|
|
589
620
|
const lastBlockNumberInCheckpoint =
|
|
590
621
|
localCheckpointForDestinationProvenCheckpointNumber.startBlock +
|
|
591
|
-
localCheckpointForDestinationProvenCheckpointNumber.
|
|
622
|
+
localCheckpointForDestinationProvenCheckpointNumber.blockCount -
|
|
592
623
|
1;
|
|
593
624
|
|
|
594
625
|
this.events.emit(L2BlockSourceEvents.L2BlockProven, {
|
|
@@ -597,7 +628,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
597
628
|
slotNumber: provenSlotNumber,
|
|
598
629
|
epochNumber: provenEpochNumber,
|
|
599
630
|
});
|
|
600
|
-
this.instrumentation.
|
|
631
|
+
this.instrumentation.updateLastProvenCheckpoint(localCheckpointForDestinationProvenCheckpointNumber);
|
|
601
632
|
} else {
|
|
602
633
|
this.log.trace(`Proven checkpoint ${provenCheckpointNumber} already stored.`);
|
|
603
634
|
}
|
|
@@ -706,7 +737,6 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
706
737
|
this.blobClient,
|
|
707
738
|
searchStartBlock, // TODO(palla/reorg): If the L2 reorg was due to an L1 reorg, we need to start search earlier
|
|
708
739
|
searchEndBlock,
|
|
709
|
-
this.l1Addresses,
|
|
710
740
|
this.instrumentation,
|
|
711
741
|
this.log,
|
|
712
742
|
!initialSyncComplete, // isHistoricalSync
|
|
@@ -819,7 +849,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
819
849
|
const prunedCheckpointNumber = result.prunedBlocks[0].checkpointNumber;
|
|
820
850
|
const prunedSlotNumber = result.prunedBlocks[0].header.globalVariables.slotNumber;
|
|
821
851
|
|
|
822
|
-
this.log.
|
|
852
|
+
this.log.info(
|
|
823
853
|
`Pruned ${result.prunedBlocks.length} mismatching blocks for checkpoint ${prunedCheckpointNumber}`,
|
|
824
854
|
{ prunedBlocks: result.prunedBlocks.map(b => b.toBlockInfo()), prunedSlotNumber, prunedCheckpointNumber },
|
|
825
855
|
);
|
package/src/store/block_store.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { isDefined } from '@aztec/foundation/types';
|
|
|
9
9
|
import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncSingleton, Range } from '@aztec/kv-store';
|
|
10
10
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
11
11
|
import {
|
|
12
|
+
type BlockData,
|
|
12
13
|
BlockHash,
|
|
13
14
|
Body,
|
|
14
15
|
CheckpointedL2Block,
|
|
@@ -18,8 +19,8 @@ import {
|
|
|
18
19
|
deserializeValidateCheckpointResult,
|
|
19
20
|
serializeValidateCheckpointResult,
|
|
20
21
|
} from '@aztec/stdlib/block';
|
|
21
|
-
import { L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
22
|
-
import type
|
|
22
|
+
import { type CheckpointData, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
23
|
+
import { type L1RollupConstants, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
23
24
|
import { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
24
25
|
import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees';
|
|
25
26
|
import {
|
|
@@ -61,23 +62,14 @@ type BlockStorage = {
|
|
|
61
62
|
type CheckpointStorage = {
|
|
62
63
|
header: Buffer;
|
|
63
64
|
archive: Buffer;
|
|
65
|
+
checkpointOutHash: Buffer;
|
|
64
66
|
checkpointNumber: number;
|
|
65
67
|
startBlock: number;
|
|
66
|
-
|
|
68
|
+
blockCount: number;
|
|
67
69
|
l1: Buffer;
|
|
68
70
|
attestations: Buffer[];
|
|
69
71
|
};
|
|
70
72
|
|
|
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
73
|
export type RemoveCheckpointsResult = { blocksRemoved: L2Block[] | undefined };
|
|
82
74
|
|
|
83
75
|
/**
|
|
@@ -90,6 +82,9 @@ export class BlockStore {
|
|
|
90
82
|
/** Map checkpoint number to checkpoint data */
|
|
91
83
|
#checkpoints: AztecAsyncMap<number, CheckpointStorage>;
|
|
92
84
|
|
|
85
|
+
/** Map slot number to checkpoint number, for looking up checkpoints by slot range. */
|
|
86
|
+
#slotToCheckpoint: AztecAsyncMap<number, number>;
|
|
87
|
+
|
|
93
88
|
/** Map block hash to list of tx hashes */
|
|
94
89
|
#blockTxs: AztecAsyncMap<string, Buffer>;
|
|
95
90
|
|
|
@@ -102,6 +97,9 @@ export class BlockStore {
|
|
|
102
97
|
/** Stores last proven checkpoint */
|
|
103
98
|
#lastProvenCheckpoint: AztecAsyncSingleton<number>;
|
|
104
99
|
|
|
100
|
+
/** Stores last finalized checkpoint (proven at or before the finalized L1 block) */
|
|
101
|
+
#lastFinalizedCheckpoint: AztecAsyncSingleton<number>;
|
|
102
|
+
|
|
105
103
|
/** Stores the pending chain validation status */
|
|
106
104
|
#pendingChainValidationStatus: AztecAsyncSingleton<Buffer>;
|
|
107
105
|
|
|
@@ -116,10 +114,7 @@ export class BlockStore {
|
|
|
116
114
|
|
|
117
115
|
#log = createLogger('archiver:block_store');
|
|
118
116
|
|
|
119
|
-
constructor(
|
|
120
|
-
private db: AztecAsyncKVStore,
|
|
121
|
-
private l1Constants: Pick<L1RollupConstants, 'epochDuration'>,
|
|
122
|
-
) {
|
|
117
|
+
constructor(private db: AztecAsyncKVStore) {
|
|
123
118
|
this.#blocks = db.openMap('archiver_blocks');
|
|
124
119
|
this.#blockTxs = db.openMap('archiver_block_txs');
|
|
125
120
|
this.#txEffects = db.openMap('archiver_tx_effects');
|
|
@@ -128,20 +123,27 @@ export class BlockStore {
|
|
|
128
123
|
this.#blockArchiveIndex = db.openMap('archiver_block_archive_index');
|
|
129
124
|
this.#lastSynchedL1Block = db.openSingleton('archiver_last_synched_l1_block');
|
|
130
125
|
this.#lastProvenCheckpoint = db.openSingleton('archiver_last_proven_l2_checkpoint');
|
|
126
|
+
this.#lastFinalizedCheckpoint = db.openSingleton('archiver_last_finalized_l2_checkpoint');
|
|
131
127
|
this.#pendingChainValidationStatus = db.openSingleton('archiver_pending_chain_validation_status');
|
|
132
128
|
this.#checkpoints = db.openMap('archiver_checkpoints');
|
|
129
|
+
this.#slotToCheckpoint = db.openMap('archiver_slot_to_checkpoint');
|
|
133
130
|
}
|
|
134
131
|
|
|
135
132
|
/**
|
|
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
|
|
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.
|
|
140
135
|
* @returns The finalized block number.
|
|
141
136
|
*/
|
|
142
137
|
async getFinalizedL2BlockNumber(): Promise<BlockNumber> {
|
|
143
|
-
const
|
|
144
|
-
|
|
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);
|
|
145
147
|
}
|
|
146
148
|
|
|
147
149
|
/**
|
|
@@ -273,7 +275,7 @@ export class BlockStore {
|
|
|
273
275
|
|
|
274
276
|
// If we have a previous checkpoint then we need to get the previous block number
|
|
275
277
|
if (previousCheckpointData !== undefined) {
|
|
276
|
-
previousBlockNumber = BlockNumber(previousCheckpointData.startBlock + previousCheckpointData.
|
|
278
|
+
previousBlockNumber = BlockNumber(previousCheckpointData.startBlock + previousCheckpointData.blockCount - 1);
|
|
277
279
|
previousBlock = await this.getBlock(previousBlockNumber);
|
|
278
280
|
if (previousBlock === undefined) {
|
|
279
281
|
// We should be able to get the required previous block
|
|
@@ -337,12 +339,16 @@ export class BlockStore {
|
|
|
337
339
|
await this.#checkpoints.set(checkpoint.checkpoint.number, {
|
|
338
340
|
header: checkpoint.checkpoint.header.toBuffer(),
|
|
339
341
|
archive: checkpoint.checkpoint.archive.toBuffer(),
|
|
342
|
+
checkpointOutHash: checkpoint.checkpoint.getCheckpointOutHash().toBuffer(),
|
|
340
343
|
l1: checkpoint.l1.toBuffer(),
|
|
341
344
|
attestations: checkpoint.attestations.map(attestation => attestation.toBuffer()),
|
|
342
345
|
checkpointNumber: checkpoint.checkpoint.number,
|
|
343
346
|
startBlock: checkpoint.checkpoint.blocks[0].number,
|
|
344
|
-
|
|
347
|
+
blockCount: checkpoint.checkpoint.blocks.length,
|
|
345
348
|
});
|
|
349
|
+
|
|
350
|
+
// Update slot-to-checkpoint index
|
|
351
|
+
await this.#slotToCheckpoint.set(checkpoint.checkpoint.header.slotNumber, checkpoint.checkpoint.number);
|
|
346
352
|
}
|
|
347
353
|
|
|
348
354
|
await this.#lastSynchedL1Block.set(checkpoints[checkpoints.length - 1].l1.blockNumber);
|
|
@@ -425,7 +431,7 @@ export class BlockStore {
|
|
|
425
431
|
if (!targetCheckpoint) {
|
|
426
432
|
throw new Error(`Target checkpoint ${checkpointNumber} not found in store`);
|
|
427
433
|
}
|
|
428
|
-
lastBlockToKeep = BlockNumber(targetCheckpoint.startBlock + targetCheckpoint.
|
|
434
|
+
lastBlockToKeep = BlockNumber(targetCheckpoint.startBlock + targetCheckpoint.blockCount - 1);
|
|
429
435
|
}
|
|
430
436
|
|
|
431
437
|
// Remove all blocks after lastBlockToKeep (both checkpointed and uncheckpointed)
|
|
@@ -433,6 +439,11 @@ export class BlockStore {
|
|
|
433
439
|
|
|
434
440
|
// Remove all checkpoints after the target
|
|
435
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);
|
|
446
|
+
}
|
|
436
447
|
await this.#checkpoints.delete(c);
|
|
437
448
|
this.#log.debug(`Removed checkpoint ${c}`);
|
|
438
449
|
}
|
|
@@ -461,17 +472,32 @@ export class BlockStore {
|
|
|
461
472
|
return checkpoints;
|
|
462
473
|
}
|
|
463
474
|
|
|
464
|
-
|
|
465
|
-
|
|
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 {
|
|
466
492
|
header: CheckpointHeader.fromBuffer(checkpointStorage.header),
|
|
467
493
|
archive: AppendOnlyTreeSnapshot.fromBuffer(checkpointStorage.archive),
|
|
494
|
+
checkpointOutHash: Fr.fromBuffer(checkpointStorage.checkpointOutHash),
|
|
468
495
|
checkpointNumber: CheckpointNumber(checkpointStorage.checkpointNumber),
|
|
469
|
-
startBlock: checkpointStorage.startBlock,
|
|
470
|
-
|
|
496
|
+
startBlock: BlockNumber(checkpointStorage.startBlock),
|
|
497
|
+
blockCount: checkpointStorage.blockCount,
|
|
471
498
|
l1: L1PublishedData.fromBuffer(checkpointStorage.l1),
|
|
472
|
-
attestations: checkpointStorage.attestations,
|
|
499
|
+
attestations: checkpointStorage.attestations.map(buf => CommitteeAttestation.fromBuffer(buf)),
|
|
473
500
|
};
|
|
474
|
-
return data;
|
|
475
501
|
}
|
|
476
502
|
|
|
477
503
|
async getBlocksForCheckpoint(checkpointNumber: CheckpointNumber): Promise<L2Block[] | undefined> {
|
|
@@ -483,7 +509,7 @@ export class BlockStore {
|
|
|
483
509
|
const blocksForCheckpoint = await toArray(
|
|
484
510
|
this.#blocks.entriesAsync({
|
|
485
511
|
start: checkpoint.startBlock,
|
|
486
|
-
end: checkpoint.startBlock + checkpoint.
|
|
512
|
+
end: checkpoint.startBlock + checkpoint.blockCount,
|
|
487
513
|
}),
|
|
488
514
|
);
|
|
489
515
|
|
|
@@ -556,7 +582,7 @@ export class BlockStore {
|
|
|
556
582
|
if (!checkpointStorage) {
|
|
557
583
|
throw new CheckpointNotFoundError(provenCheckpointNumber);
|
|
558
584
|
} else {
|
|
559
|
-
return BlockNumber(checkpointStorage.startBlock + checkpointStorage.
|
|
585
|
+
return BlockNumber(checkpointStorage.startBlock + checkpointStorage.blockCount - 1);
|
|
560
586
|
}
|
|
561
587
|
}
|
|
562
588
|
|
|
@@ -655,6 +681,32 @@ export class BlockStore {
|
|
|
655
681
|
}
|
|
656
682
|
}
|
|
657
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
|
+
|
|
658
710
|
/**
|
|
659
711
|
* Gets an L2 block.
|
|
660
712
|
* @param blockNumber - The number of the block to return.
|
|
@@ -759,15 +811,24 @@ export class BlockStore {
|
|
|
759
811
|
}
|
|
760
812
|
}
|
|
761
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
|
+
|
|
762
824
|
private async getBlockFromBlockStorage(
|
|
763
825
|
blockNumber: number,
|
|
764
826
|
blockStorage: BlockStorage,
|
|
765
827
|
): Promise<L2Block | undefined> {
|
|
766
|
-
const header =
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
const blockHashString = bufferToHex(blockHash);
|
|
828
|
+
const { header, archive, blockHash, checkpointNumber, indexWithinCheckpoint } =
|
|
829
|
+
this.getBlockDataFromBlockStorage(blockStorage);
|
|
830
|
+
header.setHash(blockHash);
|
|
831
|
+
const blockHashString = bufferToHex(blockStorage.blockHash);
|
|
771
832
|
const blockTxsBuffer = await this.#blockTxs.getAsync(blockHashString);
|
|
772
833
|
if (blockTxsBuffer === undefined) {
|
|
773
834
|
this.#log.warn(`Could not find body for block ${header.globalVariables.blockNumber} ${blockHash}`);
|
|
@@ -786,13 +847,7 @@ export class BlockStore {
|
|
|
786
847
|
txEffects.push(deserializeIndexedTxEffect(txEffect).data);
|
|
787
848
|
}
|
|
788
849
|
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
|
-
);
|
|
850
|
+
const block = new L2Block(archive, header, body, checkpointNumber, indexWithinCheckpoint);
|
|
796
851
|
|
|
797
852
|
if (block.number !== blockNumber) {
|
|
798
853
|
throw new Error(
|
|
@@ -822,7 +877,10 @@ export class BlockStore {
|
|
|
822
877
|
* @param txHash - The hash of a tx we try to get the receipt for.
|
|
823
878
|
* @returns The requested tx receipt (or undefined if not found).
|
|
824
879
|
*/
|
|
825
|
-
async getSettledTxReceipt(
|
|
880
|
+
async getSettledTxReceipt(
|
|
881
|
+
txHash: TxHash,
|
|
882
|
+
l1Constants?: Pick<L1RollupConstants, 'epochDuration'>,
|
|
883
|
+
): Promise<TxReceipt | undefined> {
|
|
826
884
|
const txEffect = await this.getTxEffect(txHash);
|
|
827
885
|
if (!txEffect) {
|
|
828
886
|
return undefined;
|
|
@@ -831,10 +889,11 @@ export class BlockStore {
|
|
|
831
889
|
const blockNumber = BlockNumber(txEffect.l2BlockNumber);
|
|
832
890
|
|
|
833
891
|
// Use existing archiver methods to determine finalization level
|
|
834
|
-
const [provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber] = await Promise.all([
|
|
892
|
+
const [provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber, blockData] = await Promise.all([
|
|
835
893
|
this.getProvenBlockNumber(),
|
|
836
894
|
this.getCheckpointedL2BlockNumber(),
|
|
837
895
|
this.getFinalizedL2BlockNumber(),
|
|
896
|
+
this.getBlockData(blockNumber),
|
|
838
897
|
]);
|
|
839
898
|
|
|
840
899
|
let status: TxStatus;
|
|
@@ -848,6 +907,9 @@ export class BlockStore {
|
|
|
848
907
|
status = TxStatus.PROPOSED;
|
|
849
908
|
}
|
|
850
909
|
|
|
910
|
+
const epochNumber =
|
|
911
|
+
blockData && l1Constants ? getEpochAtSlot(blockData.header.globalVariables.slotNumber, l1Constants) : undefined;
|
|
912
|
+
|
|
851
913
|
return new TxReceipt(
|
|
852
914
|
txHash,
|
|
853
915
|
status,
|
|
@@ -856,6 +918,7 @@ export class BlockStore {
|
|
|
856
918
|
txEffect.data.transactionFee.toBigInt(),
|
|
857
919
|
txEffect.l2BlockHash,
|
|
858
920
|
blockNumber,
|
|
921
|
+
epochNumber,
|
|
859
922
|
);
|
|
860
923
|
}
|
|
861
924
|
|
|
@@ -892,7 +955,7 @@ export class BlockStore {
|
|
|
892
955
|
if (!checkpoint) {
|
|
893
956
|
return BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
894
957
|
}
|
|
895
|
-
return BlockNumber(checkpoint.startBlock + checkpoint.
|
|
958
|
+
return BlockNumber(checkpoint.startBlock + checkpoint.blockCount - 1);
|
|
896
959
|
}
|
|
897
960
|
|
|
898
961
|
async getLatestL2BlockNumber(): Promise<BlockNumber> {
|
|
@@ -927,6 +990,20 @@ export class BlockStore {
|
|
|
927
990
|
return result;
|
|
928
991
|
}
|
|
929
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
|
+
|
|
930
1007
|
#computeBlockRange(start: BlockNumber, limit: number): Required<Pick<Range<number>, 'start' | 'limit'>> {
|
|
931
1008
|
if (limit < 1) {
|
|
932
1009
|
throw new Error(`Invalid limit: ${limit}`);
|
|
@@ -6,8 +6,14 @@ import { createLogger } from '@aztec/foundation/log';
|
|
|
6
6
|
import type { AztecAsyncKVStore, CustomRange, StoreSize } from '@aztec/kv-store';
|
|
7
7
|
import { FunctionSelector } from '@aztec/stdlib/abi';
|
|
8
8
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
9
|
-
import {
|
|
10
|
-
|
|
9
|
+
import {
|
|
10
|
+
type BlockData,
|
|
11
|
+
BlockHash,
|
|
12
|
+
CheckpointedL2Block,
|
|
13
|
+
L2Block,
|
|
14
|
+
type ValidateCheckpointResult,
|
|
15
|
+
} from '@aztec/stdlib/block';
|
|
16
|
+
import type { CheckpointData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
11
17
|
import type {
|
|
12
18
|
ContractClassPublic,
|
|
13
19
|
ContractDataSource,
|
|
@@ -25,7 +31,7 @@ import type { UInt64 } from '@aztec/stdlib/types';
|
|
|
25
31
|
import { join } from 'path';
|
|
26
32
|
|
|
27
33
|
import type { InboxMessage } from '../structs/inbox_message.js';
|
|
28
|
-
import { BlockStore, type
|
|
34
|
+
import { BlockStore, type RemoveCheckpointsResult } from './block_store.js';
|
|
29
35
|
import { ContractClassStore } from './contract_class_store.js';
|
|
30
36
|
import { ContractInstanceStore } from './contract_instance_store.js';
|
|
31
37
|
import { LogStore } from './log_store.js';
|
|
@@ -65,15 +71,19 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
65
71
|
constructor(
|
|
66
72
|
private db: AztecAsyncKVStore,
|
|
67
73
|
logsMaxPageSize: number = 1000,
|
|
68
|
-
l1Constants: Pick<L1RollupConstants, 'epochDuration'>,
|
|
69
74
|
) {
|
|
70
|
-
this.#blockStore = new BlockStore(db
|
|
75
|
+
this.#blockStore = new BlockStore(db);
|
|
71
76
|
this.#logStore = new LogStore(db, this.#blockStore, logsMaxPageSize);
|
|
72
77
|
this.#messageStore = new MessageStore(db);
|
|
73
78
|
this.#contractClassStore = new ContractClassStore(db);
|
|
74
79
|
this.#contractInstanceStore = new ContractInstanceStore(db);
|
|
75
80
|
}
|
|
76
81
|
|
|
82
|
+
/** Returns the underlying block store. Used by L2TipsCache. */
|
|
83
|
+
get blockStore(): BlockStore {
|
|
84
|
+
return this.#blockStore;
|
|
85
|
+
}
|
|
86
|
+
|
|
77
87
|
/** Opens a new transaction to the underlying store and runs all operations within it. */
|
|
78
88
|
public transactionAsync<T>(callback: () => Promise<T>): Promise<T> {
|
|
79
89
|
return this.db.transactionAsync(callback);
|
|
@@ -369,6 +379,22 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
369
379
|
return this.#blockStore.getBlockHeaderByArchive(archive);
|
|
370
380
|
}
|
|
371
381
|
|
|
382
|
+
/**
|
|
383
|
+
* Gets block metadata (without tx data) by block number.
|
|
384
|
+
* @param blockNumber - The block number to return.
|
|
385
|
+
*/
|
|
386
|
+
getBlockData(blockNumber: BlockNumber): Promise<BlockData | undefined> {
|
|
387
|
+
return this.#blockStore.getBlockData(blockNumber);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Gets block metadata (without tx data) by archive root.
|
|
392
|
+
* @param archive - The archive root to return.
|
|
393
|
+
*/
|
|
394
|
+
getBlockDataByArchive(archive: Fr): Promise<BlockData | undefined> {
|
|
395
|
+
return this.#blockStore.getBlockDataByArchive(archive);
|
|
396
|
+
}
|
|
397
|
+
|
|
372
398
|
/**
|
|
373
399
|
* Gets a tx effect.
|
|
374
400
|
* @param txHash - The hash of the tx corresponding to the tx effect.
|
|
@@ -383,8 +409,11 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
383
409
|
* @param txHash - The hash of a tx we try to get the receipt for.
|
|
384
410
|
* @returns The requested tx receipt (or undefined if not found).
|
|
385
411
|
*/
|
|
386
|
-
getSettledTxReceipt(
|
|
387
|
-
|
|
412
|
+
getSettledTxReceipt(
|
|
413
|
+
txHash: TxHash,
|
|
414
|
+
l1Constants?: Pick<L1RollupConstants, 'epochDuration'>,
|
|
415
|
+
): Promise<TxReceipt | undefined> {
|
|
416
|
+
return this.#blockStore.getSettledTxReceipt(txHash, l1Constants);
|
|
388
417
|
}
|
|
389
418
|
|
|
390
419
|
/**
|
|
@@ -515,6 +544,22 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
515
544
|
await this.#blockStore.setProvenCheckpointNumber(checkpointNumber);
|
|
516
545
|
}
|
|
517
546
|
|
|
547
|
+
/**
|
|
548
|
+
* Gets the number of the latest finalized checkpoint processed.
|
|
549
|
+
* @returns The number of the latest finalized checkpoint processed.
|
|
550
|
+
*/
|
|
551
|
+
getFinalizedCheckpointNumber(): Promise<CheckpointNumber> {
|
|
552
|
+
return this.#blockStore.getFinalizedCheckpointNumber();
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Stores the number of the latest finalized checkpoint processed.
|
|
557
|
+
* @param checkpointNumber - The number of the latest finalized checkpoint processed.
|
|
558
|
+
*/
|
|
559
|
+
async setFinalizedCheckpointNumber(checkpointNumber: CheckpointNumber) {
|
|
560
|
+
await this.#blockStore.setFinalizedCheckpointNumber(checkpointNumber);
|
|
561
|
+
}
|
|
562
|
+
|
|
518
563
|
async setBlockSynchedL1BlockNumber(l1BlockNumber: bigint) {
|
|
519
564
|
await this.#blockStore.setSynchedL1BlockNumber(l1BlockNumber);
|
|
520
565
|
}
|
|
@@ -618,6 +663,11 @@ export class KVArchiverDataStore implements ContractDataSource {
|
|
|
618
663
|
return this.#blockStore.getCheckpointData(checkpointNumber);
|
|
619
664
|
}
|
|
620
665
|
|
|
666
|
+
/** Returns checkpoint data for all checkpoints whose slot falls within the given range (inclusive). */
|
|
667
|
+
getCheckpointDataForSlotRange(startSlot: SlotNumber, endSlot: SlotNumber): Promise<CheckpointData[]> {
|
|
668
|
+
return this.#blockStore.getCheckpointDataForSlotRange(startSlot, endSlot);
|
|
669
|
+
}
|
|
670
|
+
|
|
621
671
|
/**
|
|
622
672
|
* Gets all blocks that have the given slot number.
|
|
623
673
|
* @param slotNumber - The slot number to search for.
|