@aztec/archiver 0.0.1-commit.381b1a9 → 0.0.1-commit.3a4ae741b
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 +1 -2
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +28 -15
- package/dest/config.d.ts +3 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +2 -1
- package/dest/errors.d.ts +21 -9
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +27 -14
- package/dest/factory.d.ts +3 -3
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +16 -13
- package/dest/l1/data_retrieval.d.ts +6 -3
- package/dest/l1/data_retrieval.d.ts.map +1 -1
- package/dest/l1/data_retrieval.js +12 -6
- package/dest/modules/data_source_base.d.ts +3 -3
- package/dest/modules/data_source_base.d.ts.map +1 -1
- package/dest/modules/data_source_base.js +4 -4
- package/dest/modules/data_store_updater.d.ts +7 -10
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +44 -74
- package/dest/modules/instrumentation.d.ts +12 -1
- package/dest/modules/instrumentation.d.ts.map +1 -1
- package/dest/modules/instrumentation.js +10 -0
- package/dest/modules/l1_synchronizer.d.ts +1 -1
- package/dest/modules/l1_synchronizer.d.ts.map +1 -1
- package/dest/modules/l1_synchronizer.js +17 -9
- package/dest/store/block_store.d.ts +5 -5
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +30 -48
- package/dest/store/contract_class_store.d.ts +2 -3
- package/dest/store/contract_class_store.d.ts.map +1 -1
- package/dest/store/contract_class_store.js +7 -67
- package/dest/store/contract_instance_store.d.ts +1 -1
- package/dest/store/contract_instance_store.d.ts.map +1 -1
- package/dest/store/contract_instance_store.js +6 -2
- package/dest/store/kv_archiver_store.d.ts +16 -16
- package/dest/store/kv_archiver_store.d.ts.map +1 -1
- package/dest/store/kv_archiver_store.js +18 -17
- package/dest/store/log_store.d.ts +6 -3
- package/dest/store/log_store.d.ts.map +1 -1
- package/dest/store/log_store.js +45 -6
- package/dest/store/message_store.d.ts +5 -1
- package/dest/store/message_store.d.ts.map +1 -1
- package/dest/store/message_store.js +13 -0
- package/dest/test/fake_l1_state.d.ts +1 -1
- package/dest/test/fake_l1_state.d.ts.map +1 -1
- package/dest/test/fake_l1_state.js +11 -3
- package/package.json +13 -13
- package/src/archiver.ts +31 -13
- package/src/config.ts +8 -1
- package/src/errors.ts +40 -24
- package/src/factory.ts +18 -11
- package/src/l1/data_retrieval.ts +17 -9
- package/src/modules/data_source_base.ts +8 -3
- package/src/modules/data_store_updater.ts +45 -104
- package/src/modules/instrumentation.ts +20 -0
- package/src/modules/l1_synchronizer.ts +27 -20
- package/src/store/block_store.ts +31 -56
- package/src/store/contract_class_store.ts +8 -106
- package/src/store/contract_instance_store.ts +8 -5
- package/src/store/kv_archiver_store.ts +21 -28
- package/src/store/log_store.ts +60 -15
- package/src/store/message_store.ts +19 -0
- package/src/test/fake_l1_state.ts +15 -5
|
@@ -165,16 +165,21 @@ export abstract class ArchiverDataSourceBase
|
|
|
165
165
|
return (await this.store.getPendingChainValidationStatus()) ?? { valid: true };
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
public getPrivateLogsByTags(
|
|
169
|
-
|
|
168
|
+
public getPrivateLogsByTags(
|
|
169
|
+
tags: SiloedTag[],
|
|
170
|
+
page?: number,
|
|
171
|
+
upToBlockNumber?: BlockNumber,
|
|
172
|
+
): Promise<TxScopedL2Log[][]> {
|
|
173
|
+
return this.store.getPrivateLogsByTags(tags, page, upToBlockNumber);
|
|
170
174
|
}
|
|
171
175
|
|
|
172
176
|
public getPublicLogsByTagsFromContract(
|
|
173
177
|
contractAddress: AztecAddress,
|
|
174
178
|
tags: Tag[],
|
|
175
179
|
page?: number,
|
|
180
|
+
upToBlockNumber?: BlockNumber,
|
|
176
181
|
): Promise<TxScopedL2Log[][]> {
|
|
177
|
-
return this.store.getPublicLogsByTagsFromContract(contractAddress, tags, page);
|
|
182
|
+
return this.store.getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber);
|
|
178
183
|
}
|
|
179
184
|
|
|
180
185
|
public getPublicLogs(filter: LogFilter): Promise<GetPublicLogsResponse> {
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import { filterAsync } from '@aztec/foundation/collection';
|
|
3
|
-
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
5
|
-
import {
|
|
6
|
-
ContractClassPublishedEvent,
|
|
7
|
-
PrivateFunctionBroadcastedEvent,
|
|
8
|
-
UtilityFunctionBroadcastedEvent,
|
|
9
|
-
} from '@aztec/protocol-contracts/class-registry';
|
|
4
|
+
import { ContractClassPublishedEvent } from '@aztec/protocol-contracts/class-registry';
|
|
10
5
|
import {
|
|
11
6
|
ContractInstancePublishedEvent,
|
|
12
7
|
ContractInstanceUpdatedEvent,
|
|
@@ -14,18 +9,13 @@ import {
|
|
|
14
9
|
import type { L2Block, ValidateCheckpointResult } from '@aztec/stdlib/block';
|
|
15
10
|
import { type PublishedCheckpoint, validateCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
16
11
|
import {
|
|
17
|
-
type
|
|
18
|
-
type UtilityFunctionWithMembershipProof,
|
|
12
|
+
type ContractClassPublicWithCommitment,
|
|
19
13
|
computeContractAddressFromInstance,
|
|
20
|
-
|
|
21
|
-
isValidPrivateFunctionMembershipProof,
|
|
22
|
-
isValidUtilityFunctionMembershipProof,
|
|
14
|
+
computeContractClassId,
|
|
23
15
|
} from '@aztec/stdlib/contract';
|
|
24
16
|
import type { ContractClassLog, PrivateLog, PublicLog } from '@aztec/stdlib/logs';
|
|
25
17
|
import type { UInt64 } from '@aztec/stdlib/types';
|
|
26
18
|
|
|
27
|
-
import groupBy from 'lodash.groupby';
|
|
28
|
-
|
|
29
19
|
import type { KVArchiverDataStore } from '../store/kv_archiver_store.js';
|
|
30
20
|
import type { L2TipsCache } from '../store/l2_tips_cache.js';
|
|
31
21
|
|
|
@@ -54,29 +44,28 @@ export class ArchiverDataStoreUpdater {
|
|
|
54
44
|
) {}
|
|
55
45
|
|
|
56
46
|
/**
|
|
57
|
-
* Adds proposed
|
|
58
|
-
*
|
|
59
|
-
* Extracts ContractClassPublished, ContractInstancePublished, ContractInstanceUpdated events
|
|
60
|
-
* and individually broadcasted functions from the block logs.
|
|
47
|
+
* Adds a proposed block to the store with contract class/instance extraction from logs.
|
|
48
|
+
* This is an uncheckpointed block that has been proposed by the sequencer but not yet included in a checkpoint on L1.
|
|
49
|
+
* Extracts ContractClassPublished, ContractInstancePublished, ContractInstanceUpdated events from the block logs.
|
|
61
50
|
*
|
|
62
|
-
* @param
|
|
51
|
+
* @param block - The proposed L2 block to add.
|
|
63
52
|
* @param pendingChainValidationStatus - Optional validation status to set.
|
|
64
53
|
* @returns True if the operation is successful.
|
|
65
54
|
*/
|
|
66
|
-
public async
|
|
67
|
-
|
|
55
|
+
public async addProposedBlock(
|
|
56
|
+
block: L2Block,
|
|
68
57
|
pendingChainValidationStatus?: ValidateCheckpointResult,
|
|
69
58
|
): Promise<boolean> {
|
|
70
59
|
const result = await this.store.transactionAsync(async () => {
|
|
71
|
-
await this.store.
|
|
60
|
+
await this.store.addProposedBlock(block);
|
|
72
61
|
|
|
73
62
|
const opResults = await Promise.all([
|
|
74
63
|
// Update the pending chain validation status if provided
|
|
75
64
|
pendingChainValidationStatus && this.store.setPendingChainValidationStatus(pendingChainValidationStatus),
|
|
76
|
-
// Add any logs emitted during the retrieved
|
|
77
|
-
this.store.addLogs(
|
|
78
|
-
// Unroll all logs emitted during the retrieved
|
|
79
|
-
|
|
65
|
+
// Add any logs emitted during the retrieved block
|
|
66
|
+
this.store.addLogs([block]),
|
|
67
|
+
// Unroll all logs emitted during the retrieved block and extract any contract classes and instances from it
|
|
68
|
+
this.addContractDataToDb(block),
|
|
80
69
|
]);
|
|
81
70
|
|
|
82
71
|
await this.l2TipsCache?.refresh();
|
|
@@ -89,8 +78,7 @@ export class ArchiverDataStoreUpdater {
|
|
|
89
78
|
* Reconciles local blocks with incoming checkpoints from L1.
|
|
90
79
|
* Adds new checkpoints to the store with contract class/instance extraction from logs.
|
|
91
80
|
* Prunes any local blocks that conflict with checkpoint data (by comparing archive roots).
|
|
92
|
-
* Extracts ContractClassPublished, ContractInstancePublished, ContractInstanceUpdated events
|
|
93
|
-
* and individually broadcasted functions from the checkpoint block logs.
|
|
81
|
+
* Extracts ContractClassPublished, ContractInstancePublished, ContractInstanceUpdated events from the checkpoint block logs.
|
|
94
82
|
*
|
|
95
83
|
* @param checkpoints - The published checkpoints to add.
|
|
96
84
|
* @param pendingChainValidationStatus - Optional validation status to set.
|
|
@@ -110,7 +98,7 @@ export class ArchiverDataStoreUpdater {
|
|
|
110
98
|
|
|
111
99
|
await this.store.addCheckpoints(checkpoints);
|
|
112
100
|
|
|
113
|
-
// Filter out blocks that were already inserted via
|
|
101
|
+
// Filter out blocks that were already inserted via addProposedBlock() to avoid duplicating logs/contract data
|
|
114
102
|
const newBlocks = checkpoints
|
|
115
103
|
.flatMap((ch: PublishedCheckpoint) => ch.checkpoint.blocks)
|
|
116
104
|
.filter(b => lastAlreadyInsertedBlockNumber === undefined || b.number > lastAlreadyInsertedBlockNumber);
|
|
@@ -315,9 +303,6 @@ export class ArchiverDataStoreUpdater {
|
|
|
315
303
|
this.updatePublishedContractClasses(contractClassLogs, block.number, operation),
|
|
316
304
|
this.updateDeployedContractInstances(privateLogs, block.number, operation),
|
|
317
305
|
this.updateUpdatedContractInstances(publicLogs, block.header.globalVariables.timestamp, operation),
|
|
318
|
-
operation === Operation.Store
|
|
319
|
-
? this.storeBroadcastedIndividualFunctions(contractClassLogs, block.number)
|
|
320
|
-
: Promise.resolve(true),
|
|
321
306
|
])
|
|
322
307
|
).every(Boolean);
|
|
323
308
|
}
|
|
@@ -334,18 +319,37 @@ export class ArchiverDataStoreUpdater {
|
|
|
334
319
|
.filter(log => ContractClassPublishedEvent.isContractClassPublishedEvent(log))
|
|
335
320
|
.map(log => ContractClassPublishedEvent.fromLog(log));
|
|
336
321
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
contractClasses.
|
|
340
|
-
|
|
341
|
-
// TODO: Will probably want to create some worker threads to compute these bytecode commitments as they are expensive
|
|
342
|
-
const commitments = await Promise.all(
|
|
343
|
-
contractClasses.map(c => computePublicBytecodeCommitment(c.packedBytecode)),
|
|
344
|
-
);
|
|
345
|
-
return await this.store.addContractClasses(contractClasses, commitments, blockNum);
|
|
346
|
-
} else if (operation == Operation.Delete) {
|
|
322
|
+
if (operation == Operation.Delete) {
|
|
323
|
+
const contractClasses = contractClassPublishedEvents.map(e => e.toContractClassPublic());
|
|
324
|
+
if (contractClasses.length > 0) {
|
|
325
|
+
contractClasses.forEach(c => this.log.verbose(`${Operation[operation]} contract class ${c.id.toString()}`));
|
|
347
326
|
return await this.store.deleteContractClasses(contractClasses, blockNum);
|
|
348
327
|
}
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Compute bytecode commitments and validate class IDs in a single pass.
|
|
332
|
+
const contractClasses: ContractClassPublicWithCommitment[] = [];
|
|
333
|
+
for (const event of contractClassPublishedEvents) {
|
|
334
|
+
const contractClass = await event.toContractClassPublicWithBytecodeCommitment();
|
|
335
|
+
const computedClassId = await computeContractClassId({
|
|
336
|
+
artifactHash: contractClass.artifactHash,
|
|
337
|
+
privateFunctionsRoot: contractClass.privateFunctionsRoot,
|
|
338
|
+
publicBytecodeCommitment: contractClass.publicBytecodeCommitment,
|
|
339
|
+
});
|
|
340
|
+
if (!computedClassId.equals(contractClass.id)) {
|
|
341
|
+
this.log.warn(
|
|
342
|
+
`Skipping contract class with mismatched id at block ${blockNum}. Claimed ${contractClass.id}, computed ${computedClassId}`,
|
|
343
|
+
{ blockNum, contractClassId: event.contractClassId.toString() },
|
|
344
|
+
);
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
contractClasses.push(contractClass);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (contractClasses.length > 0) {
|
|
351
|
+
contractClasses.forEach(c => this.log.verbose(`${Operation[operation]} contract class ${c.id.toString()}`));
|
|
352
|
+
return await this.store.addContractClasses(contractClasses, blockNum);
|
|
349
353
|
}
|
|
350
354
|
return true;
|
|
351
355
|
}
|
|
@@ -417,67 +421,4 @@ export class ArchiverDataStoreUpdater {
|
|
|
417
421
|
}
|
|
418
422
|
return true;
|
|
419
423
|
}
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* Stores the functions that were broadcasted individually.
|
|
423
|
-
*
|
|
424
|
-
* @dev Beware that there is not a delete variant of this, since they are added to contract classes
|
|
425
|
-
* and will be deleted as part of the class if needed.
|
|
426
|
-
*/
|
|
427
|
-
private async storeBroadcastedIndividualFunctions(
|
|
428
|
-
allLogs: ContractClassLog[],
|
|
429
|
-
_blockNum: BlockNumber,
|
|
430
|
-
): Promise<boolean> {
|
|
431
|
-
// Filter out private and utility function broadcast events
|
|
432
|
-
const privateFnEvents = allLogs
|
|
433
|
-
.filter(log => PrivateFunctionBroadcastedEvent.isPrivateFunctionBroadcastedEvent(log))
|
|
434
|
-
.map(log => PrivateFunctionBroadcastedEvent.fromLog(log));
|
|
435
|
-
const utilityFnEvents = allLogs
|
|
436
|
-
.filter(log => UtilityFunctionBroadcastedEvent.isUtilityFunctionBroadcastedEvent(log))
|
|
437
|
-
.map(log => UtilityFunctionBroadcastedEvent.fromLog(log));
|
|
438
|
-
|
|
439
|
-
// Group all events by contract class id
|
|
440
|
-
for (const [classIdString, classEvents] of Object.entries(
|
|
441
|
-
groupBy([...privateFnEvents, ...utilityFnEvents], e => e.contractClassId.toString()),
|
|
442
|
-
)) {
|
|
443
|
-
const contractClassId = Fr.fromHexString(classIdString);
|
|
444
|
-
const contractClass = await this.store.getContractClass(contractClassId);
|
|
445
|
-
if (!contractClass) {
|
|
446
|
-
this.log.warn(`Skipping broadcasted functions as contract class ${contractClassId.toString()} was not found`);
|
|
447
|
-
continue;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// Split private and utility functions, and filter out invalid ones
|
|
451
|
-
const allFns = classEvents.map(e => e.toFunctionWithMembershipProof());
|
|
452
|
-
const privateFns = allFns.filter(
|
|
453
|
-
(fn): fn is ExecutablePrivateFunctionWithMembershipProof => 'utilityFunctionsTreeRoot' in fn,
|
|
454
|
-
);
|
|
455
|
-
const utilityFns = allFns.filter(
|
|
456
|
-
(fn): fn is UtilityFunctionWithMembershipProof => 'privateFunctionsArtifactTreeRoot' in fn,
|
|
457
|
-
);
|
|
458
|
-
|
|
459
|
-
const privateFunctionsWithValidity = await Promise.all(
|
|
460
|
-
privateFns.map(async fn => ({ fn, valid: await isValidPrivateFunctionMembershipProof(fn, contractClass) })),
|
|
461
|
-
);
|
|
462
|
-
const validPrivateFns = privateFunctionsWithValidity.filter(({ valid }) => valid).map(({ fn }) => fn);
|
|
463
|
-
const utilityFunctionsWithValidity = await Promise.all(
|
|
464
|
-
utilityFns.map(async fn => ({
|
|
465
|
-
fn,
|
|
466
|
-
valid: await isValidUtilityFunctionMembershipProof(fn, contractClass),
|
|
467
|
-
})),
|
|
468
|
-
);
|
|
469
|
-
const validUtilityFns = utilityFunctionsWithValidity.filter(({ valid }) => valid).map(({ fn }) => fn);
|
|
470
|
-
const validFnCount = validPrivateFns.length + validUtilityFns.length;
|
|
471
|
-
if (validFnCount !== allFns.length) {
|
|
472
|
-
this.log.warn(`Skipping ${allFns.length - validFnCount} invalid functions`);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// Store the functions in the contract class in a single operation
|
|
476
|
-
if (validFnCount > 0) {
|
|
477
|
-
this.log.verbose(`Storing ${validFnCount} functions for contract class ${contractClassId.toString()}`);
|
|
478
|
-
}
|
|
479
|
-
await this.store.addFunctions(contractClassId, validPrivateFns, validUtilityFns);
|
|
480
|
-
}
|
|
481
|
-
return true;
|
|
482
|
-
}
|
|
483
424
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import type { SlotNumber } from '@aztec/foundation/branded-types';
|
|
1
2
|
import { createLogger } from '@aztec/foundation/log';
|
|
2
3
|
import type { L2Block } from '@aztec/stdlib/block';
|
|
3
4
|
import type { CheckpointData } from '@aztec/stdlib/checkpoint';
|
|
5
|
+
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
|
|
6
|
+
import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
4
7
|
import {
|
|
5
8
|
Attributes,
|
|
6
9
|
type Gauge,
|
|
@@ -38,6 +41,8 @@ export class ArchiverInstrumentation {
|
|
|
38
41
|
|
|
39
42
|
private blockProposalTxTargetCount: UpDownCounter;
|
|
40
43
|
|
|
44
|
+
private checkpointL1InclusionDelay: Histogram;
|
|
45
|
+
|
|
41
46
|
private log = createLogger('archiver:instrumentation');
|
|
42
47
|
|
|
43
48
|
private constructor(
|
|
@@ -85,6 +90,8 @@ export class ArchiverInstrumentation {
|
|
|
85
90
|
},
|
|
86
91
|
);
|
|
87
92
|
|
|
93
|
+
this.checkpointL1InclusionDelay = meter.createHistogram(Metrics.ARCHIVER_CHECKPOINT_L1_INCLUSION_DELAY);
|
|
94
|
+
|
|
88
95
|
this.dbMetrics = new LmdbMetrics(
|
|
89
96
|
meter,
|
|
90
97
|
{
|
|
@@ -161,4 +168,17 @@ export class ArchiverInstrumentation {
|
|
|
161
168
|
[Attributes.L1_BLOCK_PROPOSAL_USED_TRACE]: usedTrace,
|
|
162
169
|
});
|
|
163
170
|
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Records L1 inclusion timing for a checkpoint observed on L1 (seconds into the L2 slot).
|
|
174
|
+
*/
|
|
175
|
+
public processCheckpointL1Timing(data: {
|
|
176
|
+
slotNumber: SlotNumber;
|
|
177
|
+
l1Timestamp: bigint;
|
|
178
|
+
l1Constants: Pick<L1RollupConstants, 'l1GenesisTime' | 'slotDuration'>;
|
|
179
|
+
}): void {
|
|
180
|
+
const slotStartTs = getTimestampForSlot(data.slotNumber, data.l1Constants);
|
|
181
|
+
const inclusionDelaySeconds = Number(data.l1Timestamp - slotStartTs);
|
|
182
|
+
this.checkpointL1InclusionDelay.record(inclusionDelaySeconds);
|
|
183
|
+
}
|
|
164
184
|
}
|
|
@@ -3,6 +3,7 @@ import { EpochCache } from '@aztec/epoch-cache';
|
|
|
3
3
|
import { InboxContract, RollupContract } from '@aztec/ethereum/contracts';
|
|
4
4
|
import type { L1BlockId } from '@aztec/ethereum/l1-types';
|
|
5
5
|
import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
|
|
6
|
+
import { asyncPool } from '@aztec/foundation/async-pool';
|
|
6
7
|
import { maxBigint } from '@aztec/foundation/bigint';
|
|
7
8
|
import { BlockNumber, CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
|
|
8
9
|
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
@@ -245,16 +246,10 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
245
246
|
const localFinalizedCheckpointNumber = await this.store.getFinalizedCheckpointNumber();
|
|
246
247
|
if (localFinalizedCheckpointNumber !== finalizedCheckpointNumber) {
|
|
247
248
|
await this.updater.setFinalizedCheckpointNumber(finalizedCheckpointNumber);
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
finalizedCheckpointNumber,
|
|
253
|
-
previousFinalizedCheckpointNumber: localFinalizedCheckpointNumber,
|
|
254
|
-
finalizedL2BlockNumber,
|
|
255
|
-
finalizedL1BlockNumber,
|
|
256
|
-
},
|
|
257
|
-
);
|
|
249
|
+
this.log.info(`Updated finalized chain to checkpoint ${finalizedCheckpointNumber}`, {
|
|
250
|
+
finalizedCheckpointNumber,
|
|
251
|
+
finalizedL1BlockNumber,
|
|
252
|
+
});
|
|
258
253
|
}
|
|
259
254
|
} catch (err) {
|
|
260
255
|
this.log.warn(`Failed to update finalized checkpoint: ${err}`);
|
|
@@ -339,17 +334,20 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
339
334
|
|
|
340
335
|
const checkpointsToUnwind = localPendingCheckpointNumber - provenCheckpointNumber;
|
|
341
336
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
checkpoints
|
|
349
|
-
.filter(isDefined)
|
|
350
|
-
.map(cp => this.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber))),
|
|
337
|
+
// Fetch checkpoints and blocks in bounded batches to avoid unbounded concurrent
|
|
338
|
+
// promises when the gap between local pending and proven checkpoint numbers is large.
|
|
339
|
+
const BATCH_SIZE = 10;
|
|
340
|
+
const indices = Array.from({ length: checkpointsToUnwind }, (_, i) => CheckpointNumber(i + pruneFrom));
|
|
341
|
+
const checkpoints = (await asyncPool(BATCH_SIZE, indices, idx => this.store.getCheckpointData(idx))).filter(
|
|
342
|
+
isDefined,
|
|
351
343
|
);
|
|
352
|
-
const newBlocks =
|
|
344
|
+
const newBlocks = (
|
|
345
|
+
await asyncPool(BATCH_SIZE, checkpoints, cp =>
|
|
346
|
+
this.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber)),
|
|
347
|
+
)
|
|
348
|
+
)
|
|
349
|
+
.filter(isDefined)
|
|
350
|
+
.flat();
|
|
353
351
|
|
|
354
352
|
// Emit an event for listening services to react to the chain prune
|
|
355
353
|
this.events.emit(L2BlockSourceEvents.L2PruneUnproven, {
|
|
@@ -397,6 +395,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
397
395
|
const localMessagesInserted = await this.store.getTotalL1ToL2MessageCount();
|
|
398
396
|
const localLastMessage = await this.store.getLastL1ToL2Message();
|
|
399
397
|
const remoteMessagesState = await this.inbox.getState({ blockNumber: currentL1BlockNumber });
|
|
398
|
+
await this.store.setInboxTreeInProgress(remoteMessagesState.treeInProgress);
|
|
400
399
|
|
|
401
400
|
this.log.trace(`Retrieved remote inbox state at L1 block ${currentL1BlockNumber}.`, {
|
|
402
401
|
localMessagesInserted,
|
|
@@ -831,6 +830,14 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
831
830
|
);
|
|
832
831
|
}
|
|
833
832
|
|
|
833
|
+
for (const published of validCheckpoints) {
|
|
834
|
+
this.instrumentation.processCheckpointL1Timing({
|
|
835
|
+
slotNumber: published.checkpoint.header.slotNumber,
|
|
836
|
+
l1Timestamp: published.l1.timestamp,
|
|
837
|
+
l1Constants: this.l1Constants,
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
|
|
834
841
|
try {
|
|
835
842
|
const updatedValidationResult =
|
|
836
843
|
rollupStatus.validationResult === initialValidationResult ? undefined : rollupStatus.validationResult;
|
package/src/store/block_store.ts
CHANGED
|
@@ -35,15 +35,14 @@ import {
|
|
|
35
35
|
} from '@aztec/stdlib/tx';
|
|
36
36
|
|
|
37
37
|
import {
|
|
38
|
+
BlockAlreadyCheckpointedError,
|
|
38
39
|
BlockArchiveNotConsistentError,
|
|
39
40
|
BlockIndexNotSequentialError,
|
|
40
41
|
BlockNotFoundError,
|
|
41
42
|
BlockNumberNotSequentialError,
|
|
42
43
|
CannotOverwriteCheckpointedBlockError,
|
|
43
44
|
CheckpointNotFoundError,
|
|
44
|
-
CheckpointNumberNotConsistentError,
|
|
45
45
|
CheckpointNumberNotSequentialError,
|
|
46
|
-
InitialBlockNumberNotSequentialError,
|
|
47
46
|
InitialCheckpointNumberNotSequentialError,
|
|
48
47
|
} from '../errors.js';
|
|
49
48
|
|
|
@@ -147,23 +146,18 @@ export class BlockStore {
|
|
|
147
146
|
}
|
|
148
147
|
|
|
149
148
|
/**
|
|
150
|
-
* Append new proposed
|
|
151
|
-
*
|
|
149
|
+
* Append a new proposed block to the store.
|
|
150
|
+
* This is an uncheckpointed block that has been proposed by the sequencer but not yet included in a checkpoint on L1.
|
|
152
151
|
* For checkpointed blocks (already published to L1), use addCheckpoints() instead.
|
|
153
|
-
* @param
|
|
152
|
+
* @param block - The proposed L2 block to be added to the store.
|
|
154
153
|
* @returns True if the operation is successful.
|
|
155
154
|
*/
|
|
156
|
-
async
|
|
157
|
-
if (blocks.length === 0) {
|
|
158
|
-
return true;
|
|
159
|
-
}
|
|
160
|
-
|
|
155
|
+
async addProposedBlock(block: L2Block, opts: { force?: boolean } = {}): Promise<boolean> {
|
|
161
156
|
return await this.db.transactionAsync(async () => {
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
const firstBlockLastArchive = blocks[0].header.lastArchive.root;
|
|
157
|
+
const blockNumber = block.number;
|
|
158
|
+
const blockCheckpointNumber = block.checkpointNumber;
|
|
159
|
+
const blockIndex = block.indexWithinCheckpoint;
|
|
160
|
+
const blockLastArchive = block.header.lastArchive.root;
|
|
167
161
|
|
|
168
162
|
// Extract the latest block and checkpoint numbers
|
|
169
163
|
const previousBlockNumber = await this.getLatestBlockNumber();
|
|
@@ -171,71 +165,52 @@ export class BlockStore {
|
|
|
171
165
|
|
|
172
166
|
// Verify we're not overwriting checkpointed blocks
|
|
173
167
|
const lastCheckpointedBlockNumber = await this.getCheckpointedL2BlockNumber();
|
|
174
|
-
if (!opts.force &&
|
|
175
|
-
|
|
168
|
+
if (!opts.force && blockNumber <= lastCheckpointedBlockNumber) {
|
|
169
|
+
// Check if the proposed block matches the already-checkpointed one
|
|
170
|
+
const existingBlock = await this.getBlock(BlockNumber(blockNumber));
|
|
171
|
+
if (existingBlock && existingBlock.archive.root.equals(block.archive.root)) {
|
|
172
|
+
throw new BlockAlreadyCheckpointedError(blockNumber);
|
|
173
|
+
}
|
|
174
|
+
throw new CannotOverwriteCheckpointedBlockError(blockNumber, lastCheckpointedBlockNumber);
|
|
176
175
|
}
|
|
177
176
|
|
|
178
|
-
// Check that the
|
|
179
|
-
if (!opts.force && previousBlockNumber !==
|
|
180
|
-
throw new
|
|
177
|
+
// Check that the block number is the expected one
|
|
178
|
+
if (!opts.force && previousBlockNumber !== blockNumber - 1) {
|
|
179
|
+
throw new BlockNumberNotSequentialError(blockNumber, previousBlockNumber);
|
|
181
180
|
}
|
|
182
181
|
|
|
183
182
|
// The same check as above but for checkpoints
|
|
184
|
-
if (!opts.force && previousCheckpointNumber !==
|
|
185
|
-
throw new
|
|
183
|
+
if (!opts.force && previousCheckpointNumber !== blockCheckpointNumber - 1) {
|
|
184
|
+
throw new CheckpointNumberNotSequentialError(blockCheckpointNumber, previousCheckpointNumber);
|
|
186
185
|
}
|
|
187
186
|
|
|
188
187
|
// Extract the previous block if there is one and see if it is for the same checkpoint or not
|
|
189
188
|
const previousBlockResult = await this.getBlock(previousBlockNumber);
|
|
190
189
|
|
|
191
|
-
let
|
|
190
|
+
let expectedBlockIndex = 0;
|
|
192
191
|
let previousBlockIndex: number | undefined = undefined;
|
|
193
192
|
if (previousBlockResult !== undefined) {
|
|
194
|
-
if (previousBlockResult.checkpointNumber ===
|
|
193
|
+
if (previousBlockResult.checkpointNumber === blockCheckpointNumber) {
|
|
195
194
|
// The previous block is for the same checkpoint, therefore our index should follow it
|
|
196
195
|
previousBlockIndex = previousBlockResult.indexWithinCheckpoint;
|
|
197
|
-
|
|
196
|
+
expectedBlockIndex = previousBlockIndex + 1;
|
|
198
197
|
}
|
|
199
|
-
if (!previousBlockResult.archive.root.equals(
|
|
198
|
+
if (!previousBlockResult.archive.root.equals(blockLastArchive)) {
|
|
200
199
|
throw new BlockArchiveNotConsistentError(
|
|
201
|
-
|
|
200
|
+
blockNumber,
|
|
202
201
|
previousBlockResult.number,
|
|
203
|
-
|
|
202
|
+
blockLastArchive,
|
|
204
203
|
previousBlockResult.archive.root,
|
|
205
204
|
);
|
|
206
205
|
}
|
|
207
206
|
}
|
|
208
207
|
|
|
209
|
-
// Now check that the
|
|
210
|
-
if (!opts.force &&
|
|
211
|
-
throw new BlockIndexNotSequentialError(
|
|
208
|
+
// Now check that the block has the expected index value
|
|
209
|
+
if (!opts.force && expectedBlockIndex !== blockIndex) {
|
|
210
|
+
throw new BlockIndexNotSequentialError(blockIndex, previousBlockIndex);
|
|
212
211
|
}
|
|
213
212
|
|
|
214
|
-
|
|
215
|
-
let previousBlock: L2Block | undefined = undefined;
|
|
216
|
-
for (const block of blocks) {
|
|
217
|
-
if (!opts.force && previousBlock) {
|
|
218
|
-
if (previousBlock.number + 1 !== block.number) {
|
|
219
|
-
throw new BlockNumberNotSequentialError(block.number, previousBlock.number);
|
|
220
|
-
}
|
|
221
|
-
if (previousBlock.indexWithinCheckpoint + 1 !== block.indexWithinCheckpoint) {
|
|
222
|
-
throw new BlockIndexNotSequentialError(block.indexWithinCheckpoint, previousBlock.indexWithinCheckpoint);
|
|
223
|
-
}
|
|
224
|
-
if (!previousBlock.archive.root.equals(block.header.lastArchive.root)) {
|
|
225
|
-
throw new BlockArchiveNotConsistentError(
|
|
226
|
-
block.number,
|
|
227
|
-
previousBlock.number,
|
|
228
|
-
block.header.lastArchive.root,
|
|
229
|
-
previousBlock.archive.root,
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
if (!opts.force && firstBlockCheckpointNumber !== block.checkpointNumber) {
|
|
234
|
-
throw new CheckpointNumberNotConsistentError(block.checkpointNumber, firstBlockCheckpointNumber);
|
|
235
|
-
}
|
|
236
|
-
previousBlock = block;
|
|
237
|
-
await this.addBlockToDatabase(block, block.checkpointNumber, block.indexWithinCheckpoint);
|
|
238
|
-
}
|
|
213
|
+
await this.addBlockToDatabase(block, block.checkpointNumber, block.indexWithinCheckpoint);
|
|
239
214
|
|
|
240
215
|
return true;
|
|
241
216
|
});
|