@aztec/archiver 0.0.1-commit.9ef841308 → 0.0.1-commit.a89ec08
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 +2 -1
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +1 -0
- package/dest/config.d.ts +3 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +1 -2
- package/dest/errors.d.ts +1 -15
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +0 -18
- package/dest/factory.d.ts +2 -2
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +3 -1
- package/dest/l1/calldata_retriever.d.ts +1 -1
- package/dest/l1/calldata_retriever.d.ts.map +1 -1
- package/dest/l1/calldata_retriever.js +1 -2
- package/dest/l1/data_retrieval.d.ts +3 -6
- package/dest/l1/data_retrieval.d.ts.map +1 -1
- package/dest/l1/data_retrieval.js +6 -12
- 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 +6 -3
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +56 -5
- package/dest/modules/instrumentation.d.ts +1 -12
- package/dest/modules/instrumentation.d.ts.map +1 -1
- package/dest/modules/instrumentation.js +0 -10
- 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 +13 -18
- package/dest/store/block_store.d.ts +2 -1
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +50 -5
- package/dest/store/contract_class_store.d.ts +3 -2
- package/dest/store/contract_class_store.d.ts.map +1 -1
- package/dest/store/contract_class_store.js +65 -1
- package/dest/store/kv_archiver_store.d.ts +8 -10
- package/dest/store/kv_archiver_store.d.ts.map +1 -1
- package/dest/store/kv_archiver_store.js +8 -10
- package/dest/store/log_store.d.ts +3 -6
- package/dest/store/log_store.d.ts.map +1 -1
- package/dest/store/log_store.js +6 -45
- package/dest/store/message_store.d.ts +1 -5
- package/dest/store/message_store.d.ts.map +1 -1
- package/dest/store/message_store.js +0 -13
- package/dest/test/fake_l1_state.d.ts +8 -1
- package/dest/test/fake_l1_state.d.ts.map +1 -1
- package/dest/test/fake_l1_state.js +16 -11
- package/package.json +13 -13
- package/src/archiver.ts +1 -0
- package/src/config.ts +1 -8
- package/src/errors.ts +0 -30
- package/src/factory.ts +3 -1
- package/src/l1/calldata_retriever.ts +1 -2
- package/src/l1/data_retrieval.ts +9 -17
- package/src/modules/data_source_base.ts +3 -8
- package/src/modules/data_store_updater.ts +82 -3
- package/src/modules/instrumentation.ts +0 -20
- package/src/modules/l1_synchronizer.ts +25 -29
- package/src/store/block_store.ts +62 -5
- package/src/store/contract_class_store.ts +103 -1
- package/src/store/kv_archiver_store.ts +15 -12
- package/src/store/log_store.ts +15 -60
- package/src/store/message_store.ts +0 -19
- package/src/test/fake_l1_state.ts +20 -15
package/src/config.ts
CHANGED
|
@@ -8,12 +8,7 @@ import {
|
|
|
8
8
|
getConfigFromMappings,
|
|
9
9
|
numberConfigHelper,
|
|
10
10
|
} from '@aztec/foundation/config';
|
|
11
|
-
import {
|
|
12
|
-
type ChainConfig,
|
|
13
|
-
type PipelineConfig,
|
|
14
|
-
chainConfigMappings,
|
|
15
|
-
pipelineConfigMappings,
|
|
16
|
-
} from '@aztec/stdlib/config';
|
|
11
|
+
import { type ChainConfig, chainConfigMappings } from '@aztec/stdlib/config';
|
|
17
12
|
import type { ArchiverSpecificConfig } from '@aztec/stdlib/interfaces/server';
|
|
18
13
|
|
|
19
14
|
/**
|
|
@@ -26,13 +21,11 @@ import type { ArchiverSpecificConfig } from '@aztec/stdlib/interfaces/server';
|
|
|
26
21
|
export type ArchiverConfig = ArchiverSpecificConfig &
|
|
27
22
|
L1ReaderConfig &
|
|
28
23
|
L1ContractsConfig &
|
|
29
|
-
PipelineConfig & // required to pass through to epoch cache
|
|
30
24
|
BlobClientConfig &
|
|
31
25
|
ChainConfig;
|
|
32
26
|
|
|
33
27
|
export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
|
|
34
28
|
...blobClientConfigMapping,
|
|
35
|
-
...pipelineConfigMappings,
|
|
36
29
|
archiverPollingIntervalMS: {
|
|
37
30
|
env: 'ARCHIVER_POLLING_INTERVAL_MS',
|
|
38
31
|
description: 'The polling interval in ms for retrieving new L2 blocks and encrypted logs.',
|
package/src/errors.ts
CHANGED
|
@@ -74,36 +74,6 @@ export class BlockAlreadyCheckpointedError extends Error {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
/** Thrown when logs are added for a tag whose last stored log has a higher block number than the new log. */
|
|
78
|
-
export class OutOfOrderLogInsertionError extends Error {
|
|
79
|
-
constructor(
|
|
80
|
-
public readonly logType: 'private' | 'public',
|
|
81
|
-
public readonly tag: string,
|
|
82
|
-
public readonly lastBlockNumber: number,
|
|
83
|
-
public readonly newBlockNumber: number,
|
|
84
|
-
) {
|
|
85
|
-
super(
|
|
86
|
-
`Out-of-order ${logType} log insertion for tag ${tag}: ` +
|
|
87
|
-
`last existing log is from block ${lastBlockNumber} but new log is from block ${newBlockNumber}`,
|
|
88
|
-
);
|
|
89
|
-
this.name = 'OutOfOrderLogInsertionError';
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** Thrown when L1 to L2 messages are requested for a checkpoint whose message tree hasn't been sealed yet. */
|
|
94
|
-
export class L1ToL2MessagesNotReadyError extends Error {
|
|
95
|
-
constructor(
|
|
96
|
-
public readonly checkpointNumber: number,
|
|
97
|
-
public readonly inboxTreeInProgress: bigint,
|
|
98
|
-
) {
|
|
99
|
-
super(
|
|
100
|
-
`Cannot get L1 to L2 messages for checkpoint ${checkpointNumber}: ` +
|
|
101
|
-
`inbox tree in progress is ${inboxTreeInProgress}, messages not yet sealed`,
|
|
102
|
-
);
|
|
103
|
-
this.name = 'L1ToL2MessagesNotReadyError';
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
77
|
/** Thrown when a proposed block conflicts with an already checkpointed block (different content). */
|
|
108
78
|
export class CannotOverwriteCheckpointedBlockError extends Error {
|
|
109
79
|
constructor(
|
package/src/factory.ts
CHANGED
|
@@ -8,13 +8,13 @@ import { Buffer32 } from '@aztec/foundation/buffer';
|
|
|
8
8
|
import { merge } from '@aztec/foundation/collection';
|
|
9
9
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
10
10
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
11
|
+
import type { DataStoreConfig } from '@aztec/kv-store/config';
|
|
11
12
|
import { createStore } from '@aztec/kv-store/lmdb-v2';
|
|
12
13
|
import { protocolContractNames } from '@aztec/protocol-contracts';
|
|
13
14
|
import { BundledProtocolContractsProvider } from '@aztec/protocol-contracts/providers/bundle';
|
|
14
15
|
import { FunctionType, decodeFunctionSignature } from '@aztec/stdlib/abi';
|
|
15
16
|
import type { ArchiverEmitter } from '@aztec/stdlib/block';
|
|
16
17
|
import { type ContractClassPublicWithCommitment, computePublicBytecodeCommitment } from '@aztec/stdlib/contract';
|
|
17
|
-
import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
|
|
18
18
|
import { getTelemetryClient } from '@aztec/telemetry-client';
|
|
19
19
|
|
|
20
20
|
import { EventEmitter } from 'events';
|
|
@@ -189,6 +189,8 @@ export async function registerProtocolContracts(store: KVArchiverDataStore) {
|
|
|
189
189
|
const contractClassPublic: ContractClassPublicWithCommitment = {
|
|
190
190
|
...contract.contractClass,
|
|
191
191
|
publicBytecodeCommitment,
|
|
192
|
+
privateFunctions: [],
|
|
193
|
+
utilityFunctions: [],
|
|
192
194
|
};
|
|
193
195
|
|
|
194
196
|
const publicFunctionSignatures = contract.artifact.functions
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { MULTI_CALL_3_ADDRESS, type ViemCommitteeAttestations, type ViemHeader } from '@aztec/ethereum/contracts';
|
|
2
2
|
import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
|
|
3
3
|
import { CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
4
|
-
import { LruSet } from '@aztec/foundation/collection';
|
|
5
4
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
6
5
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
7
6
|
import type { Logger } from '@aztec/foundation/log';
|
|
@@ -45,7 +44,7 @@ type CheckpointData = {
|
|
|
45
44
|
*/
|
|
46
45
|
export class CalldataRetriever {
|
|
47
46
|
/** Tx hashes we've already logged for trace+debug failure (log once per tx per process). */
|
|
48
|
-
private static readonly traceFailureWarnedTxHashes = new
|
|
47
|
+
private static readonly traceFailureWarnedTxHashes = new Set<string>();
|
|
49
48
|
|
|
50
49
|
/** Clears the trace-failure warned set. For testing only. */
|
|
51
50
|
static resetTraceFailureWarnedForTesting(): void {
|
package/src/l1/data_retrieval.ts
CHANGED
|
@@ -265,9 +265,6 @@ async function processCheckpointProposedLogs(
|
|
|
265
265
|
checkpointNumber,
|
|
266
266
|
expectedHashes,
|
|
267
267
|
);
|
|
268
|
-
const { timestamp, parentBeaconBlockRoot } = await getL1Block(publicClient, log.l1BlockNumber);
|
|
269
|
-
const l1 = new L1PublishedData(log.l1BlockNumber, timestamp, log.l1BlockHash.toString());
|
|
270
|
-
|
|
271
268
|
const checkpointBlobData = await getCheckpointBlobDataFromBlobs(
|
|
272
269
|
blobClient,
|
|
273
270
|
checkpoint.blockHash,
|
|
@@ -275,8 +272,12 @@ async function processCheckpointProposedLogs(
|
|
|
275
272
|
checkpointNumber,
|
|
276
273
|
logger,
|
|
277
274
|
isHistoricalSync,
|
|
278
|
-
|
|
279
|
-
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
const l1 = new L1PublishedData(
|
|
278
|
+
log.l1BlockNumber,
|
|
279
|
+
await getL1BlockTime(publicClient, log.l1BlockNumber),
|
|
280
|
+
log.l1BlockHash.toString(),
|
|
280
281
|
);
|
|
281
282
|
|
|
282
283
|
retrievedCheckpoints.push({ ...checkpoint, checkpointBlobData, l1, chainId, version });
|
|
@@ -297,12 +298,9 @@ async function processCheckpointProposedLogs(
|
|
|
297
298
|
return retrievedCheckpoints;
|
|
298
299
|
}
|
|
299
300
|
|
|
300
|
-
export async function
|
|
301
|
-
publicClient: ViemPublicClient,
|
|
302
|
-
blockNumber: bigint,
|
|
303
|
-
): Promise<{ timestamp: bigint; parentBeaconBlockRoot: string | undefined }> {
|
|
301
|
+
export async function getL1BlockTime(publicClient: ViemPublicClient, blockNumber: bigint): Promise<bigint> {
|
|
304
302
|
const block = await publicClient.getBlock({ blockNumber, includeTransactions: false });
|
|
305
|
-
return
|
|
303
|
+
return block.timestamp;
|
|
306
304
|
}
|
|
307
305
|
|
|
308
306
|
export async function getCheckpointBlobDataFromBlobs(
|
|
@@ -312,14 +310,8 @@ export async function getCheckpointBlobDataFromBlobs(
|
|
|
312
310
|
checkpointNumber: CheckpointNumber,
|
|
313
311
|
logger: Logger,
|
|
314
312
|
isHistoricalSync: boolean,
|
|
315
|
-
parentBeaconBlockRoot?: string,
|
|
316
|
-
l1BlockTimestamp?: bigint,
|
|
317
313
|
): Promise<CheckpointBlobData> {
|
|
318
|
-
const blobBodies = await blobClient.getBlobSidecar(blockHash, blobHashes, {
|
|
319
|
-
isHistoricalSync,
|
|
320
|
-
parentBeaconBlockRoot,
|
|
321
|
-
l1BlockTimestamp,
|
|
322
|
-
});
|
|
314
|
+
const blobBodies = await blobClient.getBlobSidecar(blockHash, blobHashes, { isHistoricalSync });
|
|
323
315
|
if (blobBodies.length === 0) {
|
|
324
316
|
throw new NoBlobBodiesFoundError(checkpointNumber);
|
|
325
317
|
}
|
|
@@ -165,21 +165,16 @@ export abstract class ArchiverDataSourceBase
|
|
|
165
165
|
return (await this.store.getPendingChainValidationStatus()) ?? { valid: true };
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
public getPrivateLogsByTags(
|
|
169
|
-
tags
|
|
170
|
-
page?: number,
|
|
171
|
-
upToBlockNumber?: BlockNumber,
|
|
172
|
-
): Promise<TxScopedL2Log[][]> {
|
|
173
|
-
return this.store.getPrivateLogsByTags(tags, page, upToBlockNumber);
|
|
168
|
+
public getPrivateLogsByTags(tags: SiloedTag[], page?: number): Promise<TxScopedL2Log[][]> {
|
|
169
|
+
return this.store.getPrivateLogsByTags(tags, page);
|
|
174
170
|
}
|
|
175
171
|
|
|
176
172
|
public getPublicLogsByTagsFromContract(
|
|
177
173
|
contractAddress: AztecAddress,
|
|
178
174
|
tags: Tag[],
|
|
179
175
|
page?: number,
|
|
180
|
-
upToBlockNumber?: BlockNumber,
|
|
181
176
|
): Promise<TxScopedL2Log[][]> {
|
|
182
|
-
return this.store.getPublicLogsByTagsFromContract(contractAddress, tags, page
|
|
177
|
+
return this.store.getPublicLogsByTagsFromContract(contractAddress, tags, page);
|
|
183
178
|
}
|
|
184
179
|
|
|
185
180
|
public getPublicLogs(filter: LogFilter): Promise<GetPublicLogsResponse> {
|
|
@@ -1,7 +1,12 @@
|
|
|
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';
|
|
3
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
ContractClassPublishedEvent,
|
|
7
|
+
PrivateFunctionBroadcastedEvent,
|
|
8
|
+
UtilityFunctionBroadcastedEvent,
|
|
9
|
+
} from '@aztec/protocol-contracts/class-registry';
|
|
5
10
|
import {
|
|
6
11
|
ContractInstancePublishedEvent,
|
|
7
12
|
ContractInstanceUpdatedEvent,
|
|
@@ -10,12 +15,18 @@ import type { L2Block, ValidateCheckpointResult } from '@aztec/stdlib/block';
|
|
|
10
15
|
import { type PublishedCheckpoint, validateCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
11
16
|
import {
|
|
12
17
|
type ContractClassPublicWithCommitment,
|
|
18
|
+
type ExecutablePrivateFunctionWithMembershipProof,
|
|
19
|
+
type UtilityFunctionWithMembershipProof,
|
|
13
20
|
computeContractAddressFromInstance,
|
|
14
21
|
computeContractClassId,
|
|
22
|
+
isValidPrivateFunctionMembershipProof,
|
|
23
|
+
isValidUtilityFunctionMembershipProof,
|
|
15
24
|
} from '@aztec/stdlib/contract';
|
|
16
25
|
import type { ContractClassLog, PrivateLog, PublicLog } from '@aztec/stdlib/logs';
|
|
17
26
|
import type { UInt64 } from '@aztec/stdlib/types';
|
|
18
27
|
|
|
28
|
+
import groupBy from 'lodash.groupby';
|
|
29
|
+
|
|
19
30
|
import type { KVArchiverDataStore } from '../store/kv_archiver_store.js';
|
|
20
31
|
import type { L2TipsCache } from '../store/l2_tips_cache.js';
|
|
21
32
|
|
|
@@ -46,7 +57,8 @@ export class ArchiverDataStoreUpdater {
|
|
|
46
57
|
/**
|
|
47
58
|
* Adds a proposed block to the store with contract class/instance extraction from logs.
|
|
48
59
|
* 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
|
|
60
|
+
* Extracts ContractClassPublished, ContractInstancePublished, ContractInstanceUpdated events,
|
|
61
|
+
* and individually broadcasted functions from the block logs.
|
|
50
62
|
*
|
|
51
63
|
* @param block - The proposed L2 block to add.
|
|
52
64
|
* @param pendingChainValidationStatus - Optional validation status to set.
|
|
@@ -78,7 +90,8 @@ export class ArchiverDataStoreUpdater {
|
|
|
78
90
|
* Reconciles local blocks with incoming checkpoints from L1.
|
|
79
91
|
* Adds new checkpoints to the store with contract class/instance extraction from logs.
|
|
80
92
|
* Prunes any local blocks that conflict with checkpoint data (by comparing archive roots).
|
|
81
|
-
* Extracts ContractClassPublished, ContractInstancePublished, ContractInstanceUpdated events
|
|
93
|
+
* Extracts ContractClassPublished, ContractInstancePublished, ContractInstanceUpdated events,
|
|
94
|
+
* and individually broadcasted functions from the checkpoint block logs.
|
|
82
95
|
*
|
|
83
96
|
* @param checkpoints - The published checkpoints to add.
|
|
84
97
|
* @param pendingChainValidationStatus - Optional validation status to set.
|
|
@@ -303,6 +316,9 @@ export class ArchiverDataStoreUpdater {
|
|
|
303
316
|
this.updatePublishedContractClasses(contractClassLogs, block.number, operation),
|
|
304
317
|
this.updateDeployedContractInstances(privateLogs, block.number, operation),
|
|
305
318
|
this.updateUpdatedContractInstances(publicLogs, block.header.globalVariables.timestamp, operation),
|
|
319
|
+
operation === Operation.Store
|
|
320
|
+
? this.storeBroadcastedIndividualFunctions(contractClassLogs, block.number)
|
|
321
|
+
: Promise.resolve(true),
|
|
306
322
|
])
|
|
307
323
|
).every(Boolean);
|
|
308
324
|
}
|
|
@@ -421,4 +437,67 @@ export class ArchiverDataStoreUpdater {
|
|
|
421
437
|
}
|
|
422
438
|
return true;
|
|
423
439
|
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Stores the functions that were broadcasted individually.
|
|
443
|
+
*
|
|
444
|
+
* @dev Beware that there is not a delete variant of this, since they are added to contract classes
|
|
445
|
+
* and will be deleted as part of the class if needed.
|
|
446
|
+
*/
|
|
447
|
+
private async storeBroadcastedIndividualFunctions(
|
|
448
|
+
allLogs: ContractClassLog[],
|
|
449
|
+
_blockNum: BlockNumber,
|
|
450
|
+
): Promise<boolean> {
|
|
451
|
+
// Filter out private and utility function broadcast events
|
|
452
|
+
const privateFnEvents = allLogs
|
|
453
|
+
.filter(log => PrivateFunctionBroadcastedEvent.isPrivateFunctionBroadcastedEvent(log))
|
|
454
|
+
.map(log => PrivateFunctionBroadcastedEvent.fromLog(log));
|
|
455
|
+
const utilityFnEvents = allLogs
|
|
456
|
+
.filter(log => UtilityFunctionBroadcastedEvent.isUtilityFunctionBroadcastedEvent(log))
|
|
457
|
+
.map(log => UtilityFunctionBroadcastedEvent.fromLog(log));
|
|
458
|
+
|
|
459
|
+
// Group all events by contract class id
|
|
460
|
+
for (const [classIdString, classEvents] of Object.entries(
|
|
461
|
+
groupBy([...privateFnEvents, ...utilityFnEvents], e => e.contractClassId.toString()),
|
|
462
|
+
)) {
|
|
463
|
+
const contractClassId = Fr.fromHexString(classIdString);
|
|
464
|
+
const contractClass = await this.store.getContractClass(contractClassId);
|
|
465
|
+
if (!contractClass) {
|
|
466
|
+
this.log.warn(`Skipping broadcasted functions as contract class ${contractClassId.toString()} was not found`);
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Split private and utility functions, and filter out invalid ones
|
|
471
|
+
const allFns = classEvents.map(e => e.toFunctionWithMembershipProof());
|
|
472
|
+
const privateFns = allFns.filter(
|
|
473
|
+
(fn): fn is ExecutablePrivateFunctionWithMembershipProof => 'utilityFunctionsTreeRoot' in fn,
|
|
474
|
+
);
|
|
475
|
+
const utilityFns = allFns.filter(
|
|
476
|
+
(fn): fn is UtilityFunctionWithMembershipProof => 'privateFunctionsArtifactTreeRoot' in fn,
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
const privateFunctionsWithValidity = await Promise.all(
|
|
480
|
+
privateFns.map(async fn => ({ fn, valid: await isValidPrivateFunctionMembershipProof(fn, contractClass) })),
|
|
481
|
+
);
|
|
482
|
+
const validPrivateFns = privateFunctionsWithValidity.filter(({ valid }) => valid).map(({ fn }) => fn);
|
|
483
|
+
const utilityFunctionsWithValidity = await Promise.all(
|
|
484
|
+
utilityFns.map(async fn => ({
|
|
485
|
+
fn,
|
|
486
|
+
valid: await isValidUtilityFunctionMembershipProof(fn, contractClass),
|
|
487
|
+
})),
|
|
488
|
+
);
|
|
489
|
+
const validUtilityFns = utilityFunctionsWithValidity.filter(({ valid }) => valid).map(({ fn }) => fn);
|
|
490
|
+
const validFnCount = validPrivateFns.length + validUtilityFns.length;
|
|
491
|
+
if (validFnCount !== allFns.length) {
|
|
492
|
+
this.log.warn(`Skipping ${allFns.length - validFnCount} invalid functions`);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Store the functions in the contract class in a single operation
|
|
496
|
+
if (validFnCount > 0) {
|
|
497
|
+
this.log.verbose(`Storing ${validFnCount} functions for contract class ${contractClassId.toString()}`);
|
|
498
|
+
}
|
|
499
|
+
await this.store.addFunctions(contractClassId, validPrivateFns, validUtilityFns);
|
|
500
|
+
}
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
424
503
|
}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import type { SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
1
|
import { createLogger } from '@aztec/foundation/log';
|
|
3
2
|
import type { L2Block } from '@aztec/stdlib/block';
|
|
4
3
|
import type { CheckpointData } from '@aztec/stdlib/checkpoint';
|
|
5
|
-
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
|
|
6
|
-
import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
7
4
|
import {
|
|
8
5
|
Attributes,
|
|
9
6
|
type Gauge,
|
|
@@ -41,8 +38,6 @@ export class ArchiverInstrumentation {
|
|
|
41
38
|
|
|
42
39
|
private blockProposalTxTargetCount: UpDownCounter;
|
|
43
40
|
|
|
44
|
-
private checkpointL1InclusionDelay: Histogram;
|
|
45
|
-
|
|
46
41
|
private log = createLogger('archiver:instrumentation');
|
|
47
42
|
|
|
48
43
|
private constructor(
|
|
@@ -90,8 +85,6 @@ export class ArchiverInstrumentation {
|
|
|
90
85
|
},
|
|
91
86
|
);
|
|
92
87
|
|
|
93
|
-
this.checkpointL1InclusionDelay = meter.createHistogram(Metrics.ARCHIVER_CHECKPOINT_L1_INCLUSION_DELAY);
|
|
94
|
-
|
|
95
88
|
this.dbMetrics = new LmdbMetrics(
|
|
96
89
|
meter,
|
|
97
90
|
{
|
|
@@ -168,17 +161,4 @@ export class ArchiverInstrumentation {
|
|
|
168
161
|
[Attributes.L1_BLOCK_PROPOSAL_USED_TRACE]: usedTrace,
|
|
169
162
|
});
|
|
170
163
|
}
|
|
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
|
-
}
|
|
184
164
|
}
|
|
@@ -3,7 +3,6 @@ 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';
|
|
7
6
|
import { maxBigint } from '@aztec/foundation/bigint';
|
|
8
7
|
import { BlockNumber, CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
|
|
9
8
|
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
@@ -246,13 +245,22 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
246
245
|
const localFinalizedCheckpointNumber = await this.store.getFinalizedCheckpointNumber();
|
|
247
246
|
if (localFinalizedCheckpointNumber !== finalizedCheckpointNumber) {
|
|
248
247
|
await this.updater.setFinalizedCheckpointNumber(finalizedCheckpointNumber);
|
|
249
|
-
this.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
248
|
+
const finalizedL2BlockNumber = await this.store.getFinalizedL2BlockNumber();
|
|
249
|
+
this.log.info(
|
|
250
|
+
`Updated finalized chain to checkpoint ${finalizedCheckpointNumber} (L2 block ${finalizedL2BlockNumber})`,
|
|
251
|
+
{
|
|
252
|
+
finalizedCheckpointNumber,
|
|
253
|
+
previousFinalizedCheckpointNumber: localFinalizedCheckpointNumber,
|
|
254
|
+
finalizedL2BlockNumber,
|
|
255
|
+
finalizedL1BlockNumber,
|
|
256
|
+
},
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
} catch (err: any) {
|
|
260
|
+
// The rollup contract may not exist at the finalized L1 block right after deployment.
|
|
261
|
+
if (!err?.message?.includes('returned no data')) {
|
|
262
|
+
this.log.warn(`Failed to update finalized checkpoint: ${err}`);
|
|
253
263
|
}
|
|
254
|
-
} catch (err) {
|
|
255
|
-
this.log.warn(`Failed to update finalized checkpoint: ${err}`);
|
|
256
264
|
}
|
|
257
265
|
}
|
|
258
266
|
|
|
@@ -334,20 +342,17 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
334
342
|
|
|
335
343
|
const checkpointsToUnwind = localPendingCheckpointNumber - provenCheckpointNumber;
|
|
336
344
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
345
|
+
const checkpointPromises = Array.from({ length: checkpointsToUnwind })
|
|
346
|
+
.fill(0)
|
|
347
|
+
.map((_, i) => this.store.getCheckpointData(CheckpointNumber(i + pruneFrom)));
|
|
348
|
+
const checkpoints = await Promise.all(checkpointPromises);
|
|
349
|
+
|
|
350
|
+
const blockPromises = await Promise.all(
|
|
351
|
+
checkpoints
|
|
352
|
+
.filter(isDefined)
|
|
353
|
+
.map(cp => this.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber))),
|
|
343
354
|
);
|
|
344
|
-
const newBlocks = (
|
|
345
|
-
await asyncPool(BATCH_SIZE, checkpoints, cp =>
|
|
346
|
-
this.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber)),
|
|
347
|
-
)
|
|
348
|
-
)
|
|
349
|
-
.filter(isDefined)
|
|
350
|
-
.flat();
|
|
355
|
+
const newBlocks = blockPromises.filter(isDefined).flat();
|
|
351
356
|
|
|
352
357
|
// Emit an event for listening services to react to the chain prune
|
|
353
358
|
this.events.emit(L2BlockSourceEvents.L2PruneUnproven, {
|
|
@@ -395,7 +400,6 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
395
400
|
const localMessagesInserted = await this.store.getTotalL1ToL2MessageCount();
|
|
396
401
|
const localLastMessage = await this.store.getLastL1ToL2Message();
|
|
397
402
|
const remoteMessagesState = await this.inbox.getState({ blockNumber: currentL1BlockNumber });
|
|
398
|
-
await this.store.setInboxTreeInProgress(remoteMessagesState.treeInProgress);
|
|
399
403
|
|
|
400
404
|
this.log.trace(`Retrieved remote inbox state at L1 block ${currentL1BlockNumber}.`, {
|
|
401
405
|
localMessagesInserted,
|
|
@@ -830,14 +834,6 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
830
834
|
);
|
|
831
835
|
}
|
|
832
836
|
|
|
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
|
-
|
|
841
837
|
try {
|
|
842
838
|
const updatedValidationResult =
|
|
843
839
|
rollupStatus.validationResult === initialValidationResult ? undefined : rollupStatus.validationResult;
|
package/src/store/block_store.ts
CHANGED
|
@@ -227,21 +227,34 @@ export class BlockStore {
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
return await this.db.transactionAsync(async () => {
|
|
230
|
-
// Check that the checkpoint immediately before the first block to be added is present in the store.
|
|
231
230
|
const firstCheckpointNumber = checkpoints[0].checkpoint.number;
|
|
232
231
|
const previousCheckpointNumber = await this.getLatestCheckpointNumber();
|
|
233
232
|
|
|
234
|
-
|
|
233
|
+
// Handle already-stored checkpoints at the start of the batch.
|
|
234
|
+
// This can happen after an L1 reorg re-includes a checkpoint in a different L1 block.
|
|
235
|
+
// We accept them if archives match (same content) and update their L1 metadata.
|
|
236
|
+
if (!opts.force && firstCheckpointNumber <= previousCheckpointNumber) {
|
|
237
|
+
checkpoints = await this.skipOrUpdateAlreadyStoredCheckpoints(checkpoints, previousCheckpointNumber);
|
|
238
|
+
if (checkpoints.length === 0) {
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
// Re-check sequentiality after skipping
|
|
242
|
+
const newFirstNumber = checkpoints[0].checkpoint.number;
|
|
243
|
+
if (previousCheckpointNumber !== newFirstNumber - 1) {
|
|
244
|
+
throw new InitialCheckpointNumberNotSequentialError(newFirstNumber, previousCheckpointNumber);
|
|
245
|
+
}
|
|
246
|
+
} else if (previousCheckpointNumber !== firstCheckpointNumber - 1 && !opts.force) {
|
|
235
247
|
throw new InitialCheckpointNumberNotSequentialError(firstCheckpointNumber, previousCheckpointNumber);
|
|
236
248
|
}
|
|
237
249
|
|
|
238
250
|
// Extract the previous checkpoint if there is one
|
|
251
|
+
const currentFirstCheckpointNumber = checkpoints[0].checkpoint.number;
|
|
239
252
|
let previousCheckpointData: CheckpointData | undefined = undefined;
|
|
240
|
-
if (
|
|
253
|
+
if (currentFirstCheckpointNumber - 1 !== INITIAL_CHECKPOINT_NUMBER - 1) {
|
|
241
254
|
// There should be a previous checkpoint
|
|
242
|
-
previousCheckpointData = await this.getCheckpointData(
|
|
255
|
+
previousCheckpointData = await this.getCheckpointData(CheckpointNumber(currentFirstCheckpointNumber - 1));
|
|
243
256
|
if (previousCheckpointData === undefined) {
|
|
244
|
-
throw new CheckpointNotFoundError(
|
|
257
|
+
throw new CheckpointNotFoundError(CheckpointNumber(currentFirstCheckpointNumber - 1));
|
|
245
258
|
}
|
|
246
259
|
}
|
|
247
260
|
|
|
@@ -331,6 +344,50 @@ export class BlockStore {
|
|
|
331
344
|
});
|
|
332
345
|
}
|
|
333
346
|
|
|
347
|
+
/**
|
|
348
|
+
* Handles checkpoints at the start of a batch that are already stored (e.g. due to L1 reorg).
|
|
349
|
+
* Verifies the archive root matches, updates L1 metadata, and returns only the new checkpoints.
|
|
350
|
+
*/
|
|
351
|
+
private async skipOrUpdateAlreadyStoredCheckpoints(
|
|
352
|
+
checkpoints: PublishedCheckpoint[],
|
|
353
|
+
latestStored: CheckpointNumber,
|
|
354
|
+
): Promise<PublishedCheckpoint[]> {
|
|
355
|
+
let i = 0;
|
|
356
|
+
for (; i < checkpoints.length && checkpoints[i].checkpoint.number <= latestStored; i++) {
|
|
357
|
+
const incoming = checkpoints[i];
|
|
358
|
+
const stored = await this.getCheckpointData(incoming.checkpoint.number);
|
|
359
|
+
if (!stored) {
|
|
360
|
+
// Should not happen if latestStored is correct, but be safe
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
// Verify the checkpoint content matches (archive root)
|
|
364
|
+
if (!stored.archive.root.equals(incoming.checkpoint.archive.root)) {
|
|
365
|
+
throw new Error(
|
|
366
|
+
`Checkpoint ${incoming.checkpoint.number} already exists in store but with a different archive root. ` +
|
|
367
|
+
`Stored: ${stored.archive.root}, incoming: ${incoming.checkpoint.archive.root}`,
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
// Update L1 metadata and attestations for the already-stored checkpoint
|
|
371
|
+
this.#log.warn(
|
|
372
|
+
`Checkpoint ${incoming.checkpoint.number} already stored, updating L1 info ` +
|
|
373
|
+
`(L1 block ${stored.l1.blockNumber} -> ${incoming.l1.blockNumber})`,
|
|
374
|
+
);
|
|
375
|
+
await this.#checkpoints.set(incoming.checkpoint.number, {
|
|
376
|
+
header: incoming.checkpoint.header.toBuffer(),
|
|
377
|
+
archive: incoming.checkpoint.archive.toBuffer(),
|
|
378
|
+
checkpointOutHash: incoming.checkpoint.getCheckpointOutHash().toBuffer(),
|
|
379
|
+
l1: incoming.l1.toBuffer(),
|
|
380
|
+
attestations: incoming.attestations.map(a => a.toBuffer()),
|
|
381
|
+
checkpointNumber: incoming.checkpoint.number,
|
|
382
|
+
startBlock: incoming.checkpoint.blocks[0].number,
|
|
383
|
+
blockCount: incoming.checkpoint.blocks.length,
|
|
384
|
+
});
|
|
385
|
+
// Update the sync point to reflect the new L1 block
|
|
386
|
+
await this.#lastSynchedL1Block.set(incoming.l1.blockNumber);
|
|
387
|
+
}
|
|
388
|
+
return checkpoints.slice(i);
|
|
389
|
+
}
|
|
390
|
+
|
|
334
391
|
private async addBlockToDatabase(block: L2Block, checkpointNumber: number, indexWithinCheckpoint: number) {
|
|
335
392
|
const blockHash = await block.hash();
|
|
336
393
|
|