@aztec/archiver 0.0.1-commit.993d52e → 0.0.1-commit.9badcec54
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 +12 -6
- package/dest/archiver.d.ts +8 -7
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +64 -22
- package/dest/config.d.ts +3 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +3 -2
- package/dest/errors.d.ts +34 -10
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +45 -16
- package/dest/factory.d.ts +4 -5
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +24 -21
- 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 +2 -1
- package/dest/l1/data_retrieval.d.ts +8 -5
- package/dest/l1/data_retrieval.d.ts.map +1 -1
- package/dest/l1/data_retrieval.js +26 -21
- package/dest/modules/data_source_base.d.ts +8 -6
- package/dest/modules/data_source_base.d.ts.map +1 -1
- package/dest/modules/data_source_base.js +11 -5
- package/dest/modules/data_store_updater.d.ts +18 -12
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +87 -77
- package/dest/modules/instrumentation.d.ts +15 -2
- package/dest/modules/instrumentation.d.ts.map +1 -1
- package/dest/modules/instrumentation.js +27 -6
- package/dest/modules/l1_synchronizer.d.ts +4 -2
- package/dest/modules/l1_synchronizer.d.ts.map +1 -1
- package/dest/modules/l1_synchronizer.js +173 -129
- package/dest/modules/validation.d.ts +1 -1
- package/dest/modules/validation.d.ts.map +1 -1
- package/dest/modules/validation.js +2 -2
- package/dest/store/block_store.d.ts +50 -16
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +290 -121
- 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 +45 -22
- package/dest/store/kv_archiver_store.d.ts.map +1 -1
- package/dest/store/kv_archiver_store.js +57 -27
- package/dest/store/l2_tips_cache.d.ts +2 -1
- package/dest/store/l2_tips_cache.d.ts.map +1 -1
- package/dest/store/l2_tips_cache.js +27 -7
- 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 +95 -20
- 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 +21 -9
- package/dest/test/fake_l1_state.d.ts +16 -1
- package/dest/test/fake_l1_state.d.ts.map +1 -1
- package/dest/test/fake_l1_state.js +77 -8
- package/dest/test/mock_l1_to_l2_message_source.d.ts +1 -1
- package/dest/test/mock_l1_to_l2_message_source.d.ts.map +1 -1
- package/dest/test/mock_l1_to_l2_message_source.js +2 -1
- package/dest/test/mock_l2_block_source.d.ts +10 -4
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +35 -7
- package/dest/test/mock_structs.d.ts +4 -1
- package/dest/test/mock_structs.d.ts.map +1 -1
- package/dest/test/mock_structs.js +13 -1
- package/dest/test/noop_l1_archiver.d.ts +4 -1
- package/dest/test/noop_l1_archiver.d.ts.map +1 -1
- package/dest/test/noop_l1_archiver.js +5 -2
- package/package.json +13 -13
- package/src/archiver.ts +78 -28
- package/src/config.ts +9 -2
- package/src/errors.ts +70 -26
- package/src/factory.ts +23 -15
- package/src/l1/calldata_retriever.ts +2 -1
- package/src/l1/data_retrieval.ts +25 -21
- package/src/modules/data_source_base.ts +26 -7
- package/src/modules/data_store_updater.ts +96 -107
- package/src/modules/instrumentation.ts +39 -7
- package/src/modules/l1_synchronizer.ts +197 -161
- package/src/modules/validation.ts +2 -2
- package/src/store/block_store.ts +371 -141
- 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 +81 -39
- package/src/store/l2_tips_cache.ts +58 -13
- package/src/store/log_store.ts +128 -32
- package/src/store/message_store.ts +27 -10
- package/src/structs/inbox_message.ts +1 -1
- package/src/test/fake_l1_state.ts +103 -13
- package/src/test/mock_l1_to_l2_message_source.ts +1 -0
- package/src/test/mock_l2_block_source.ts +52 -5
- package/src/test/mock_structs.ts +20 -6
- package/src/test/noop_l1_archiver.ts +7 -2
package/src/archiver.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { EthAddress } from '@aztec/foundation/eth-address';
|
|
|
11
11
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
12
12
|
import { type PromiseWithResolvers, promiseWithResolvers } from '@aztec/foundation/promise';
|
|
13
13
|
import { RunningPromise, makeLoggingErrorHandler } from '@aztec/foundation/running-promise';
|
|
14
|
-
import { DateProvider } from '@aztec/foundation/timer';
|
|
14
|
+
import { DateProvider, elapsed } from '@aztec/foundation/timer';
|
|
15
15
|
import {
|
|
16
16
|
type ArchiverEmitter,
|
|
17
17
|
L2Block,
|
|
@@ -19,19 +19,18 @@ import {
|
|
|
19
19
|
type L2Tips,
|
|
20
20
|
type ValidateCheckpointResult,
|
|
21
21
|
} from '@aztec/stdlib/block';
|
|
22
|
-
import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
22
|
+
import { type ProposedCheckpointInput, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
23
23
|
import {
|
|
24
24
|
type L1RollupConstants,
|
|
25
|
-
|
|
25
|
+
getEpochAtSlot,
|
|
26
26
|
getSlotAtNextL1Block,
|
|
27
|
-
getSlotAtTimestamp,
|
|
28
27
|
getSlotRangeForEpoch,
|
|
29
28
|
getTimestampRangeForEpoch,
|
|
30
29
|
} from '@aztec/stdlib/epoch-helpers';
|
|
31
30
|
import { type TelemetryClient, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
|
|
32
31
|
|
|
33
32
|
import { type ArchiverConfig, mapArchiverConfig } from './config.js';
|
|
34
|
-
import { NoBlobBodiesFoundError } from './errors.js';
|
|
33
|
+
import { BlockAlreadyCheckpointedError, NoBlobBodiesFoundError } from './errors.js';
|
|
35
34
|
import { validateAndLogTraceAvailability } from './l1/validate_trace.js';
|
|
36
35
|
import { ArchiverDataSourceBase } from './modules/data_source_base.js';
|
|
37
36
|
import { ArchiverDataStoreUpdater } from './modules/data_store_updater.js';
|
|
@@ -86,17 +85,18 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
86
85
|
|
|
87
86
|
public readonly tracer: Tracer;
|
|
88
87
|
|
|
88
|
+
private readonly instrumentation: ArchiverInstrumentation;
|
|
89
|
+
|
|
89
90
|
/**
|
|
90
91
|
* Creates a new instance of the Archiver.
|
|
91
92
|
* @param publicClient - A client for interacting with the Ethereum node.
|
|
92
93
|
* @param debugClient - A client for interacting with the Ethereum node for debug/trace methods.
|
|
93
94
|
* @param rollup - Rollup contract instance.
|
|
94
95
|
* @param inbox - Inbox contract instance.
|
|
95
|
-
* @param l1Addresses - L1 contract addresses (registry, governance proposer,
|
|
96
|
+
* @param l1Addresses - L1 contract addresses (registry, governance proposer, slashing proposer).
|
|
96
97
|
* @param dataStore - An archiver data store for storage & retrieval of blocks, encrypted logs & contract data.
|
|
97
98
|
* @param config - Archiver configuration options.
|
|
98
99
|
* @param blobClient - Client for retrieving blob data.
|
|
99
|
-
* @param epochCache - Cache for epoch-related data.
|
|
100
100
|
* @param dateProvider - Provider for current date/time.
|
|
101
101
|
* @param instrumentation - Instrumentation for metrics and tracing.
|
|
102
102
|
* @param l1Constants - L1 rollup constants.
|
|
@@ -106,10 +106,9 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
106
106
|
private readonly publicClient: ViemPublicClient,
|
|
107
107
|
private readonly debugClient: ViemPublicDebugClient,
|
|
108
108
|
private readonly rollup: RollupContract,
|
|
109
|
-
private readonly l1Addresses: Pick<
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
> & { slashingProposerAddress: EthAddress },
|
|
109
|
+
private readonly l1Addresses: Pick<L1ContractAddresses, 'registryAddress' | 'governanceProposerAddress'> & {
|
|
110
|
+
slashingProposerAddress: EthAddress;
|
|
111
|
+
},
|
|
113
112
|
readonly dataStore: KVArchiverDataStore,
|
|
114
113
|
private config: {
|
|
115
114
|
pollingIntervalMs: number;
|
|
@@ -120,7 +119,10 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
120
119
|
},
|
|
121
120
|
private readonly blobClient: BlobClientInterface,
|
|
122
121
|
instrumentation: ArchiverInstrumentation,
|
|
123
|
-
protected override readonly l1Constants: L1RollupConstants & {
|
|
122
|
+
protected override readonly l1Constants: L1RollupConstants & {
|
|
123
|
+
l1StartBlockHash: Buffer32;
|
|
124
|
+
genesisArchiveRoot: Fr;
|
|
125
|
+
},
|
|
124
126
|
synchronizer: ArchiverL1Synchronizer,
|
|
125
127
|
events: ArchiverEmitter,
|
|
126
128
|
l2TipsCache?: L2TipsCache,
|
|
@@ -129,11 +131,14 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
129
131
|
super(dataStore, l1Constants);
|
|
130
132
|
|
|
131
133
|
this.tracer = instrumentation.tracer;
|
|
134
|
+
this.instrumentation = instrumentation;
|
|
132
135
|
this.initialSyncPromise = promiseWithResolvers();
|
|
133
136
|
this.synchronizer = synchronizer;
|
|
134
137
|
this.events = events;
|
|
135
138
|
this.l2TipsCache = l2TipsCache ?? new L2TipsCache(this.dataStore.blockStore);
|
|
136
|
-
this.updater = new ArchiverDataStoreUpdater(this.dataStore, this.l2TipsCache
|
|
139
|
+
this.updater = new ArchiverDataStoreUpdater(this.dataStore, this.l2TipsCache, {
|
|
140
|
+
rollupManaLimit: l1Constants.rollupManaLimit,
|
|
141
|
+
});
|
|
137
142
|
|
|
138
143
|
// Running promise starts with a small interval inbetween runs, so all iterations needed for the initial sync
|
|
139
144
|
// are done as fast as possible. This then gets updated once the initial sync completes.
|
|
@@ -206,6 +211,10 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
206
211
|
});
|
|
207
212
|
}
|
|
208
213
|
|
|
214
|
+
public async setProposedCheckpoint(pending: ProposedCheckpointInput): Promise<void> {
|
|
215
|
+
await this.updater.setProposedCheckpoint(pending);
|
|
216
|
+
}
|
|
217
|
+
|
|
209
218
|
/**
|
|
210
219
|
* Processes all queued blocks, adding them to the store.
|
|
211
220
|
* Called at the beginning of each sync iteration.
|
|
@@ -238,10 +247,16 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
238
247
|
}
|
|
239
248
|
|
|
240
249
|
try {
|
|
241
|
-
await this.updater.
|
|
250
|
+
const [durationMs] = await elapsed(() => this.updater.addProposedBlock(block));
|
|
251
|
+
this.instrumentation.processNewProposedBlock(durationMs, block);
|
|
242
252
|
this.log.debug(`Added block ${block.number} to store`);
|
|
243
253
|
resolve();
|
|
244
254
|
} catch (err: any) {
|
|
255
|
+
if (err instanceof BlockAlreadyCheckpointedError) {
|
|
256
|
+
this.log.debug(`Proposed block ${block.number} matches already checkpointed block, ignoring late proposal`);
|
|
257
|
+
resolve();
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
245
260
|
this.log.error(`Failed to add block ${block.number} to store: ${err.message}`);
|
|
246
261
|
reject(err);
|
|
247
262
|
}
|
|
@@ -333,16 +348,49 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
333
348
|
return Promise.resolve(this.synchronizer.getL1Timestamp());
|
|
334
349
|
}
|
|
335
350
|
|
|
336
|
-
public
|
|
351
|
+
public async getSyncedL2SlotNumber(): Promise<SlotNumber | undefined> {
|
|
352
|
+
// The synced L2 slot is the latest slot for which we have all L1 data,
|
|
353
|
+
// either because we have seen all L1 blocks for that slot, or because
|
|
354
|
+
// we have seen the corresponding checkpoint.
|
|
355
|
+
|
|
356
|
+
let slotFromL1Sync: SlotNumber | undefined;
|
|
337
357
|
const l1Timestamp = this.synchronizer.getL1Timestamp();
|
|
338
|
-
|
|
358
|
+
if (l1Timestamp !== undefined) {
|
|
359
|
+
const nextL1BlockSlot = getSlotAtNextL1Block(l1Timestamp, this.l1Constants);
|
|
360
|
+
if (Number(nextL1BlockSlot) > 0) {
|
|
361
|
+
slotFromL1Sync = SlotNumber.add(nextL1BlockSlot, -1);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
let slotFromCheckpoint: SlotNumber | undefined;
|
|
366
|
+
const latestCheckpointNumber = await this.store.getSynchedCheckpointNumber();
|
|
367
|
+
if (latestCheckpointNumber > 0) {
|
|
368
|
+
const checkpointData = await this.store.getCheckpointData(latestCheckpointNumber);
|
|
369
|
+
if (checkpointData) {
|
|
370
|
+
slotFromCheckpoint = checkpointData.header.slotNumber;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (slotFromL1Sync === undefined && slotFromCheckpoint === undefined) {
|
|
375
|
+
return undefined;
|
|
376
|
+
}
|
|
377
|
+
return SlotNumber(Math.max(slotFromL1Sync ?? 0, slotFromCheckpoint ?? 0));
|
|
339
378
|
}
|
|
340
379
|
|
|
341
|
-
public
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
380
|
+
public async getSyncedL2EpochNumber(): Promise<EpochNumber | undefined> {
|
|
381
|
+
const syncedSlot = await this.getSyncedL2SlotNumber();
|
|
382
|
+
if (syncedSlot === undefined) {
|
|
383
|
+
return undefined;
|
|
384
|
+
}
|
|
385
|
+
// An epoch is fully synced when all its slots are synced.
|
|
386
|
+
// We check if syncedSlot is the last slot of its epoch; if so, that epoch is fully synced.
|
|
387
|
+
// Otherwise, only the previous epoch is fully synced.
|
|
388
|
+
const epoch = getEpochAtSlot(syncedSlot, this.l1Constants);
|
|
389
|
+
const [, endSlot] = getSlotRangeForEpoch(epoch, this.l1Constants);
|
|
390
|
+
if (syncedSlot >= endSlot) {
|
|
391
|
+
return epoch;
|
|
392
|
+
}
|
|
393
|
+
return Number(epoch) > 0 ? EpochNumber(Number(epoch) - 1) : undefined;
|
|
346
394
|
}
|
|
347
395
|
|
|
348
396
|
public async isEpochComplete(epochNumber: EpochNumber): Promise<boolean> {
|
|
@@ -445,16 +493,18 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
445
493
|
await this.store.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
|
|
446
494
|
this.log.info(`Setting L1 syncpoints to ${targetL1BlockNumber}`);
|
|
447
495
|
await this.store.setCheckpointSynchedL1BlockNumber(targetL1BlockNumber);
|
|
448
|
-
await this.store.
|
|
496
|
+
await this.store.setMessageSyncState(
|
|
497
|
+
{ l1BlockNumber: targetL1BlockNumber, l1BlockHash: targetL1BlockHash },
|
|
498
|
+
undefined,
|
|
499
|
+
);
|
|
449
500
|
if (targetL2BlockNumber < currentProvenBlock) {
|
|
450
501
|
this.log.info(`Rolling back proven L2 checkpoint to ${targetCheckpointNumber}`);
|
|
451
502
|
await this.updater.setProvenCheckpointNumber(targetCheckpointNumber);
|
|
452
503
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
// }
|
|
504
|
+
const currentFinalizedBlock = currentBlocks.finalized.block.number;
|
|
505
|
+
if (targetL2BlockNumber < currentFinalizedBlock) {
|
|
506
|
+
this.log.info(`Rolling back finalized L2 checkpoint to ${targetCheckpointNumber}`);
|
|
507
|
+
await this.updater.setFinalizedCheckpointNumber(targetCheckpointNumber);
|
|
508
|
+
}
|
|
459
509
|
}
|
|
460
510
|
}
|
package/src/config.ts
CHANGED
|
@@ -8,7 +8,12 @@ import {
|
|
|
8
8
|
getConfigFromMappings,
|
|
9
9
|
numberConfigHelper,
|
|
10
10
|
} from '@aztec/foundation/config';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
type ChainConfig,
|
|
13
|
+
type PipelineConfig,
|
|
14
|
+
chainConfigMappings,
|
|
15
|
+
pipelineConfigMappings,
|
|
16
|
+
} from '@aztec/stdlib/config';
|
|
12
17
|
import type { ArchiverSpecificConfig } from '@aztec/stdlib/interfaces/server';
|
|
13
18
|
|
|
14
19
|
/**
|
|
@@ -21,11 +26,13 @@ import type { ArchiverSpecificConfig } from '@aztec/stdlib/interfaces/server';
|
|
|
21
26
|
export type ArchiverConfig = ArchiverSpecificConfig &
|
|
22
27
|
L1ReaderConfig &
|
|
23
28
|
L1ContractsConfig &
|
|
29
|
+
PipelineConfig & // required to pass through to epoch cache
|
|
24
30
|
BlobClientConfig &
|
|
25
31
|
ChainConfig;
|
|
26
32
|
|
|
27
33
|
export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
|
|
28
34
|
...blobClientConfigMapping,
|
|
35
|
+
...pipelineConfigMappings,
|
|
29
36
|
archiverPollingIntervalMS: {
|
|
30
37
|
env: 'ARCHIVER_POLLING_INTERVAL_MS',
|
|
31
38
|
description: 'The polling interval in ms for retrieving new L2 blocks and encrypted logs.',
|
|
@@ -43,7 +50,7 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
|
|
|
43
50
|
},
|
|
44
51
|
archiverStoreMapSizeKb: {
|
|
45
52
|
env: 'ARCHIVER_STORE_MAP_SIZE_KB',
|
|
46
|
-
parseEnv: (val: string
|
|
53
|
+
parseEnv: (val: string) => +val,
|
|
47
54
|
description: 'The maximum possible size of the archiver DB in KB. Overwrites the general dataStoreMapSizeKb.',
|
|
48
55
|
},
|
|
49
56
|
skipValidateCheckpointAttestations: {
|
package/src/errors.ts
CHANGED
|
@@ -6,24 +6,9 @@ export class NoBlobBodiesFoundError extends Error {
|
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export class InitialBlockNumberNotSequentialError extends Error {
|
|
10
|
-
constructor(
|
|
11
|
-
public readonly newBlockNumber: number,
|
|
12
|
-
public readonly previousBlockNumber: number | undefined,
|
|
13
|
-
) {
|
|
14
|
-
super(
|
|
15
|
-
`Cannot insert new block ${newBlockNumber} given previous block number in store is ${
|
|
16
|
-
previousBlockNumber ?? 'undefined'
|
|
17
|
-
}`,
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
9
|
export class BlockNumberNotSequentialError extends Error {
|
|
23
10
|
constructor(newBlockNumber: number, previous: number | undefined) {
|
|
24
|
-
super(
|
|
25
|
-
`Cannot insert new block ${newBlockNumber} given previous block number in batch is ${previous ?? 'undefined'}`,
|
|
26
|
-
);
|
|
11
|
+
super(`Cannot insert new block ${newBlockNumber} given previous block number is ${previous ?? 'undefined'}`);
|
|
27
12
|
}
|
|
28
13
|
}
|
|
29
14
|
|
|
@@ -41,17 +26,13 @@ export class InitialCheckpointNumberNotSequentialError extends Error {
|
|
|
41
26
|
}
|
|
42
27
|
|
|
43
28
|
export class CheckpointNumberNotSequentialError extends Error {
|
|
44
|
-
constructor(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export class CheckpointNumberNotConsistentError extends Error {
|
|
52
|
-
constructor(newCheckpointNumber: number, previous: number | undefined) {
|
|
29
|
+
constructor(
|
|
30
|
+
newCheckpointNumber: number,
|
|
31
|
+
previous: number | undefined,
|
|
32
|
+
source: 'confirmed' | 'proposed' = 'confirmed',
|
|
33
|
+
) {
|
|
53
34
|
super(
|
|
54
|
-
`Cannot insert
|
|
35
|
+
`Cannot insert new checkpoint ${newCheckpointNumber} given previous ${source} checkpoint number is ${previous ?? 'undefined'}`,
|
|
55
36
|
);
|
|
56
37
|
}
|
|
57
38
|
}
|
|
@@ -89,6 +70,69 @@ export class BlockNotFoundError extends Error {
|
|
|
89
70
|
}
|
|
90
71
|
}
|
|
91
72
|
|
|
73
|
+
/** Thrown when a proposed block matches a block that was already checkpointed. This is expected for late proposals. */
|
|
74
|
+
export class BlockAlreadyCheckpointedError extends Error {
|
|
75
|
+
constructor(public readonly blockNumber: number) {
|
|
76
|
+
super(`Block ${blockNumber} has already been checkpointed with the same content`);
|
|
77
|
+
this.name = 'BlockAlreadyCheckpointedError';
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Thrown when logs are added for a tag whose last stored log has a higher block number than the new log. */
|
|
82
|
+
export class OutOfOrderLogInsertionError extends Error {
|
|
83
|
+
constructor(
|
|
84
|
+
public readonly logType: 'private' | 'public',
|
|
85
|
+
public readonly tag: string,
|
|
86
|
+
public readonly lastBlockNumber: number,
|
|
87
|
+
public readonly newBlockNumber: number,
|
|
88
|
+
) {
|
|
89
|
+
super(
|
|
90
|
+
`Out-of-order ${logType} log insertion for tag ${tag}: ` +
|
|
91
|
+
`last existing log is from block ${lastBlockNumber} but new log is from block ${newBlockNumber}`,
|
|
92
|
+
);
|
|
93
|
+
this.name = 'OutOfOrderLogInsertionError';
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Thrown when L1 to L2 messages are requested for a checkpoint whose message tree hasn't been sealed yet. */
|
|
98
|
+
export class L1ToL2MessagesNotReadyError extends Error {
|
|
99
|
+
constructor(
|
|
100
|
+
public readonly checkpointNumber: number,
|
|
101
|
+
public readonly inboxTreeInProgress: bigint,
|
|
102
|
+
) {
|
|
103
|
+
super(
|
|
104
|
+
`Cannot get L1 to L2 messages for checkpoint ${checkpointNumber}: ` +
|
|
105
|
+
`inbox tree in progress is ${inboxTreeInProgress}, messages not yet sealed`,
|
|
106
|
+
);
|
|
107
|
+
this.name = 'L1ToL2MessagesNotReadyError';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Thrown when a proposed checkpoint number is stale (already processed). */
|
|
112
|
+
export class ProposedCheckpointStaleError extends Error {
|
|
113
|
+
constructor(
|
|
114
|
+
public readonly proposedCheckpointNumber: number,
|
|
115
|
+
public readonly currentProposedNumber: number,
|
|
116
|
+
) {
|
|
117
|
+
super(`Stale proposed checkpoint ${proposedCheckpointNumber}: current proposed is ${currentProposedNumber}`);
|
|
118
|
+
this.name = 'ProposedCheckpointStaleError';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Thrown when a proposed checkpoint number is not the expected confirmed + 1. */
|
|
123
|
+
export class ProposedCheckpointNotSequentialError extends Error {
|
|
124
|
+
constructor(
|
|
125
|
+
public readonly proposedCheckpointNumber: number,
|
|
126
|
+
public readonly confirmedCheckpointNumber: number,
|
|
127
|
+
) {
|
|
128
|
+
super(
|
|
129
|
+
`Proposed checkpoint ${proposedCheckpointNumber} is not sequential: expected ${confirmedCheckpointNumber + 1} (confirmed + 1)`,
|
|
130
|
+
);
|
|
131
|
+
this.name = 'ProposedCheckpointNotSequentialError';
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Thrown when a proposed block conflicts with an already checkpointed block (different content). */
|
|
92
136
|
export class CannotOverwriteCheckpointedBlockError extends Error {
|
|
93
137
|
constructor(
|
|
94
138
|
public readonly blockNumber: number,
|
package/src/factory.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
2
2
|
import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
3
|
+
import { makeL1HttpTransport } from '@aztec/ethereum/client';
|
|
3
4
|
import { InboxContract, RollupContract } from '@aztec/ethereum/contracts';
|
|
4
5
|
import type { ViemPublicDebugClient } from '@aztec/ethereum/types';
|
|
5
6
|
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
@@ -7,18 +8,17 @@ import { Buffer32 } from '@aztec/foundation/buffer';
|
|
|
7
8
|
import { merge } from '@aztec/foundation/collection';
|
|
8
9
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
9
10
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
10
|
-
import type { DataStoreConfig } from '@aztec/kv-store/config';
|
|
11
11
|
import { createStore } from '@aztec/kv-store/lmdb-v2';
|
|
12
12
|
import { protocolContractNames } from '@aztec/protocol-contracts';
|
|
13
13
|
import { BundledProtocolContractsProvider } from '@aztec/protocol-contracts/providers/bundle';
|
|
14
14
|
import { FunctionType, decodeFunctionSignature } from '@aztec/stdlib/abi';
|
|
15
15
|
import type { ArchiverEmitter } from '@aztec/stdlib/block';
|
|
16
|
-
import { type
|
|
17
|
-
import type {
|
|
16
|
+
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';
|
|
21
|
-
import { createPublicClient
|
|
21
|
+
import { createPublicClient } from 'viem';
|
|
22
22
|
|
|
23
23
|
import { Archiver, type ArchiverDeps } from './archiver.js';
|
|
24
24
|
import { type ArchiverConfig, mapArchiverConfig } from './config.js';
|
|
@@ -32,14 +32,13 @@ export const ARCHIVER_STORE_NAME = 'archiver';
|
|
|
32
32
|
/** Creates an archiver store. */
|
|
33
33
|
export async function createArchiverStore(
|
|
34
34
|
userConfig: Pick<ArchiverConfig, 'archiverStoreMapSizeKb' | 'maxLogs'> & DataStoreConfig,
|
|
35
|
-
l1Constants: Pick<L1RollupConstants, 'epochDuration'>,
|
|
36
35
|
) {
|
|
37
36
|
const config = {
|
|
38
37
|
...userConfig,
|
|
39
38
|
dataStoreMapSizeKb: userConfig.archiverStoreMapSizeKb ?? userConfig.dataStoreMapSizeKb,
|
|
40
39
|
};
|
|
41
40
|
const store = await createStore(ARCHIVER_STORE_NAME, ARCHIVER_DB_VERSION, config);
|
|
42
|
-
return new KVArchiverDataStore(store, config.maxLogs
|
|
41
|
+
return new KVArchiverDataStore(store, config.maxLogs);
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
/**
|
|
@@ -54,14 +53,15 @@ export async function createArchiver(
|
|
|
54
53
|
deps: ArchiverDeps,
|
|
55
54
|
opts: { blockUntilSync: boolean } = { blockUntilSync: true },
|
|
56
55
|
): Promise<Archiver> {
|
|
57
|
-
const archiverStore = await createArchiverStore(config
|
|
56
|
+
const archiverStore = await createArchiverStore(config);
|
|
58
57
|
await registerProtocolContracts(archiverStore);
|
|
59
58
|
|
|
60
59
|
// Create Ethereum clients
|
|
61
60
|
const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
|
|
61
|
+
const httpTimeout = config.l1HttpTimeoutMS;
|
|
62
62
|
const publicClient = createPublicClient({
|
|
63
63
|
chain: chain.chainInfo,
|
|
64
|
-
transport:
|
|
64
|
+
transport: makeL1HttpTransport(config.l1RpcUrls, { timeout: httpTimeout }),
|
|
65
65
|
pollingInterval: config.viemPollingIntervalMS,
|
|
66
66
|
});
|
|
67
67
|
|
|
@@ -69,7 +69,7 @@ export async function createArchiver(
|
|
|
69
69
|
const debugRpcUrls = config.l1DebugRpcUrls.length > 0 ? config.l1DebugRpcUrls : config.l1RpcUrls;
|
|
70
70
|
const debugClient = createPublicClient({
|
|
71
71
|
chain: chain.chainInfo,
|
|
72
|
-
transport:
|
|
72
|
+
transport: makeL1HttpTransport(debugRpcUrls, { timeout: httpTimeout }),
|
|
73
73
|
pollingInterval: config.viemPollingIntervalMS,
|
|
74
74
|
}) as ViemPublicDebugClient;
|
|
75
75
|
|
|
@@ -85,6 +85,7 @@ export async function createArchiver(
|
|
|
85
85
|
genesisArchiveRoot,
|
|
86
86
|
slashingProposerAddress,
|
|
87
87
|
targetCommitteeSize,
|
|
88
|
+
rollupManaLimit,
|
|
88
89
|
] = await Promise.all([
|
|
89
90
|
rollup.getL1StartBlock(),
|
|
90
91
|
rollup.getL1GenesisTime(),
|
|
@@ -92,6 +93,7 @@ export async function createArchiver(
|
|
|
92
93
|
rollup.getGenesisArchiveTreeRoot(),
|
|
93
94
|
rollup.getSlashingProposerAddress(),
|
|
94
95
|
rollup.getTargetCommitteeSize(),
|
|
96
|
+
rollup.getManaLimit(),
|
|
95
97
|
] as const);
|
|
96
98
|
|
|
97
99
|
const l1StartBlockHash = await publicClient
|
|
@@ -110,6 +112,7 @@ export async function createArchiver(
|
|
|
110
112
|
proofSubmissionEpochs: Number(proofSubmissionEpochs),
|
|
111
113
|
targetCommitteeSize,
|
|
112
114
|
genesisArchiveRoot: Fr.fromString(genesisArchiveRoot.toString()),
|
|
115
|
+
rollupManaLimit: Number(rollupManaLimit),
|
|
113
116
|
};
|
|
114
117
|
|
|
115
118
|
const archiverConfig = merge(
|
|
@@ -170,16 +173,22 @@ export async function createArchiver(
|
|
|
170
173
|
return archiver;
|
|
171
174
|
}
|
|
172
175
|
|
|
173
|
-
/** Registers protocol contracts in the archiver store. */
|
|
176
|
+
/** Registers protocol contracts in the archiver store. Idempotent — skips contracts that already exist (e.g. on node restart). */
|
|
174
177
|
export async function registerProtocolContracts(store: KVArchiverDataStore) {
|
|
175
178
|
const blockNumber = 0;
|
|
176
179
|
for (const name of protocolContractNames) {
|
|
177
180
|
const provider = new BundledProtocolContractsProvider();
|
|
178
181
|
const contract = await provider.getProtocolContractArtifact(name);
|
|
179
|
-
|
|
182
|
+
|
|
183
|
+
// Skip if already registered (happens on node restart with a persisted store).
|
|
184
|
+
if (await store.getContractClass(contract.contractClass.id)) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const publicBytecodeCommitment = await computePublicBytecodeCommitment(contract.contractClass.packedBytecode);
|
|
189
|
+
const contractClassPublic: ContractClassPublicWithCommitment = {
|
|
180
190
|
...contract.contractClass,
|
|
181
|
-
|
|
182
|
-
utilityFunctions: [],
|
|
191
|
+
publicBytecodeCommitment,
|
|
183
192
|
};
|
|
184
193
|
|
|
185
194
|
const publicFunctionSignatures = contract.artifact.functions
|
|
@@ -187,8 +196,7 @@ export async function registerProtocolContracts(store: KVArchiverDataStore) {
|
|
|
187
196
|
.map(fn => decodeFunctionSignature(fn.name, fn.parameters));
|
|
188
197
|
|
|
189
198
|
await store.registerContractFunctionSignatures(publicFunctionSignatures);
|
|
190
|
-
|
|
191
|
-
await store.addContractClasses([contractClassPublic], [bytecodeCommitment], BlockNumber(blockNumber));
|
|
199
|
+
await store.addContractClasses([contractClassPublic], BlockNumber(blockNumber));
|
|
192
200
|
await store.addContractInstances([contract.instance], BlockNumber(blockNumber));
|
|
193
201
|
}
|
|
194
202
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
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';
|
|
4
5
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
5
6
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
7
|
import type { Logger } from '@aztec/foundation/log';
|
|
@@ -44,7 +45,7 @@ type CheckpointData = {
|
|
|
44
45
|
*/
|
|
45
46
|
export class CalldataRetriever {
|
|
46
47
|
/** Tx hashes we've already logged for trace+debug failure (log once per tx per process). */
|
|
47
|
-
private static readonly traceFailureWarnedTxHashes = new
|
|
48
|
+
private static readonly traceFailureWarnedTxHashes = new LruSet<string>(1000);
|
|
48
49
|
|
|
49
50
|
/** Clears the trace-failure warned set. For testing only. */
|
|
50
51
|
static resetTraceFailureWarnedForTesting(): void {
|
package/src/l1/data_retrieval.ts
CHANGED
|
@@ -144,7 +144,7 @@ export async function retrievedToPublishedCheckpoint({
|
|
|
144
144
|
* @param blobClient - The blob client client for fetching blob data.
|
|
145
145
|
* @param searchStartBlock - The block number to use for starting the search.
|
|
146
146
|
* @param searchEndBlock - The highest block number that we should search up to.
|
|
147
|
-
* @param contractAddresses - The contract addresses (governanceProposerAddress,
|
|
147
|
+
* @param contractAddresses - The contract addresses (governanceProposerAddress, slashingProposerAddress).
|
|
148
148
|
* @param instrumentation - The archiver instrumentation instance.
|
|
149
149
|
* @param logger - The logger instance.
|
|
150
150
|
* @param isHistoricalSync - Whether this is a historical sync.
|
|
@@ -265,6 +265,9 @@ 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
|
+
|
|
268
271
|
const checkpointBlobData = await getCheckpointBlobDataFromBlobs(
|
|
269
272
|
blobClient,
|
|
270
273
|
checkpoint.blockHash,
|
|
@@ -272,12 +275,8 @@ async function processCheckpointProposedLogs(
|
|
|
272
275
|
checkpointNumber,
|
|
273
276
|
logger,
|
|
274
277
|
isHistoricalSync,
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const l1 = new L1PublishedData(
|
|
278
|
-
log.l1BlockNumber,
|
|
279
|
-
await getL1BlockTime(publicClient, log.l1BlockNumber),
|
|
280
|
-
log.l1BlockHash.toString(),
|
|
278
|
+
parentBeaconBlockRoot,
|
|
279
|
+
timestamp,
|
|
281
280
|
);
|
|
282
281
|
|
|
283
282
|
retrievedCheckpoints.push({ ...checkpoint, checkpointBlobData, l1, chainId, version });
|
|
@@ -298,9 +297,12 @@ async function processCheckpointProposedLogs(
|
|
|
298
297
|
return retrievedCheckpoints;
|
|
299
298
|
}
|
|
300
299
|
|
|
301
|
-
export async function
|
|
300
|
+
export async function getL1Block(
|
|
301
|
+
publicClient: ViemPublicClient,
|
|
302
|
+
blockNumber: bigint,
|
|
303
|
+
): Promise<{ timestamp: bigint; parentBeaconBlockRoot: string | undefined }> {
|
|
302
304
|
const block = await publicClient.getBlock({ blockNumber, includeTransactions: false });
|
|
303
|
-
return block.timestamp;
|
|
305
|
+
return { timestamp: block.timestamp, parentBeaconBlockRoot: block.parentBeaconBlockRoot };
|
|
304
306
|
}
|
|
305
307
|
|
|
306
308
|
export async function getCheckpointBlobDataFromBlobs(
|
|
@@ -310,8 +312,14 @@ export async function getCheckpointBlobDataFromBlobs(
|
|
|
310
312
|
checkpointNumber: CheckpointNumber,
|
|
311
313
|
logger: Logger,
|
|
312
314
|
isHistoricalSync: boolean,
|
|
315
|
+
parentBeaconBlockRoot?: string,
|
|
316
|
+
l1BlockTimestamp?: bigint,
|
|
313
317
|
): Promise<CheckpointBlobData> {
|
|
314
|
-
const blobBodies = await blobClient.getBlobSidecar(blockHash, blobHashes, {
|
|
318
|
+
const blobBodies = await blobClient.getBlobSidecar(blockHash, blobHashes, {
|
|
319
|
+
isHistoricalSync,
|
|
320
|
+
parentBeaconBlockRoot,
|
|
321
|
+
l1BlockTimestamp,
|
|
322
|
+
});
|
|
315
323
|
if (blobBodies.length === 0) {
|
|
316
324
|
throw new NoBlobBodiesFoundError(checkpointNumber);
|
|
317
325
|
}
|
|
@@ -336,14 +344,10 @@ export async function getCheckpointBlobDataFromBlobs(
|
|
|
336
344
|
/** Given an L1 to L2 message, retrieves its corresponding event from the Inbox within a specific block range. */
|
|
337
345
|
export async function retrieveL1ToL2Message(
|
|
338
346
|
inbox: InboxContract,
|
|
339
|
-
|
|
340
|
-
fromBlock: bigint,
|
|
341
|
-
toBlock: bigint,
|
|
347
|
+
message: InboxMessage,
|
|
342
348
|
): Promise<InboxMessage | undefined> {
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
const messages = mapLogsInboxMessage(logs);
|
|
346
|
-
return messages.length > 0 ? messages[0] : undefined;
|
|
349
|
+
const log = await inbox.getMessageSentEventByHash(message.leaf.toString(), message.l1BlockHash.toString());
|
|
350
|
+
return log && mapLogInboxMessage(log);
|
|
347
351
|
}
|
|
348
352
|
|
|
349
353
|
/**
|
|
@@ -366,22 +370,22 @@ export async function retrieveL1ToL2Messages(
|
|
|
366
370
|
break;
|
|
367
371
|
}
|
|
368
372
|
|
|
369
|
-
retrievedL1ToL2Messages.push(...
|
|
373
|
+
retrievedL1ToL2Messages.push(...messageSentLogs.map(mapLogInboxMessage));
|
|
370
374
|
searchStartBlock = messageSentLogs.at(-1)!.l1BlockNumber + 1n;
|
|
371
375
|
}
|
|
372
376
|
|
|
373
377
|
return retrievedL1ToL2Messages;
|
|
374
378
|
}
|
|
375
379
|
|
|
376
|
-
function
|
|
377
|
-
return
|
|
380
|
+
function mapLogInboxMessage(log: MessageSentLog): InboxMessage {
|
|
381
|
+
return {
|
|
378
382
|
index: log.args.index,
|
|
379
383
|
leaf: log.args.leaf,
|
|
380
384
|
l1BlockNumber: log.l1BlockNumber,
|
|
381
385
|
l1BlockHash: log.l1BlockHash,
|
|
382
386
|
checkpointNumber: log.args.checkpointNumber,
|
|
383
387
|
rollingHash: log.args.rollingHash,
|
|
384
|
-
}
|
|
388
|
+
};
|
|
385
389
|
}
|
|
386
390
|
|
|
387
391
|
/** Retrieves L2ProofVerified events from the rollup contract. */
|