@aztec/archiver 4.0.0-nightly.20260111 → 4.0.0-nightly.20260113
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 +97 -32
- 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/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 +137 -40
- 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/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
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { GENESIS_ARCHIVE_ROOT } from '@aztec/constants';
|
|
2
2
|
import { DefaultL1ContractsConfig } from '@aztec/ethereum/config';
|
|
3
|
-
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
4
4
|
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
5
5
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
6
6
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
7
7
|
import { createLogger } from '@aztec/foundation/log';
|
|
8
|
-
import { L2Block, L2BlockHash, PublishedL2Block } from '@aztec/stdlib/block';
|
|
8
|
+
import { CheckpointedL2Block, L2Block, L2BlockHash, PublishedL2Block } from '@aztec/stdlib/block';
|
|
9
9
|
import { L1PublishedData } from '@aztec/stdlib/checkpoint';
|
|
10
10
|
import { EmptyL1RollupConstants, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers';
|
|
11
11
|
import { TxReceipt, TxStatus } from '@aztec/stdlib/tx';
|
|
@@ -15,6 +15,7 @@ import { TxReceipt, TxStatus } from '@aztec/stdlib/tx';
|
|
|
15
15
|
l2Blocks = [];
|
|
16
16
|
provenBlockNumber = 0;
|
|
17
17
|
finalizedBlockNumber = 0;
|
|
18
|
+
checkpointedBlockNumber = 0;
|
|
18
19
|
log = createLogger('archiver:mock_l2_block_source');
|
|
19
20
|
async createBlocks(numBlocks) {
|
|
20
21
|
for(let i = 0; i < numBlocks; i++){
|
|
@@ -41,6 +42,9 @@ import { TxReceipt, TxStatus } from '@aztec/stdlib/tx';
|
|
|
41
42
|
}
|
|
42
43
|
this.finalizedBlockNumber = finalizedBlockNumber;
|
|
43
44
|
}
|
|
45
|
+
setCheckpointedBlockNumber(checkpointedBlockNumber) {
|
|
46
|
+
this.checkpointedBlockNumber = checkpointedBlockNumber;
|
|
47
|
+
}
|
|
44
48
|
/**
|
|
45
49
|
* Method to fetch the rollup contract address at the base-layer.
|
|
46
50
|
* @returns The rollup address.
|
|
@@ -62,9 +66,30 @@ import { TxReceipt, TxStatus } from '@aztec/stdlib/tx';
|
|
|
62
66
|
getProvenBlockNumber() {
|
|
63
67
|
return Promise.resolve(BlockNumber(this.provenBlockNumber));
|
|
64
68
|
}
|
|
65
|
-
getCheckpointedBlock(
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
getCheckpointedBlock(number) {
|
|
70
|
+
if (number > this.checkpointedBlockNumber) {
|
|
71
|
+
return Promise.resolve(undefined);
|
|
72
|
+
}
|
|
73
|
+
const block = this.l2Blocks[number - 1];
|
|
74
|
+
if (!block) {
|
|
75
|
+
return Promise.resolve(undefined);
|
|
76
|
+
}
|
|
77
|
+
const checkpointedBlock = new CheckpointedL2Block(CheckpointNumber(number), block.toL2Block(), new L1PublishedData(BigInt(number), BigInt(number), `0x${number.toString(16).padStart(64, '0')}`), []);
|
|
78
|
+
return Promise.resolve(checkpointedBlock);
|
|
79
|
+
}
|
|
80
|
+
async getCheckpointedBlocks(from, limit, _proven) {
|
|
81
|
+
const result = [];
|
|
82
|
+
for(let i = 0; i < limit; i++){
|
|
83
|
+
const blockNum = from + i;
|
|
84
|
+
if (blockNum > this.checkpointedBlockNumber) {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
const block = await this.getCheckpointedBlock(BlockNumber(blockNum));
|
|
88
|
+
if (block) {
|
|
89
|
+
result.push(block);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
68
93
|
}
|
|
69
94
|
/**
|
|
70
95
|
* Gets an l2 block.
|
|
@@ -105,6 +130,10 @@ import { TxReceipt, TxStatus } from '@aztec/stdlib/tx';
|
|
|
105
130
|
attestations: []
|
|
106
131
|
}));
|
|
107
132
|
}
|
|
133
|
+
async getL2BlocksNew(from, limit, proven) {
|
|
134
|
+
const blocks = await this.getBlocks(from, limit, proven);
|
|
135
|
+
return blocks.map((x)=>x.toL2Block());
|
|
136
|
+
}
|
|
108
137
|
async getPublishedBlockByHash(blockHash) {
|
|
109
138
|
for (const block of this.l2Blocks){
|
|
110
139
|
const hash = await block.hash();
|
|
@@ -199,27 +228,44 @@ import { TxReceipt, TxStatus } from '@aztec/stdlib/tx';
|
|
|
199
228
|
return undefined;
|
|
200
229
|
}
|
|
201
230
|
async getL2Tips() {
|
|
202
|
-
const [latest, proven, finalized] = [
|
|
231
|
+
const [latest, proven, finalized, checkpointed] = [
|
|
203
232
|
await this.getBlockNumber(),
|
|
204
233
|
await this.getProvenBlockNumber(),
|
|
205
|
-
this.finalizedBlockNumber
|
|
234
|
+
this.finalizedBlockNumber,
|
|
235
|
+
this.checkpointedBlockNumber
|
|
206
236
|
];
|
|
207
237
|
const latestBlock = this.l2Blocks[latest - 1];
|
|
208
238
|
const provenBlock = this.l2Blocks[proven - 1];
|
|
209
239
|
const finalizedBlock = this.l2Blocks[finalized - 1];
|
|
240
|
+
const checkpointedBlock = this.l2Blocks[checkpointed - 1];
|
|
241
|
+
const latestBlockId = {
|
|
242
|
+
number: BlockNumber(latest),
|
|
243
|
+
hash: (await latestBlock?.hash())?.toString()
|
|
244
|
+
};
|
|
245
|
+
const provenBlockId = {
|
|
246
|
+
number: BlockNumber(proven),
|
|
247
|
+
hash: (await provenBlock?.hash())?.toString()
|
|
248
|
+
};
|
|
249
|
+
const finalizedBlockId = {
|
|
250
|
+
number: BlockNumber(finalized),
|
|
251
|
+
hash: (await finalizedBlock?.hash())?.toString()
|
|
252
|
+
};
|
|
253
|
+
const checkpointedBlockId = {
|
|
254
|
+
number: BlockNumber(checkpointed),
|
|
255
|
+
hash: (await checkpointedBlock?.hash())?.toString()
|
|
256
|
+
};
|
|
257
|
+
const makeTipId = (blockId)=>({
|
|
258
|
+
block: blockId,
|
|
259
|
+
checkpoint: {
|
|
260
|
+
number: CheckpointNumber(blockId.number),
|
|
261
|
+
hash: blockId.hash
|
|
262
|
+
}
|
|
263
|
+
});
|
|
210
264
|
return {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
proven: {
|
|
216
|
-
number: BlockNumber(proven),
|
|
217
|
-
hash: (await provenBlock?.hash())?.toString()
|
|
218
|
-
},
|
|
219
|
-
finalized: {
|
|
220
|
-
number: BlockNumber(finalized),
|
|
221
|
-
hash: (await finalizedBlock?.hash())?.toString()
|
|
222
|
-
}
|
|
265
|
+
proposed: latestBlockId,
|
|
266
|
+
checkpointed: makeTipId(checkpointedBlockId),
|
|
267
|
+
proven: makeTipId(provenBlockId),
|
|
268
|
+
finalized: makeTipId(finalizedBlockId)
|
|
223
269
|
};
|
|
224
270
|
}
|
|
225
271
|
getL2EpochNumber() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/archiver",
|
|
3
|
-
"version": "4.0.0-nightly.
|
|
3
|
+
"version": "4.0.0-nightly.20260113",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -66,18 +66,18 @@
|
|
|
66
66
|
]
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@aztec/blob-client": "4.0.0-nightly.
|
|
70
|
-
"@aztec/blob-lib": "4.0.0-nightly.
|
|
71
|
-
"@aztec/constants": "4.0.0-nightly.
|
|
72
|
-
"@aztec/epoch-cache": "4.0.0-nightly.
|
|
73
|
-
"@aztec/ethereum": "4.0.0-nightly.
|
|
74
|
-
"@aztec/foundation": "4.0.0-nightly.
|
|
75
|
-
"@aztec/kv-store": "4.0.0-nightly.
|
|
76
|
-
"@aztec/l1-artifacts": "4.0.0-nightly.
|
|
77
|
-
"@aztec/noir-protocol-circuits-types": "4.0.0-nightly.
|
|
78
|
-
"@aztec/protocol-contracts": "4.0.0-nightly.
|
|
79
|
-
"@aztec/stdlib": "4.0.0-nightly.
|
|
80
|
-
"@aztec/telemetry-client": "4.0.0-nightly.
|
|
69
|
+
"@aztec/blob-client": "4.0.0-nightly.20260113",
|
|
70
|
+
"@aztec/blob-lib": "4.0.0-nightly.20260113",
|
|
71
|
+
"@aztec/constants": "4.0.0-nightly.20260113",
|
|
72
|
+
"@aztec/epoch-cache": "4.0.0-nightly.20260113",
|
|
73
|
+
"@aztec/ethereum": "4.0.0-nightly.20260113",
|
|
74
|
+
"@aztec/foundation": "4.0.0-nightly.20260113",
|
|
75
|
+
"@aztec/kv-store": "4.0.0-nightly.20260113",
|
|
76
|
+
"@aztec/l1-artifacts": "4.0.0-nightly.20260113",
|
|
77
|
+
"@aztec/noir-protocol-circuits-types": "4.0.0-nightly.20260113",
|
|
78
|
+
"@aztec/protocol-contracts": "4.0.0-nightly.20260113",
|
|
79
|
+
"@aztec/stdlib": "4.0.0-nightly.20260113",
|
|
80
|
+
"@aztec/telemetry-client": "4.0.0-nightly.20260113",
|
|
81
81
|
"lodash.groupby": "^4.6.0",
|
|
82
82
|
"lodash.omit": "^4.5.0",
|
|
83
83
|
"tslib": "^2.5.0",
|
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,
|
|
@@ -103,7 +105,7 @@ import {
|
|
|
103
105
|
} from './l1/data_retrieval.js';
|
|
104
106
|
import { validateAndLogTraceAvailability } from './l1/validate_trace.js';
|
|
105
107
|
import type { InboxMessage } from './structs/inbox_message.js';
|
|
106
|
-
import { type
|
|
108
|
+
import { type ValidateCheckpointResult, validateCheckpointAttestations } from './validation.js';
|
|
107
109
|
|
|
108
110
|
/**
|
|
109
111
|
* Helper interface to combine all sources this archiver implementation provides.
|
|
@@ -128,7 +130,7 @@ function mapArchiverConfig(config: Partial<ArchiverConfig>) {
|
|
|
128
130
|
return {
|
|
129
131
|
pollingIntervalMs: config.archiverPollingIntervalMS,
|
|
130
132
|
batchSize: config.archiverBatchSize,
|
|
131
|
-
|
|
133
|
+
skipValidateCheckpointAttestations: config.skipValidateCheckpointAttestations,
|
|
132
134
|
maxAllowedEthClientDriftSeconds: config.maxAllowedEthClientDriftSeconds,
|
|
133
135
|
ethereumAllowNoDebugHosts: config.ethereumAllowNoDebugHosts,
|
|
134
136
|
};
|
|
@@ -139,7 +141,7 @@ type RollupStatus = {
|
|
|
139
141
|
provenArchive: Hex;
|
|
140
142
|
pendingCheckpointNumber: CheckpointNumber;
|
|
141
143
|
pendingArchive: Hex;
|
|
142
|
-
validationResult:
|
|
144
|
+
validationResult: ValidateCheckpointResult | undefined;
|
|
143
145
|
lastRetrievedCheckpoint?: PublishedCheckpoint;
|
|
144
146
|
lastL1BlockWithCheckpoint?: bigint;
|
|
145
147
|
};
|
|
@@ -193,7 +195,7 @@ export class Archiver
|
|
|
193
195
|
private config: {
|
|
194
196
|
pollingIntervalMs: number;
|
|
195
197
|
batchSize: number;
|
|
196
|
-
|
|
198
|
+
skipValidateCheckpointAttestations?: boolean;
|
|
197
199
|
maxAllowedEthClientDriftSeconds: number;
|
|
198
200
|
ethereumAllowNoDebugHosts?: boolean;
|
|
199
201
|
},
|
|
@@ -593,14 +595,11 @@ export class Archiver
|
|
|
593
595
|
);
|
|
594
596
|
const newBlocks = blockPromises.filter(isDefined).flat();
|
|
595
597
|
|
|
596
|
-
// TODO(pw/mbps): Don't convert to legacy blocks here
|
|
597
|
-
const blocks: L2Block[] = (await Promise.all(newBlocks.map(x => this.getBlock(x.number)))).filter(isDefined);
|
|
598
|
-
|
|
599
598
|
// Emit an event for listening services to react to the chain prune
|
|
600
599
|
this.emit(L2BlockSourceEvents.L2PruneDetected, {
|
|
601
600
|
type: L2BlockSourceEvents.L2PruneDetected,
|
|
602
601
|
epochNumber: pruneFromEpochNumber,
|
|
603
|
-
blocks,
|
|
602
|
+
blocks: newBlocks,
|
|
604
603
|
});
|
|
605
604
|
|
|
606
605
|
this.log.debug(
|
|
@@ -793,7 +792,8 @@ export class Archiver
|
|
|
793
792
|
@trackSpan('Archiver.handleCheckpoints')
|
|
794
793
|
private async handleCheckpoints(blocksSynchedTo: bigint, currentL1BlockNumber: bigint): Promise<RollupStatus> {
|
|
795
794
|
const localPendingCheckpointNumber = await this.getSynchedCheckpointNumber();
|
|
796
|
-
const initialValidationResult:
|
|
795
|
+
const initialValidationResult: ValidateCheckpointResult | undefined =
|
|
796
|
+
await this.store.getPendingChainValidationStatus();
|
|
797
797
|
const {
|
|
798
798
|
provenCheckpointNumber,
|
|
799
799
|
provenArchive,
|
|
@@ -1008,7 +1008,7 @@ export class Archiver
|
|
|
1008
1008
|
const validCheckpoints: PublishedCheckpoint[] = [];
|
|
1009
1009
|
|
|
1010
1010
|
for (const published of publishedCheckpoints) {
|
|
1011
|
-
const validationResult = this.config.
|
|
1011
|
+
const validationResult = this.config.skipValidateCheckpointAttestations
|
|
1012
1012
|
? { valid: true as const }
|
|
1013
1013
|
: await validateCheckpointAttestations(published, this.epochCache, this.l1constants, this.log);
|
|
1014
1014
|
|
|
@@ -1021,7 +1021,7 @@ export class Archiver
|
|
|
1021
1021
|
rollupStatus.validationResult?.valid !== validationResult.valid ||
|
|
1022
1022
|
(!rollupStatus.validationResult.valid &&
|
|
1023
1023
|
!validationResult.valid &&
|
|
1024
|
-
rollupStatus.validationResult.
|
|
1024
|
+
rollupStatus.validationResult.checkpoint.checkpointNumber === validationResult.checkpoint.checkpointNumber)
|
|
1025
1025
|
) {
|
|
1026
1026
|
rollupStatus.validationResult = validationResult;
|
|
1027
1027
|
}
|
|
@@ -1033,9 +1033,9 @@ export class Archiver
|
|
|
1033
1033
|
...pick(validationResult, 'reason'),
|
|
1034
1034
|
});
|
|
1035
1035
|
|
|
1036
|
-
// Emit event for invalid
|
|
1037
|
-
this.emit(L2BlockSourceEvents.
|
|
1038
|
-
type: L2BlockSourceEvents.
|
|
1036
|
+
// Emit event for invalid checkpoint detection
|
|
1037
|
+
this.emit(L2BlockSourceEvents.InvalidAttestationsCheckpointDetected, {
|
|
1038
|
+
type: L2BlockSourceEvents.InvalidAttestationsCheckpointDetected,
|
|
1039
1039
|
validationResult,
|
|
1040
1040
|
});
|
|
1041
1041
|
|
|
@@ -1362,7 +1362,7 @@ export class Archiver
|
|
|
1362
1362
|
|
|
1363
1363
|
public addCheckpoints(
|
|
1364
1364
|
checkpoints: PublishedCheckpoint[],
|
|
1365
|
-
pendingChainValidationStatus?:
|
|
1365
|
+
pendingChainValidationStatus?: ValidateCheckpointResult,
|
|
1366
1366
|
): Promise<boolean> {
|
|
1367
1367
|
return this.store.addCheckpoints(checkpoints, pendingChainValidationStatus);
|
|
1368
1368
|
}
|
|
@@ -1392,6 +1392,16 @@ export class Archiver
|
|
|
1392
1392
|
return publishedBlock;
|
|
1393
1393
|
}
|
|
1394
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
|
+
|
|
1395
1405
|
public async getBlockHeader(number: BlockNumber | 'latest'): Promise<BlockHeader | undefined> {
|
|
1396
1406
|
if (number === 'latest') {
|
|
1397
1407
|
number = await this.store.getSynchedL2BlockNumber();
|
|
@@ -1407,6 +1417,20 @@ export class Archiver
|
|
|
1407
1417
|
return this.store.getCheckpointedBlock(number);
|
|
1408
1418
|
}
|
|
1409
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
|
+
|
|
1410
1434
|
getCheckpointedBlockByHash(blockHash: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
1411
1435
|
return this.store.getCheckpointedBlockByHash(blockHash);
|
|
1412
1436
|
}
|
|
@@ -1414,6 +1438,9 @@ export class Archiver
|
|
|
1414
1438
|
getProvenBlockNumber(): Promise<BlockNumber> {
|
|
1415
1439
|
return this.store.getProvenBlockNumber();
|
|
1416
1440
|
}
|
|
1441
|
+
getCheckpointedBlockNumber(): Promise<BlockNumber> {
|
|
1442
|
+
return this.store.getCheckpointedL2BlockNumber();
|
|
1443
|
+
}
|
|
1417
1444
|
getCheckpointedBlockByArchive(archive: Fr): Promise<CheckpointedL2Block | undefined> {
|
|
1418
1445
|
return this.store.getCheckpointedBlockByArchive(archive);
|
|
1419
1446
|
}
|
|
@@ -1515,7 +1542,7 @@ export class Archiver
|
|
|
1515
1542
|
return this.store.getDebugFunctionName(address, selector);
|
|
1516
1543
|
}
|
|
1517
1544
|
|
|
1518
|
-
async getPendingChainValidationStatus(): Promise<
|
|
1545
|
+
async getPendingChainValidationStatus(): Promise<ValidateCheckpointResult> {
|
|
1519
1546
|
return (await this.store.getPendingChainValidationStatus()) ?? { valid: true };
|
|
1520
1547
|
}
|
|
1521
1548
|
|
|
@@ -1524,9 +1551,10 @@ export class Archiver
|
|
|
1524
1551
|
}
|
|
1525
1552
|
|
|
1526
1553
|
async getL2Tips(): Promise<L2Tips> {
|
|
1527
|
-
const [latestBlockNumber, provenBlockNumber] = await Promise.all([
|
|
1554
|
+
const [latestBlockNumber, provenBlockNumber, checkpointedBlockNumber] = await Promise.all([
|
|
1528
1555
|
this.getBlockNumber(),
|
|
1529
1556
|
this.getProvenBlockNumber(),
|
|
1557
|
+
this.getCheckpointedBlockNumber(),
|
|
1530
1558
|
] as const);
|
|
1531
1559
|
|
|
1532
1560
|
// TODO(#13569): Compute proper finalized block number based on L1 finalized block.
|
|
@@ -1534,44 +1562,112 @@ export class Archiver
|
|
|
1534
1562
|
// NOTE: update end-to-end/src/e2e_epochs/epochs_empty_blocks.test.ts as that uses finalized blocks in computations
|
|
1535
1563
|
const finalizedBlockNumber = BlockNumber(Math.max(provenBlockNumber - this.l1constants.epochDuration * 2, 0));
|
|
1536
1564
|
|
|
1537
|
-
const
|
|
1538
|
-
latestBlockNumber > 0 ? this.getBlockHeader(latestBlockNumber) : undefined,
|
|
1539
|
-
provenBlockNumber > 0 ? this.getBlockHeader(provenBlockNumber) : undefined,
|
|
1540
|
-
finalizedBlockNumber > 0 ? this.getBlockHeader(finalizedBlockNumber) : undefined,
|
|
1541
|
-
] as const);
|
|
1565
|
+
const beforeInitialblockNumber = BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
1542
1566
|
|
|
1543
|
-
|
|
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) {
|
|
1544
1579
|
throw new Error(`Failed to retrieve latest block header for block ${latestBlockNumber}`);
|
|
1545
1580
|
}
|
|
1546
1581
|
|
|
1547
|
-
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) {
|
|
1548
1590
|
throw new Error(
|
|
1549
|
-
`Failed to retrieve proven
|
|
1591
|
+
`Failed to retrieve proven checkpointed for block ${provenBlockNumber} (latest block is ${latestBlockNumber})`,
|
|
1550
1592
|
);
|
|
1551
1593
|
}
|
|
1552
1594
|
|
|
1553
|
-
if (finalizedBlockNumber >
|
|
1595
|
+
if (finalizedBlockNumber > beforeInitialblockNumber && !finalizedCheckpointedBlock?.block.header) {
|
|
1554
1596
|
throw new Error(
|
|
1555
1597
|
`Failed to retrieve finalized block header for block ${finalizedBlockNumber} (latest block is ${latestBlockNumber})`,
|
|
1556
1598
|
);
|
|
1557
1599
|
}
|
|
1558
1600
|
|
|
1559
1601
|
const latestBlockHeaderHash = (await latestBlockHeader?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
|
|
1560
|
-
const provenBlockHeaderHash = (await
|
|
1561
|
-
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
|
+
};
|
|
1562
1624
|
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
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
|
+
};
|
|
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
|
+
},
|
|
1567
1661
|
};
|
|
1662
|
+
|
|
1663
|
+
return l2Tips;
|
|
1568
1664
|
}
|
|
1569
1665
|
|
|
1570
1666
|
public async rollbackTo(targetL2BlockNumber: BlockNumber): Promise<void> {
|
|
1571
1667
|
// TODO(pw/mbps): This still assumes 1 block per checkpoint
|
|
1572
1668
|
const currentBlocks = await this.getL2Tips();
|
|
1573
|
-
const currentL2Block = currentBlocks.
|
|
1574
|
-
const currentProvenBlock = currentBlocks.proven.number;
|
|
1669
|
+
const currentL2Block = currentBlocks.proposed.number;
|
|
1670
|
+
const currentProvenBlock = currentBlocks.proven.block.number;
|
|
1575
1671
|
|
|
1576
1672
|
if (targetL2BlockNumber >= currentL2Block) {
|
|
1577
1673
|
throw new Error(`Target L2 block ${targetL2BlockNumber} must be less than current L2 block ${currentL2Block}`);
|
|
@@ -1777,6 +1873,7 @@ export class ArchiverStoreHelper
|
|
|
1777
1873
|
| 'addBlocks'
|
|
1778
1874
|
| 'getBlock'
|
|
1779
1875
|
| 'getBlocks'
|
|
1876
|
+
| 'getCheckpointedBlocks'
|
|
1780
1877
|
>
|
|
1781
1878
|
{
|
|
1782
1879
|
#log = createLogger('archiver:block-helper');
|
|
@@ -1935,7 +2032,7 @@ export class ArchiverStoreHelper
|
|
|
1935
2032
|
).every(Boolean);
|
|
1936
2033
|
}
|
|
1937
2034
|
|
|
1938
|
-
public addBlocks(blocks: L2BlockNew[], pendingChainValidationStatus?:
|
|
2035
|
+
public addBlocks(blocks: L2BlockNew[], pendingChainValidationStatus?: ValidateCheckpointResult): Promise<boolean> {
|
|
1939
2036
|
// Add the blocks to the store. Store will throw if the blocks are not in order, there are gaps,
|
|
1940
2037
|
// or if the previous block is not in the store.
|
|
1941
2038
|
return this.store.transactionAsync(async () => {
|
|
@@ -1958,7 +2055,7 @@ export class ArchiverStoreHelper
|
|
|
1958
2055
|
|
|
1959
2056
|
public addCheckpoints(
|
|
1960
2057
|
checkpoints: PublishedCheckpoint[],
|
|
1961
|
-
pendingChainValidationStatus?:
|
|
2058
|
+
pendingChainValidationStatus?: ValidateCheckpointResult,
|
|
1962
2059
|
): Promise<boolean> {
|
|
1963
2060
|
// Add the blocks to the store. Store will throw if the blocks are not in order, there are gaps,
|
|
1964
2061
|
// or if the previous block is not in the store.
|
|
@@ -2102,7 +2199,7 @@ export class ArchiverStoreHelper
|
|
|
2102
2199
|
return this.store.getContractClassLogs(filter);
|
|
2103
2200
|
}
|
|
2104
2201
|
getSynchedL2BlockNumber(): Promise<BlockNumber> {
|
|
2105
|
-
return this.store.
|
|
2202
|
+
return this.store.getLatestBlockNumber();
|
|
2106
2203
|
}
|
|
2107
2204
|
getProvenCheckpointNumber(): Promise<CheckpointNumber> {
|
|
2108
2205
|
return this.store.getProvenCheckpointNumber();
|
|
@@ -2158,10 +2255,10 @@ export class ArchiverStoreHelper
|
|
|
2158
2255
|
getLastL1ToL2Message(): Promise<InboxMessage | undefined> {
|
|
2159
2256
|
return this.store.getLastL1ToL2Message();
|
|
2160
2257
|
}
|
|
2161
|
-
getPendingChainValidationStatus(): Promise<
|
|
2258
|
+
getPendingChainValidationStatus(): Promise<ValidateCheckpointResult | undefined> {
|
|
2162
2259
|
return this.store.getPendingChainValidationStatus();
|
|
2163
2260
|
}
|
|
2164
|
-
setPendingChainValidationStatus(status:
|
|
2261
|
+
setPendingChainValidationStatus(status: ValidateCheckpointResult | undefined): Promise<void> {
|
|
2165
2262
|
this.#log.debug(`Setting pending chain validation status to valid ${status?.valid}`, status);
|
|
2166
2263
|
return this.store.setPendingChainValidationStatus(status);
|
|
2167
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: {
|