@aztec/archiver 0.0.1-commit.6c91f13 → 0.0.1-commit.96bb3f7
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 +14 -14
- package/dest/archiver/archiver.d.ts +13 -10
- package/dest/archiver/archiver.d.ts.map +1 -1
- package/dest/archiver/archiver.js +514 -57
- package/dest/archiver/archiver_store.d.ts +11 -4
- package/dest/archiver/archiver_store.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.d.ts +1 -1
- package/dest/archiver/archiver_store_test_suite.d.ts.map +1 -1
- package/dest/archiver/archiver_store_test_suite.js +6 -6
- package/dest/archiver/config.js +2 -2
- package/dest/archiver/instrumentation.d.ts +1 -1
- package/dest/archiver/instrumentation.d.ts.map +1 -1
- package/dest/archiver/instrumentation.js +15 -63
- package/dest/archiver/kv_archiver_store/block_store.d.ts +11 -4
- package/dest/archiver/kv_archiver_store/block_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/block_store.js +22 -3
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts +5 -4
- package/dest/archiver/kv_archiver_store/kv_archiver_store.d.ts.map +1 -1
- package/dest/archiver/kv_archiver_store/kv_archiver_store.js +3 -0
- package/dest/archiver/l1/calldata_retriever.d.ts +2 -2
- package/dest/archiver/l1/calldata_retriever.d.ts.map +1 -1
- package/dest/archiver/l1/calldata_retriever.js +2 -2
- package/dest/archiver/l1/validate_trace.js +1 -1
- package/dest/archiver/validation.d.ts +4 -4
- package/dest/archiver/validation.d.ts.map +1 -1
- package/dest/archiver/validation.js +1 -1
- package/dest/test/mock_l1_to_l2_message_source.d.ts +2 -2
- package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
- package/dest/test/mock_l1_to_l2_message_source.js +12 -3
- package/dest/test/mock_l2_block_source.d.ts +8 -4
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +65 -19
- package/package.json +13 -13
- package/src/archiver/archiver.ts +179 -71
- package/src/archiver/archiver_store.ts +11 -3
- package/src/archiver/archiver_store_test_suite.ts +12 -13
- package/src/archiver/config.ts +2 -2
- package/src/archiver/instrumentation.ts +14 -63
- package/src/archiver/kv_archiver_store/block_store.ts +35 -7
- package/src/archiver/kv_archiver_store/kv_archiver_store.ts +7 -3
- package/src/archiver/l1/calldata_retriever.ts +2 -2
- package/src/archiver/l1/validate_trace.ts +1 -1
- package/src/archiver/validation.ts +6 -6
- package/src/test/mock_l1_to_l2_message_source.ts +10 -4
- package/src/test/mock_l2_block_source.ts +76 -18
package/src/archiver/archiver.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
2
|
-
import { GENESIS_BLOCK_HEADER_HASH } from '@aztec/constants';
|
|
2
|
+
import { GENESIS_BLOCK_HEADER_HASH, INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
3
3
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
4
4
|
import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
5
5
|
import { BlockTagTooOldError, InboxContract, RollupContract } from '@aztec/ethereum/contracts';
|
|
@@ -33,8 +33,10 @@ import type { FunctionSelector } from '@aztec/stdlib/abi';
|
|
|
33
33
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
34
34
|
import {
|
|
35
35
|
type ArchiverEmitter,
|
|
36
|
+
type CheckpointId,
|
|
36
37
|
CheckpointedL2Block,
|
|
37
38
|
CommitteeAttestation,
|
|
39
|
+
GENESIS_CHECKPOINT_HEADER_HASH,
|
|
38
40
|
L2Block,
|
|
39
41
|
L2BlockNew,
|
|
40
42
|
type L2BlockSink,
|
|
@@ -81,6 +83,7 @@ import {
|
|
|
81
83
|
type TelemetryClient,
|
|
82
84
|
type Traceable,
|
|
83
85
|
type Tracer,
|
|
86
|
+
execInSpan,
|
|
84
87
|
getTelemetryClient,
|
|
85
88
|
trackSpan,
|
|
86
89
|
} from '@aztec/telemetry-client';
|
|
@@ -102,7 +105,7 @@ import {
|
|
|
102
105
|
} from './l1/data_retrieval.js';
|
|
103
106
|
import { validateAndLogTraceAvailability } from './l1/validate_trace.js';
|
|
104
107
|
import type { InboxMessage } from './structs/inbox_message.js';
|
|
105
|
-
import { type
|
|
108
|
+
import { type ValidateCheckpointResult, validateCheckpointAttestations } from './validation.js';
|
|
106
109
|
|
|
107
110
|
/**
|
|
108
111
|
* Helper interface to combine all sources this archiver implementation provides.
|
|
@@ -127,7 +130,7 @@ function mapArchiverConfig(config: Partial<ArchiverConfig>) {
|
|
|
127
130
|
return {
|
|
128
131
|
pollingIntervalMs: config.archiverPollingIntervalMS,
|
|
129
132
|
batchSize: config.archiverBatchSize,
|
|
130
|
-
|
|
133
|
+
skipValidateCheckpointAttestations: config.skipValidateCheckpointAttestations,
|
|
131
134
|
maxAllowedEthClientDriftSeconds: config.maxAllowedEthClientDriftSeconds,
|
|
132
135
|
ethereumAllowNoDebugHosts: config.ethereumAllowNoDebugHosts,
|
|
133
136
|
};
|
|
@@ -138,7 +141,7 @@ type RollupStatus = {
|
|
|
138
141
|
provenArchive: Hex;
|
|
139
142
|
pendingCheckpointNumber: CheckpointNumber;
|
|
140
143
|
pendingArchive: Hex;
|
|
141
|
-
validationResult:
|
|
144
|
+
validationResult: ValidateCheckpointResult | undefined;
|
|
142
145
|
lastRetrievedCheckpoint?: PublishedCheckpoint;
|
|
143
146
|
lastL1BlockWithCheckpoint?: bigint;
|
|
144
147
|
};
|
|
@@ -192,7 +195,7 @@ export class Archiver
|
|
|
192
195
|
private config: {
|
|
193
196
|
pollingIntervalMs: number;
|
|
194
197
|
batchSize: number;
|
|
195
|
-
|
|
198
|
+
skipValidateCheckpointAttestations?: boolean;
|
|
196
199
|
maxAllowedEthClientDriftSeconds: number;
|
|
197
200
|
ethereumAllowNoDebugHosts?: boolean;
|
|
198
201
|
},
|
|
@@ -275,7 +278,7 @@ export class Archiver
|
|
|
275
278
|
slotDuration,
|
|
276
279
|
ethereumSlotDuration,
|
|
277
280
|
proofSubmissionEpochs: Number(proofSubmissionEpochs),
|
|
278
|
-
genesisArchiveRoot: Fr.
|
|
281
|
+
genesisArchiveRoot: Fr.fromString(genesisArchiveRoot.toString()),
|
|
279
282
|
};
|
|
280
283
|
|
|
281
284
|
const opts = merge(
|
|
@@ -409,6 +412,7 @@ export class Archiver
|
|
|
409
412
|
}
|
|
410
413
|
}
|
|
411
414
|
|
|
415
|
+
@trackSpan('Archiver.syncFromL1')
|
|
412
416
|
private async syncFromL1() {
|
|
413
417
|
/**
|
|
414
418
|
* We keep track of three "pointers" to L1 blocks:
|
|
@@ -555,6 +559,7 @@ export class Archiver
|
|
|
555
559
|
}
|
|
556
560
|
|
|
557
561
|
/** Checks if there'd be a reorg for the next checkpoint submission and start pruning now. */
|
|
562
|
+
@trackSpan('Archiver.handleEpochPrune')
|
|
558
563
|
private async handleEpochPrune(
|
|
559
564
|
provenCheckpointNumber: CheckpointNumber,
|
|
560
565
|
currentL1BlockNumber: bigint,
|
|
@@ -590,14 +595,11 @@ export class Archiver
|
|
|
590
595
|
);
|
|
591
596
|
const newBlocks = blockPromises.filter(isDefined).flat();
|
|
592
597
|
|
|
593
|
-
// TODO(pw/mbps): Don't convert to legacy blocks here
|
|
594
|
-
const blocks: L2Block[] = (await Promise.all(newBlocks.map(x => this.getBlock(x.number)))).filter(isDefined);
|
|
595
|
-
|
|
596
598
|
// Emit an event for listening services to react to the chain prune
|
|
597
599
|
this.emit(L2BlockSourceEvents.L2PruneDetected, {
|
|
598
600
|
type: L2BlockSourceEvents.L2PruneDetected,
|
|
599
601
|
epochNumber: pruneFromEpochNumber,
|
|
600
|
-
blocks,
|
|
602
|
+
blocks: newBlocks,
|
|
601
603
|
});
|
|
602
604
|
|
|
603
605
|
this.log.debug(
|
|
@@ -628,6 +630,7 @@ export class Archiver
|
|
|
628
630
|
return [nextStart, nextEnd];
|
|
629
631
|
}
|
|
630
632
|
|
|
633
|
+
@trackSpan('Archiver.handleL1ToL2Messages')
|
|
631
634
|
private async handleL1ToL2Messages(
|
|
632
635
|
messagesSyncPoint: L1BlockId,
|
|
633
636
|
currentL1BlockNumber: bigint,
|
|
@@ -786,23 +789,25 @@ export class Archiver
|
|
|
786
789
|
return Buffer32.fromString(block.hash);
|
|
787
790
|
}
|
|
788
791
|
|
|
792
|
+
@trackSpan('Archiver.handleCheckpoints')
|
|
789
793
|
private async handleCheckpoints(blocksSynchedTo: bigint, currentL1BlockNumber: bigint): Promise<RollupStatus> {
|
|
790
794
|
const localPendingCheckpointNumber = await this.getSynchedCheckpointNumber();
|
|
791
|
-
const initialValidationResult:
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
provenArchive,
|
|
795
|
-
rollupPendingCheckpointNumber,
|
|
796
|
-
pendingArchive,
|
|
797
|
-
archiveForLocalPendingCheckpointNumber,
|
|
798
|
-
] = await this.rollup.status(localPendingCheckpointNumber, { blockNumber: currentL1BlockNumber });
|
|
799
|
-
const provenCheckpointNumber = CheckpointNumber.fromBigInt(rollupProvenCheckpointNumber);
|
|
800
|
-
const pendingCheckpointNumber = CheckpointNumber.fromBigInt(rollupPendingCheckpointNumber);
|
|
801
|
-
const rollupStatus = {
|
|
795
|
+
const initialValidationResult: ValidateCheckpointResult | undefined =
|
|
796
|
+
await this.store.getPendingChainValidationStatus();
|
|
797
|
+
const {
|
|
802
798
|
provenCheckpointNumber,
|
|
803
799
|
provenArchive,
|
|
804
800
|
pendingCheckpointNumber,
|
|
805
801
|
pendingArchive,
|
|
802
|
+
archiveOfMyCheckpoint: archiveForLocalPendingCheckpointNumber,
|
|
803
|
+
} = await execInSpan(this.tracer, 'Archiver.getRollupStatus', () =>
|
|
804
|
+
this.rollup.status(localPendingCheckpointNumber, { blockNumber: currentL1BlockNumber }),
|
|
805
|
+
);
|
|
806
|
+
const rollupStatus: RollupStatus = {
|
|
807
|
+
provenCheckpointNumber,
|
|
808
|
+
provenArchive: provenArchive.toString(),
|
|
809
|
+
pendingCheckpointNumber,
|
|
810
|
+
pendingArchive: pendingArchive.toString(),
|
|
806
811
|
validationResult: initialValidationResult,
|
|
807
812
|
};
|
|
808
813
|
this.log.trace(`Retrieved rollup status at current L1 block ${currentL1BlockNumber}.`, {
|
|
@@ -848,14 +853,12 @@ export class Archiver
|
|
|
848
853
|
|
|
849
854
|
if (
|
|
850
855
|
localCheckpointForDestinationProvenCheckpointNumber &&
|
|
851
|
-
provenArchive
|
|
856
|
+
provenArchive.equals(localCheckpointForDestinationProvenCheckpointNumber.archive.root)
|
|
852
857
|
) {
|
|
853
858
|
const localProvenCheckpointNumber = await this.getProvenCheckpointNumber();
|
|
854
859
|
if (localProvenCheckpointNumber !== provenCheckpointNumber) {
|
|
855
860
|
await this.setProvenCheckpointNumber(provenCheckpointNumber);
|
|
856
|
-
this.log.info(`Updated proven chain to checkpoint ${provenCheckpointNumber}`, {
|
|
857
|
-
provenCheckpointNumber,
|
|
858
|
-
});
|
|
861
|
+
this.log.info(`Updated proven chain to checkpoint ${provenCheckpointNumber}`, { provenCheckpointNumber });
|
|
859
862
|
const provenSlotNumber = localCheckpointForDestinationProvenCheckpointNumber.header.slotNumber;
|
|
860
863
|
const provenEpochNumber: EpochNumber = getEpochAtSlot(provenSlotNumber, this.l1constants);
|
|
861
864
|
const lastBlockNumberInCheckpoint =
|
|
@@ -898,7 +901,7 @@ export class Archiver
|
|
|
898
901
|
}
|
|
899
902
|
|
|
900
903
|
const localPendingArchiveRoot = localPendingCheckpoint.archive.root.toString();
|
|
901
|
-
const noCheckpointSinceLast = localPendingCheckpoint && pendingArchive === localPendingArchiveRoot;
|
|
904
|
+
const noCheckpointSinceLast = localPendingCheckpoint && pendingArchive.toString() === localPendingArchiveRoot;
|
|
902
905
|
if (noCheckpointSinceLast) {
|
|
903
906
|
// We believe the following line causes a problem when we encounter L1 re-orgs.
|
|
904
907
|
// Basically, by setting the synched L1 block number here, we are saying that we have
|
|
@@ -912,7 +915,9 @@ export class Archiver
|
|
|
912
915
|
return rollupStatus;
|
|
913
916
|
}
|
|
914
917
|
|
|
915
|
-
const localPendingCheckpointInChain = archiveForLocalPendingCheckpointNumber
|
|
918
|
+
const localPendingCheckpointInChain = archiveForLocalPendingCheckpointNumber.equals(
|
|
919
|
+
localPendingCheckpoint.archive.root,
|
|
920
|
+
);
|
|
916
921
|
if (!localPendingCheckpointInChain) {
|
|
917
922
|
// If our local pending checkpoint tip is not in the chain on L1 a "prune" must have happened
|
|
918
923
|
// or the L1 have reorged.
|
|
@@ -938,7 +943,7 @@ export class Archiver
|
|
|
938
943
|
archiveLocal: candidateCheckpoint.archive.root.toString(),
|
|
939
944
|
},
|
|
940
945
|
);
|
|
941
|
-
if (archiveAtContract
|
|
946
|
+
if (archiveAtContract.equals(candidateCheckpoint.archive.root)) {
|
|
942
947
|
break;
|
|
943
948
|
}
|
|
944
949
|
tipAfterUnwind--;
|
|
@@ -968,17 +973,19 @@ export class Archiver
|
|
|
968
973
|
this.log.trace(`Retrieving checkpoints from L1 block ${searchStartBlock} to ${searchEndBlock}`);
|
|
969
974
|
|
|
970
975
|
// TODO(md): Retrieve from blob client then from consensus client, then from peers
|
|
971
|
-
const retrievedCheckpoints = await retrieveCheckpointsFromRollup(
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
976
|
+
const retrievedCheckpoints = await execInSpan(this.tracer, 'Archiver.retrieveCheckpointsFromRollup', () =>
|
|
977
|
+
retrieveCheckpointsFromRollup(
|
|
978
|
+
this.rollup.getContract() as GetContractReturnType<typeof RollupAbi, ViemPublicClient>,
|
|
979
|
+
this.publicClient,
|
|
980
|
+
this.debugClient,
|
|
981
|
+
this.blobClient,
|
|
982
|
+
searchStartBlock, // TODO(palla/reorg): If the L2 reorg was due to an L1 reorg, we need to start search earlier
|
|
983
|
+
searchEndBlock,
|
|
984
|
+
this.l1Addresses,
|
|
985
|
+
this.instrumentation,
|
|
986
|
+
this.log,
|
|
987
|
+
!this.initialSyncComplete, // isHistoricalSync
|
|
988
|
+
),
|
|
982
989
|
);
|
|
983
990
|
|
|
984
991
|
if (retrievedCheckpoints.length === 0) {
|
|
@@ -1001,7 +1008,7 @@ export class Archiver
|
|
|
1001
1008
|
const validCheckpoints: PublishedCheckpoint[] = [];
|
|
1002
1009
|
|
|
1003
1010
|
for (const published of publishedCheckpoints) {
|
|
1004
|
-
const validationResult = this.config.
|
|
1011
|
+
const validationResult = this.config.skipValidateCheckpointAttestations
|
|
1005
1012
|
? { valid: true as const }
|
|
1006
1013
|
: await validateCheckpointAttestations(published, this.epochCache, this.l1constants, this.log);
|
|
1007
1014
|
|
|
@@ -1014,7 +1021,7 @@ export class Archiver
|
|
|
1014
1021
|
rollupStatus.validationResult?.valid !== validationResult.valid ||
|
|
1015
1022
|
(!rollupStatus.validationResult.valid &&
|
|
1016
1023
|
!validationResult.valid &&
|
|
1017
|
-
rollupStatus.validationResult.
|
|
1024
|
+
rollupStatus.validationResult.checkpoint.checkpointNumber === validationResult.checkpoint.checkpointNumber)
|
|
1018
1025
|
) {
|
|
1019
1026
|
rollupStatus.validationResult = validationResult;
|
|
1020
1027
|
}
|
|
@@ -1026,9 +1033,9 @@ export class Archiver
|
|
|
1026
1033
|
...pick(validationResult, 'reason'),
|
|
1027
1034
|
});
|
|
1028
1035
|
|
|
1029
|
-
// Emit event for invalid
|
|
1030
|
-
this.emit(L2BlockSourceEvents.
|
|
1031
|
-
type: L2BlockSourceEvents.
|
|
1036
|
+
// Emit event for invalid checkpoint detection
|
|
1037
|
+
this.emit(L2BlockSourceEvents.InvalidAttestationsCheckpointDetected, {
|
|
1038
|
+
type: L2BlockSourceEvents.InvalidAttestationsCheckpointDetected,
|
|
1032
1039
|
validationResult,
|
|
1033
1040
|
});
|
|
1034
1041
|
|
|
@@ -1043,7 +1050,7 @@ export class Archiver
|
|
|
1043
1050
|
// checkpoints we just retrieved.
|
|
1044
1051
|
const l1ToL2Messages = await this.getL1ToL2Messages(published.checkpoint.number);
|
|
1045
1052
|
const computedInHash = computeInHashFromL1ToL2Messages(l1ToL2Messages);
|
|
1046
|
-
const publishedInHash = published.checkpoint.header.
|
|
1053
|
+
const publishedInHash = published.checkpoint.header.inHash;
|
|
1047
1054
|
if (!computedInHash.equals(publishedInHash)) {
|
|
1048
1055
|
this.log.fatal(`Mismatch inHash for checkpoint ${published.checkpoint.number}`, {
|
|
1049
1056
|
checkpointHash: published.checkpoint.hash(),
|
|
@@ -1072,7 +1079,11 @@ export class Archiver
|
|
|
1072
1079
|
try {
|
|
1073
1080
|
const updatedValidationResult =
|
|
1074
1081
|
rollupStatus.validationResult === initialValidationResult ? undefined : rollupStatus.validationResult;
|
|
1075
|
-
const [processDuration] = await elapsed(() =>
|
|
1082
|
+
const [processDuration] = await elapsed(() =>
|
|
1083
|
+
execInSpan(this.tracer, 'Archiver.addCheckpoints', () =>
|
|
1084
|
+
this.addCheckpoints(validCheckpoints, updatedValidationResult),
|
|
1085
|
+
),
|
|
1086
|
+
);
|
|
1076
1087
|
this.instrumentation.processNewBlocks(
|
|
1077
1088
|
processDuration / validCheckpoints.length,
|
|
1078
1089
|
validCheckpoints.flatMap(c => c.checkpoint.blocks),
|
|
@@ -1351,7 +1362,7 @@ export class Archiver
|
|
|
1351
1362
|
|
|
1352
1363
|
public addCheckpoints(
|
|
1353
1364
|
checkpoints: PublishedCheckpoint[],
|
|
1354
|
-
pendingChainValidationStatus?:
|
|
1365
|
+
pendingChainValidationStatus?: ValidateCheckpointResult,
|
|
1355
1366
|
): Promise<boolean> {
|
|
1356
1367
|
return this.store.addCheckpoints(checkpoints, pendingChainValidationStatus);
|
|
1357
1368
|
}
|
|
@@ -1381,6 +1392,16 @@ export class Archiver
|
|
|
1381
1392
|
return publishedBlock;
|
|
1382
1393
|
}
|
|
1383
1394
|
|
|
1395
|
+
public async getL2BlocksNew(from: BlockNumber, limit: number, proven?: boolean): Promise<L2BlockNew[]> {
|
|
1396
|
+
const blocks = await this.store.store.getBlocks(from, limit);
|
|
1397
|
+
|
|
1398
|
+
if (proven === true) {
|
|
1399
|
+
const provenBlockNumber = await this.store.getProvenBlockNumber();
|
|
1400
|
+
return blocks.filter(b => b.number <= provenBlockNumber);
|
|
1401
|
+
}
|
|
1402
|
+
return blocks;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1384
1405
|
public async getBlockHeader(number: BlockNumber | 'latest'): Promise<BlockHeader | undefined> {
|
|
1385
1406
|
if (number === 'latest') {
|
|
1386
1407
|
number = await this.store.getSynchedL2BlockNumber();
|
|
@@ -1396,6 +1417,20 @@ export class Archiver
|
|
|
1396
1417
|
return this.store.getCheckpointedBlock(number);
|
|
1397
1418
|
}
|
|
1398
1419
|
|
|
1420
|
+
public async getCheckpointedBlocks(
|
|
1421
|
+
from: BlockNumber,
|
|
1422
|
+
limit: number,
|
|
1423
|
+
proven?: boolean,
|
|
1424
|
+
): Promise<CheckpointedL2Block[]> {
|
|
1425
|
+
const blocks = await this.store.store.getCheckpointedBlocks(from, limit);
|
|
1426
|
+
|
|
1427
|
+
if (proven === true) {
|
|
1428
|
+
const provenBlockNumber = await this.store.getProvenBlockNumber();
|
|
1429
|
+
return blocks.filter(b => b.block.number <= provenBlockNumber);
|
|
1430
|
+
}
|
|
1431
|
+
return blocks;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1399
1434
|
getCheckpointedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
1400
1435
|
return this.store.getCheckpointedBlockByHash(blockHash);
|
|
1401
1436
|
}
|
|
@@ -1403,6 +1438,9 @@ export class Archiver
|
|
|
1403
1438
|
getProvenBlockNumber(): Promise<BlockNumber> {
|
|
1404
1439
|
return this.store.getProvenBlockNumber();
|
|
1405
1440
|
}
|
|
1441
|
+
getCheckpointedBlockNumber(): Promise<BlockNumber> {
|
|
1442
|
+
return this.store.getCheckpointedL2BlockNumber();
|
|
1443
|
+
}
|
|
1406
1444
|
getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
1407
1445
|
return this.store.getCheckpointedBlockByArchive(archive);
|
|
1408
1446
|
}
|
|
@@ -1504,7 +1542,7 @@ export class Archiver
|
|
|
1504
1542
|
return this.store.getDebugFunctionName(address, selector);
|
|
1505
1543
|
}
|
|
1506
1544
|
|
|
1507
|
-
async getPendingChainValidationStatus(): Promise<
|
|
1545
|
+
async getPendingChainValidationStatus(): Promise<ValidateCheckpointResult> {
|
|
1508
1546
|
return (await this.store.getPendingChainValidationStatus()) ?? { valid: true };
|
|
1509
1547
|
}
|
|
1510
1548
|
|
|
@@ -1513,9 +1551,10 @@ export class Archiver
|
|
|
1513
1551
|
}
|
|
1514
1552
|
|
|
1515
1553
|
async getL2Tips(): Promise<L2Tips> {
|
|
1516
|
-
const [latestBlockNumber, provenBlockNumber] = await Promise.all([
|
|
1554
|
+
const [latestBlockNumber, provenBlockNumber, checkpointedBlockNumber] = await Promise.all([
|
|
1517
1555
|
this.getBlockNumber(),
|
|
1518
1556
|
this.getProvenBlockNumber(),
|
|
1557
|
+
this.getCheckpointedBlockNumber(),
|
|
1519
1558
|
] as const);
|
|
1520
1559
|
|
|
1521
1560
|
// TODO(#13569): Compute proper finalized block number based on L1 finalized block.
|
|
@@ -1523,44 +1562,112 @@ export class Archiver
|
|
|
1523
1562
|
// NOTE: update end-to-end/src/e2e_epochs/epochs_empty_blocks.test.ts as that uses finalized blocks in computations
|
|
1524
1563
|
const finalizedBlockNumber = BlockNumber(Math.max(provenBlockNumber - this.l1constants.epochDuration * 2, 0));
|
|
1525
1564
|
|
|
1526
|
-
const
|
|
1527
|
-
latestBlockNumber > 0 ? this.getBlockHeader(latestBlockNumber) : undefined,
|
|
1528
|
-
provenBlockNumber > 0 ? this.getBlockHeader(provenBlockNumber) : undefined,
|
|
1529
|
-
finalizedBlockNumber > 0 ? this.getBlockHeader(finalizedBlockNumber) : undefined,
|
|
1530
|
-
] as const);
|
|
1565
|
+
const beforeInitialblockNumber = BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
1531
1566
|
|
|
1532
|
-
|
|
1567
|
+
// Get the latest block header and checkpointed blocks for proven, finalised and checkpointed blocks
|
|
1568
|
+
const [latestBlockHeader, provenCheckpointedBlock, finalizedCheckpointedBlock, checkpointedBlock] =
|
|
1569
|
+
await Promise.all([
|
|
1570
|
+
latestBlockNumber > beforeInitialblockNumber ? this.getBlockHeader(latestBlockNumber) : undefined,
|
|
1571
|
+
provenBlockNumber > beforeInitialblockNumber ? this.getCheckpointedBlock(provenBlockNumber) : undefined,
|
|
1572
|
+
finalizedBlockNumber > beforeInitialblockNumber ? this.getCheckpointedBlock(finalizedBlockNumber) : undefined,
|
|
1573
|
+
checkpointedBlockNumber > beforeInitialblockNumber
|
|
1574
|
+
? this.getCheckpointedBlock(checkpointedBlockNumber)
|
|
1575
|
+
: undefined,
|
|
1576
|
+
] as const);
|
|
1577
|
+
|
|
1578
|
+
if (latestBlockNumber > beforeInitialblockNumber && !latestBlockHeader) {
|
|
1533
1579
|
throw new Error(`Failed to retrieve latest block header for block ${latestBlockNumber}`);
|
|
1534
1580
|
}
|
|
1535
1581
|
|
|
1536
|
-
if
|
|
1582
|
+
// Checkpointed blocks must exist for proven, finalized and checkpointed tips if they are beyond the initial block number.
|
|
1583
|
+
if (checkpointedBlockNumber > beforeInitialblockNumber && !checkpointedBlock?.block.header) {
|
|
1584
|
+
throw new Error(
|
|
1585
|
+
`Failed to retrieve checkpointed block header for block ${checkpointedBlockNumber} (latest block is ${latestBlockNumber})`,
|
|
1586
|
+
);
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
if (provenBlockNumber > beforeInitialblockNumber && !provenCheckpointedBlock?.block.header) {
|
|
1537
1590
|
throw new Error(
|
|
1538
|
-
`Failed to retrieve proven
|
|
1591
|
+
`Failed to retrieve proven checkpointed for block ${provenBlockNumber} (latest block is ${latestBlockNumber})`,
|
|
1539
1592
|
);
|
|
1540
1593
|
}
|
|
1541
1594
|
|
|
1542
|
-
if (finalizedBlockNumber >
|
|
1595
|
+
if (finalizedBlockNumber > beforeInitialblockNumber && !finalizedCheckpointedBlock?.block.header) {
|
|
1543
1596
|
throw new Error(
|
|
1544
1597
|
`Failed to retrieve finalized block header for block ${finalizedBlockNumber} (latest block is ${latestBlockNumber})`,
|
|
1545
1598
|
);
|
|
1546
1599
|
}
|
|
1547
1600
|
|
|
1548
1601
|
const latestBlockHeaderHash = (await latestBlockHeader?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
|
|
1549
|
-
const provenBlockHeaderHash = (await
|
|
1550
|
-
const finalizedBlockHeaderHash =
|
|
1602
|
+
const provenBlockHeaderHash = (await provenCheckpointedBlock?.block.header?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
|
|
1603
|
+
const finalizedBlockHeaderHash =
|
|
1604
|
+
(await finalizedCheckpointedBlock?.block.header?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
|
|
1605
|
+
const checkpointedBlockHeaderHash = (await checkpointedBlock?.block.header?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
|
|
1606
|
+
|
|
1607
|
+
// Now attempt to retrieve checkpoints for proven, finalised and checkpointed blocks
|
|
1608
|
+
const [[provenBlockCheckpoint], [finalizedBlockCheckpoint], [checkpointedBlockCheckpoint]] = await Promise.all([
|
|
1609
|
+
provenCheckpointedBlock !== undefined
|
|
1610
|
+
? await this.getPublishedCheckpoints(provenCheckpointedBlock?.checkpointNumber, 1)
|
|
1611
|
+
: [undefined],
|
|
1612
|
+
finalizedCheckpointedBlock !== undefined
|
|
1613
|
+
? await this.getPublishedCheckpoints(finalizedCheckpointedBlock?.checkpointNumber, 1)
|
|
1614
|
+
: [undefined],
|
|
1615
|
+
checkpointedBlock !== undefined
|
|
1616
|
+
? await this.getPublishedCheckpoints(checkpointedBlock?.checkpointNumber, 1)
|
|
1617
|
+
: [undefined],
|
|
1618
|
+
]);
|
|
1619
|
+
|
|
1620
|
+
const initialcheckpointId: CheckpointId = {
|
|
1621
|
+
number: CheckpointNumber.ZERO,
|
|
1622
|
+
hash: GENESIS_CHECKPOINT_HEADER_HASH.toString(),
|
|
1623
|
+
};
|
|
1551
1624
|
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1625
|
+
const makeCheckpointId = (checkpoint: PublishedCheckpoint | undefined) => {
|
|
1626
|
+
if (checkpoint === undefined) {
|
|
1627
|
+
return initialcheckpointId;
|
|
1628
|
+
}
|
|
1629
|
+
return {
|
|
1630
|
+
number: checkpoint.checkpoint.number,
|
|
1631
|
+
hash: checkpoint.checkpoint.hash().toString(),
|
|
1632
|
+
};
|
|
1556
1633
|
};
|
|
1634
|
+
|
|
1635
|
+
const l2Tips: L2Tips = {
|
|
1636
|
+
proposed: {
|
|
1637
|
+
number: latestBlockNumber,
|
|
1638
|
+
hash: latestBlockHeaderHash.toString(),
|
|
1639
|
+
},
|
|
1640
|
+
proven: {
|
|
1641
|
+
block: {
|
|
1642
|
+
number: provenBlockNumber,
|
|
1643
|
+
hash: provenBlockHeaderHash.toString(),
|
|
1644
|
+
},
|
|
1645
|
+
checkpoint: makeCheckpointId(provenBlockCheckpoint),
|
|
1646
|
+
},
|
|
1647
|
+
finalized: {
|
|
1648
|
+
block: {
|
|
1649
|
+
number: finalizedBlockNumber,
|
|
1650
|
+
hash: finalizedBlockHeaderHash.toString(),
|
|
1651
|
+
},
|
|
1652
|
+
checkpoint: makeCheckpointId(finalizedBlockCheckpoint),
|
|
1653
|
+
},
|
|
1654
|
+
checkpointed: {
|
|
1655
|
+
block: {
|
|
1656
|
+
number: checkpointedBlockNumber,
|
|
1657
|
+
hash: checkpointedBlockHeaderHash.toString(),
|
|
1658
|
+
},
|
|
1659
|
+
checkpoint: makeCheckpointId(checkpointedBlockCheckpoint),
|
|
1660
|
+
},
|
|
1661
|
+
};
|
|
1662
|
+
|
|
1663
|
+
return l2Tips;
|
|
1557
1664
|
}
|
|
1558
1665
|
|
|
1559
1666
|
public async rollbackTo(targetL2BlockNumber: BlockNumber): Promise<void> {
|
|
1560
1667
|
// TODO(pw/mbps): This still assumes 1 block per checkpoint
|
|
1561
1668
|
const currentBlocks = await this.getL2Tips();
|
|
1562
|
-
const currentL2Block = currentBlocks.
|
|
1563
|
-
const currentProvenBlock = currentBlocks.proven.number;
|
|
1669
|
+
const currentL2Block = currentBlocks.proposed.number;
|
|
1670
|
+
const currentProvenBlock = currentBlocks.proven.block.number;
|
|
1564
1671
|
|
|
1565
1672
|
if (targetL2BlockNumber >= currentL2Block) {
|
|
1566
1673
|
throw new Error(`Target L2 block ${targetL2BlockNumber} must be less than current L2 block ${currentL2Block}`);
|
|
@@ -1766,6 +1873,7 @@ export class ArchiverStoreHelper
|
|
|
1766
1873
|
| 'addBlocks'
|
|
1767
1874
|
| 'getBlock'
|
|
1768
1875
|
| 'getBlocks'
|
|
1876
|
+
| 'getCheckpointedBlocks'
|
|
1769
1877
|
>
|
|
1770
1878
|
{
|
|
1771
1879
|
#log = createLogger('archiver:block-helper');
|
|
@@ -1924,7 +2032,7 @@ export class ArchiverStoreHelper
|
|
|
1924
2032
|
).every(Boolean);
|
|
1925
2033
|
}
|
|
1926
2034
|
|
|
1927
|
-
public addBlocks(blocks: L2BlockNew[], pendingChainValidationStatus?:
|
|
2035
|
+
public addBlocks(blocks: L2BlockNew[], pendingChainValidationStatus?: ValidateCheckpointResult): Promise<boolean> {
|
|
1928
2036
|
// Add the blocks to the store. Store will throw if the blocks are not in order, there are gaps,
|
|
1929
2037
|
// or if the previous block is not in the store.
|
|
1930
2038
|
return this.store.transactionAsync(async () => {
|
|
@@ -1947,7 +2055,7 @@ export class ArchiverStoreHelper
|
|
|
1947
2055
|
|
|
1948
2056
|
public addCheckpoints(
|
|
1949
2057
|
checkpoints: PublishedCheckpoint[],
|
|
1950
|
-
pendingChainValidationStatus?:
|
|
2058
|
+
pendingChainValidationStatus?: ValidateCheckpointResult,
|
|
1951
2059
|
): Promise<boolean> {
|
|
1952
2060
|
// Add the blocks to the store. Store will throw if the blocks are not in order, there are gaps,
|
|
1953
2061
|
// or if the previous block is not in the store.
|
|
@@ -2091,7 +2199,7 @@ export class ArchiverStoreHelper
|
|
|
2091
2199
|
return this.store.getContractClassLogs(filter);
|
|
2092
2200
|
}
|
|
2093
2201
|
getSynchedL2BlockNumber(): Promise<BlockNumber> {
|
|
2094
|
-
return this.store.
|
|
2202
|
+
return this.store.getLatestBlockNumber();
|
|
2095
2203
|
}
|
|
2096
2204
|
getProvenCheckpointNumber(): Promise<CheckpointNumber> {
|
|
2097
2205
|
return this.store.getProvenCheckpointNumber();
|
|
@@ -2147,10 +2255,10 @@ export class ArchiverStoreHelper
|
|
|
2147
2255
|
getLastL1ToL2Message(): Promise<InboxMessage | undefined> {
|
|
2148
2256
|
return this.store.getLastL1ToL2Message();
|
|
2149
2257
|
}
|
|
2150
|
-
getPendingChainValidationStatus(): Promise<
|
|
2258
|
+
getPendingChainValidationStatus(): Promise<ValidateCheckpointResult | undefined> {
|
|
2151
2259
|
return this.store.getPendingChainValidationStatus();
|
|
2152
2260
|
}
|
|
2153
|
-
setPendingChainValidationStatus(status:
|
|
2261
|
+
setPendingChainValidationStatus(status: ValidateCheckpointResult | undefined): Promise<void> {
|
|
2154
2262
|
this.#log.debug(`Setting pending chain validation status to valid ${status?.valid}`, status);
|
|
2155
2263
|
return this.store.setPendingChainValidationStatus(status);
|
|
2156
2264
|
}
|
|
@@ -4,7 +4,7 @@ import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
4
4
|
import type { CustomRange } from '@aztec/kv-store';
|
|
5
5
|
import type { FunctionSelector } from '@aztec/stdlib/abi';
|
|
6
6
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
7
|
-
import type { CheckpointedL2Block, L2BlockNew,
|
|
7
|
+
import type { CheckpointedL2Block, L2BlockNew, ValidateCheckpointResult } from '@aztec/stdlib/block';
|
|
8
8
|
import type { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
9
9
|
import type {
|
|
10
10
|
ContractClassPublic,
|
|
@@ -85,6 +85,14 @@ export interface ArchiverDataStore {
|
|
|
85
85
|
*/
|
|
86
86
|
getCheckpointedBlock(number: number): Promise<CheckpointedL2Block | undefined>;
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Gets up to `limit` amount of checkpointed L2 blocks starting from `from`.
|
|
90
|
+
* @param from - Number of the first block to return (inclusive).
|
|
91
|
+
* @param limit - The number of blocks to return.
|
|
92
|
+
* @returns The requested checkpointed L2 blocks.
|
|
93
|
+
*/
|
|
94
|
+
getCheckpointedBlocks(from: number, limit: number): Promise<CheckpointedL2Block[]>;
|
|
95
|
+
|
|
88
96
|
/**
|
|
89
97
|
* Returns the block for the given hash, or undefined if not exists.
|
|
90
98
|
* @param blockHash - The block hash to return.
|
|
@@ -365,8 +373,8 @@ export interface ArchiverDataStore {
|
|
|
365
373
|
getLastL1ToL2Message(): Promise<InboxMessage | undefined>;
|
|
366
374
|
|
|
367
375
|
/** Returns the last synced validation status of the pending chain. */
|
|
368
|
-
getPendingChainValidationStatus(): Promise<
|
|
376
|
+
getPendingChainValidationStatus(): Promise<ValidateCheckpointResult | undefined>;
|
|
369
377
|
|
|
370
378
|
/** Sets the last synced validation status of the pending chain. */
|
|
371
|
-
setPendingChainValidationStatus(status:
|
|
379
|
+
setPendingChainValidationStatus(status: ValidateCheckpointResult | undefined): Promise<void>;
|
|
372
380
|
}
|
|
@@ -20,10 +20,9 @@ import {
|
|
|
20
20
|
EthAddress,
|
|
21
21
|
L2BlockHash,
|
|
22
22
|
L2BlockNew,
|
|
23
|
-
type
|
|
24
|
-
randomBlockInfo,
|
|
23
|
+
type ValidateCheckpointResult,
|
|
25
24
|
} from '@aztec/stdlib/block';
|
|
26
|
-
import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
25
|
+
import { Checkpoint, L1PublishedData, PublishedCheckpoint, randomCheckpointInfo } from '@aztec/stdlib/checkpoint';
|
|
27
26
|
import {
|
|
28
27
|
type ContractClassPublic,
|
|
29
28
|
type ContractInstanceWithAddress,
|
|
@@ -2756,7 +2755,7 @@ export function describeArchiverDataStore(
|
|
|
2756
2755
|
});
|
|
2757
2756
|
|
|
2758
2757
|
it('should store and retrieve a valid validation status', async () => {
|
|
2759
|
-
const validStatus:
|
|
2758
|
+
const validStatus: ValidateCheckpointResult = { valid: true };
|
|
2760
2759
|
|
|
2761
2760
|
await store.setPendingChainValidationStatus(validStatus);
|
|
2762
2761
|
const retrievedStatus = await store.getPendingChainValidationStatus();
|
|
@@ -2765,9 +2764,9 @@ export function describeArchiverDataStore(
|
|
|
2765
2764
|
});
|
|
2766
2765
|
|
|
2767
2766
|
it('should store and retrieve an invalid validation status with insufficient attestations', async () => {
|
|
2768
|
-
const invalidStatus:
|
|
2767
|
+
const invalidStatus: ValidateCheckpointResult = {
|
|
2769
2768
|
valid: false,
|
|
2770
|
-
|
|
2769
|
+
checkpoint: randomCheckpointInfo(1),
|
|
2771
2770
|
committee: [EthAddress.random(), EthAddress.random()],
|
|
2772
2771
|
epoch: EpochNumber(123),
|
|
2773
2772
|
seed: 456n,
|
|
@@ -2783,9 +2782,9 @@ export function describeArchiverDataStore(
|
|
|
2783
2782
|
});
|
|
2784
2783
|
|
|
2785
2784
|
it('should store and retrieve an invalid validation status with invalid attestation', async () => {
|
|
2786
|
-
const invalidStatus:
|
|
2785
|
+
const invalidStatus: ValidateCheckpointResult = {
|
|
2787
2786
|
valid: false,
|
|
2788
|
-
|
|
2787
|
+
checkpoint: randomCheckpointInfo(2),
|
|
2789
2788
|
committee: [EthAddress.random()],
|
|
2790
2789
|
attestors: [EthAddress.random()],
|
|
2791
2790
|
epoch: EpochNumber(789),
|
|
@@ -2802,10 +2801,10 @@ export function describeArchiverDataStore(
|
|
|
2802
2801
|
});
|
|
2803
2802
|
|
|
2804
2803
|
it('should overwrite existing status when setting a new one', async () => {
|
|
2805
|
-
const firstStatus:
|
|
2806
|
-
const secondStatus:
|
|
2804
|
+
const firstStatus: ValidateCheckpointResult = { valid: true };
|
|
2805
|
+
const secondStatus: ValidateCheckpointResult = {
|
|
2807
2806
|
valid: false,
|
|
2808
|
-
|
|
2807
|
+
checkpoint: randomCheckpointInfo(3),
|
|
2809
2808
|
committee: [EthAddress.random()],
|
|
2810
2809
|
epoch: EpochNumber(999),
|
|
2811
2810
|
seed: 888n,
|
|
@@ -2822,9 +2821,9 @@ export function describeArchiverDataStore(
|
|
|
2822
2821
|
});
|
|
2823
2822
|
|
|
2824
2823
|
it('should handle empty committee and attestations arrays', async () => {
|
|
2825
|
-
const statusWithEmptyArrays:
|
|
2824
|
+
const statusWithEmptyArrays: ValidateCheckpointResult = {
|
|
2826
2825
|
valid: false,
|
|
2827
|
-
|
|
2826
|
+
checkpoint: randomCheckpointInfo(4),
|
|
2828
2827
|
committee: [],
|
|
2829
2828
|
epoch: EpochNumber(0),
|
|
2830
2829
|
seed: 0n,
|
package/src/archiver/config.ts
CHANGED
|
@@ -46,8 +46,8 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
|
|
|
46
46
|
parseEnv: (val: string | undefined) => (val ? +val : undefined),
|
|
47
47
|
description: 'The maximum possible size of the archiver DB in KB. Overwrites the general dataStoreMapSizeKb.',
|
|
48
48
|
},
|
|
49
|
-
|
|
50
|
-
description: '
|
|
49
|
+
skipValidateCheckpointAttestations: {
|
|
50
|
+
description: 'Skip validating checkpoint attestations (for testing purposes only)',
|
|
51
51
|
...booleanConfigHelper(false),
|
|
52
52
|
},
|
|
53
53
|
maxAllowedEthClientDriftSeconds: {
|