@aztec/archiver 0.0.1-commit.d1f2d6c → 0.0.1-commit.d20b825a7
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 +16 -10
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +110 -122
- package/dest/config.d.ts +5 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +15 -3
- package/dest/errors.d.ts +55 -10
- package/dest/errors.d.ts.map +1 -1
- package/dest/errors.js +74 -15
- package/dest/factory.d.ts +5 -4
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +34 -29
- package/dest/index.d.ts +4 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +3 -1
- package/dest/l1/bin/retrieve-calldata.js +36 -33
- package/dest/l1/calldata_retriever.d.ts +73 -50
- package/dest/l1/calldata_retriever.d.ts.map +1 -1
- package/dest/l1/calldata_retriever.js +191 -259
- package/dest/l1/data_retrieval.d.ts +26 -17
- package/dest/l1/data_retrieval.d.ts.map +1 -1
- package/dest/l1/data_retrieval.js +43 -48
- package/dest/l1/spire_proposer.d.ts +5 -5
- package/dest/l1/spire_proposer.d.ts.map +1 -1
- package/dest/l1/spire_proposer.js +9 -17
- package/dest/l1/validate_historical_logs.d.ts +23 -0
- package/dest/l1/validate_historical_logs.d.ts.map +1 -0
- package/dest/l1/validate_historical_logs.js +108 -0
- package/dest/l1/validate_trace.d.ts +6 -3
- package/dest/l1/validate_trace.d.ts.map +1 -1
- package/dest/l1/validate_trace.js +13 -9
- package/dest/modules/data_source_base.d.ts +17 -10
- package/dest/modules/data_source_base.d.ts.map +1 -1
- package/dest/modules/data_source_base.js +39 -77
- package/dest/modules/data_store_updater.d.ts +50 -26
- package/dest/modules/data_store_updater.d.ts.map +1 -1
- package/dest/modules/data_store_updater.js +169 -130
- package/dest/modules/instrumentation.d.ts +21 -3
- package/dest/modules/instrumentation.d.ts.map +1 -1
- package/dest/modules/instrumentation.js +58 -18
- package/dest/modules/l1_synchronizer.d.ts +10 -9
- package/dest/modules/l1_synchronizer.d.ts.map +1 -1
- package/dest/modules/l1_synchronizer.js +285 -157
- 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 +85 -36
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +433 -162
- 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 +16 -72
- 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 +76 -32
- package/dest/store/kv_archiver_store.d.ts.map +1 -1
- package/dest/store/kv_archiver_store.js +92 -37
- package/dest/store/l2_tips_cache.d.ts +20 -0
- package/dest/store/l2_tips_cache.d.ts.map +1 -0
- package/dest/store/l2_tips_cache.js +109 -0
- 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 +151 -56
- 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 +24 -1
- package/dest/test/fake_l1_state.d.ts.map +1 -1
- package/dest/test/fake_l1_state.js +145 -28
- package/dest/test/index.js +3 -1
- package/dest/test/mock_archiver.d.ts +1 -1
- package/dest/test/mock_archiver.d.ts.map +1 -1
- package/dest/test/mock_archiver.js +3 -2
- 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 +31 -10
- package/dest/test/mock_l2_block_source.d.ts.map +1 -1
- package/dest/test/mock_l2_block_source.js +163 -92
- package/dest/test/mock_structs.d.ts +6 -2
- package/dest/test/mock_structs.d.ts.map +1 -1
- package/dest/test/mock_structs.js +20 -6
- package/dest/test/noop_l1_archiver.d.ts +26 -0
- package/dest/test/noop_l1_archiver.d.ts.map +1 -0
- package/dest/test/noop_l1_archiver.js +74 -0
- package/package.json +14 -13
- package/src/archiver.ts +150 -146
- package/src/config.ts +22 -2
- package/src/errors.ts +116 -26
- package/src/factory.ts +49 -26
- package/src/index.ts +3 -1
- package/src/l1/README.md +25 -68
- package/src/l1/bin/retrieve-calldata.ts +46 -39
- package/src/l1/calldata_retriever.ts +250 -379
- package/src/l1/data_retrieval.ts +59 -70
- package/src/l1/spire_proposer.ts +7 -15
- package/src/l1/validate_historical_logs.ts +140 -0
- package/src/l1/validate_trace.ts +24 -6
- package/src/modules/data_source_base.ts +81 -101
- package/src/modules/data_store_updater.ts +202 -160
- package/src/modules/instrumentation.ts +71 -19
- package/src/modules/l1_synchronizer.ts +365 -197
- package/src/modules/validation.ts +2 -2
- package/src/store/block_store.ts +546 -206
- package/src/store/contract_class_store.ts +16 -110
- package/src/store/contract_instance_store.ts +8 -5
- package/src/store/kv_archiver_store.ts +143 -53
- package/src/store/l2_tips_cache.ts +134 -0
- package/src/store/log_store.ts +225 -67
- package/src/store/message_store.ts +27 -10
- package/src/structs/inbox_message.ts +1 -1
- package/src/test/fake_l1_state.ts +193 -32
- package/src/test/index.ts +3 -0
- package/src/test/mock_archiver.ts +3 -2
- package/src/test/mock_l1_to_l2_message_source.ts +1 -0
- package/src/test/mock_l2_block_source.ts +217 -90
- package/src/test/mock_structs.ts +42 -12
- package/src/test/noop_l1_archiver.ts +117 -0
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/archiver",
|
|
3
|
-
"version": "0.0.1-commit.
|
|
3
|
+
"version": "0.0.1-commit.d20b825a7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
7
7
|
"./test": "./dest/test/index.js",
|
|
8
|
+
"./test/noop-l1": "./dest/test/noop_l1_archiver.js",
|
|
8
9
|
"./config": "./dest/config.js"
|
|
9
10
|
},
|
|
10
11
|
"typedocOptions": {
|
|
@@ -64,18 +65,18 @@
|
|
|
64
65
|
]
|
|
65
66
|
},
|
|
66
67
|
"dependencies": {
|
|
67
|
-
"@aztec/blob-client": "0.0.1-commit.
|
|
68
|
-
"@aztec/blob-lib": "0.0.1-commit.
|
|
69
|
-
"@aztec/constants": "0.0.1-commit.
|
|
70
|
-
"@aztec/epoch-cache": "0.0.1-commit.
|
|
71
|
-
"@aztec/ethereum": "0.0.1-commit.
|
|
72
|
-
"@aztec/foundation": "0.0.1-commit.
|
|
73
|
-
"@aztec/kv-store": "0.0.1-commit.
|
|
74
|
-
"@aztec/l1-artifacts": "0.0.1-commit.
|
|
75
|
-
"@aztec/noir-protocol-circuits-types": "0.0.1-commit.
|
|
76
|
-
"@aztec/protocol-contracts": "0.0.1-commit.
|
|
77
|
-
"@aztec/stdlib": "0.0.1-commit.
|
|
78
|
-
"@aztec/telemetry-client": "0.0.1-commit.
|
|
68
|
+
"@aztec/blob-client": "0.0.1-commit.d20b825a7",
|
|
69
|
+
"@aztec/blob-lib": "0.0.1-commit.d20b825a7",
|
|
70
|
+
"@aztec/constants": "0.0.1-commit.d20b825a7",
|
|
71
|
+
"@aztec/epoch-cache": "0.0.1-commit.d20b825a7",
|
|
72
|
+
"@aztec/ethereum": "0.0.1-commit.d20b825a7",
|
|
73
|
+
"@aztec/foundation": "0.0.1-commit.d20b825a7",
|
|
74
|
+
"@aztec/kv-store": "0.0.1-commit.d20b825a7",
|
|
75
|
+
"@aztec/l1-artifacts": "0.0.1-commit.d20b825a7",
|
|
76
|
+
"@aztec/noir-protocol-circuits-types": "0.0.1-commit.d20b825a7",
|
|
77
|
+
"@aztec/protocol-contracts": "0.0.1-commit.d20b825a7",
|
|
78
|
+
"@aztec/stdlib": "0.0.1-commit.d20b825a7",
|
|
79
|
+
"@aztec/telemetry-client": "0.0.1-commit.d20b825a7",
|
|
79
80
|
"lodash.groupby": "^4.6.0",
|
|
80
81
|
"lodash.omit": "^4.5.0",
|
|
81
82
|
"tslib": "^2.5.0",
|
package/src/archiver.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
2
|
-
import { GENESIS_BLOCK_HEADER_HASH, INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
3
2
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
4
3
|
import { BlockTagTooOldError, RollupContract } from '@aztec/ethereum/contracts';
|
|
5
4
|
import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
|
|
@@ -12,34 +11,34 @@ import { EthAddress } from '@aztec/foundation/eth-address';
|
|
|
12
11
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
13
12
|
import { type PromiseWithResolvers, promiseWithResolvers } from '@aztec/foundation/promise';
|
|
14
13
|
import { RunningPromise, makeLoggingErrorHandler } from '@aztec/foundation/running-promise';
|
|
15
|
-
import { DateProvider } from '@aztec/foundation/timer';
|
|
14
|
+
import { DateProvider, elapsed } from '@aztec/foundation/timer';
|
|
16
15
|
import {
|
|
17
16
|
type ArchiverEmitter,
|
|
18
|
-
type CheckpointId,
|
|
19
|
-
GENESIS_CHECKPOINT_HEADER_HASH,
|
|
20
17
|
L2Block,
|
|
21
18
|
type L2BlockSink,
|
|
22
19
|
type L2Tips,
|
|
23
20
|
type ValidateCheckpointResult,
|
|
24
21
|
} from '@aztec/stdlib/block';
|
|
25
|
-
import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
22
|
+
import { type ProposedCheckpointInput, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
26
23
|
import {
|
|
27
24
|
type L1RollupConstants,
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
getEpochAtSlot,
|
|
26
|
+
getSlotAtNextL1Block,
|
|
30
27
|
getSlotRangeForEpoch,
|
|
31
28
|
getTimestampRangeForEpoch,
|
|
32
29
|
} from '@aztec/stdlib/epoch-helpers';
|
|
33
30
|
import { type TelemetryClient, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
|
|
34
31
|
|
|
35
32
|
import { type ArchiverConfig, mapArchiverConfig } from './config.js';
|
|
36
|
-
import { NoBlobBodiesFoundError } from './errors.js';
|
|
33
|
+
import { BlockAlreadyCheckpointedError, NoBlobBodiesFoundError } from './errors.js';
|
|
34
|
+
import { validateAndLogHistoricalLogsAvailability } from './l1/validate_historical_logs.js';
|
|
37
35
|
import { validateAndLogTraceAvailability } from './l1/validate_trace.js';
|
|
38
36
|
import { ArchiverDataSourceBase } from './modules/data_source_base.js';
|
|
39
37
|
import { ArchiverDataStoreUpdater } from './modules/data_store_updater.js';
|
|
40
38
|
import type { ArchiverInstrumentation } from './modules/instrumentation.js';
|
|
41
39
|
import type { ArchiverL1Synchronizer } from './modules/l1_synchronizer.js';
|
|
42
40
|
import type { KVArchiverDataStore } from './store/kv_archiver_store.js';
|
|
41
|
+
import { L2TipsCache } from './store/l2_tips_cache.js';
|
|
43
42
|
|
|
44
43
|
/** Export ArchiverEmitter for use in factory and tests. */
|
|
45
44
|
export type { ArchiverEmitter };
|
|
@@ -68,7 +67,7 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
68
67
|
public readonly events: ArchiverEmitter;
|
|
69
68
|
|
|
70
69
|
/** A loop in which we will be continually fetching new checkpoints. */
|
|
71
|
-
|
|
70
|
+
protected runningPromise: RunningPromise;
|
|
72
71
|
|
|
73
72
|
/** L1 synchronizer that handles fetching checkpoints and messages from L1. */
|
|
74
73
|
private readonly synchronizer: ArchiverL1Synchronizer;
|
|
@@ -82,19 +81,23 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
82
81
|
/** Helper to handle updates to the store */
|
|
83
82
|
private readonly updater: ArchiverDataStoreUpdater;
|
|
84
83
|
|
|
84
|
+
/** In-memory cache for L2 chain tips. */
|
|
85
|
+
private readonly l2TipsCache: L2TipsCache;
|
|
86
|
+
|
|
85
87
|
public readonly tracer: Tracer;
|
|
86
88
|
|
|
89
|
+
private readonly instrumentation: ArchiverInstrumentation;
|
|
90
|
+
|
|
87
91
|
/**
|
|
88
92
|
* Creates a new instance of the Archiver.
|
|
89
93
|
* @param publicClient - A client for interacting with the Ethereum node.
|
|
90
94
|
* @param debugClient - A client for interacting with the Ethereum node for debug/trace methods.
|
|
91
95
|
* @param rollup - Rollup contract instance.
|
|
92
96
|
* @param inbox - Inbox contract instance.
|
|
93
|
-
* @param l1Addresses - L1 contract addresses (registry, governance proposer,
|
|
97
|
+
* @param l1Addresses - L1 contract addresses (registry, governance proposer, slashing proposer).
|
|
94
98
|
* @param dataStore - An archiver data store for storage & retrieval of blocks, encrypted logs & contract data.
|
|
95
99
|
* @param config - Archiver configuration options.
|
|
96
100
|
* @param blobClient - Client for retrieving blob data.
|
|
97
|
-
* @param epochCache - Cache for epoch-related data.
|
|
98
101
|
* @param dateProvider - Provider for current date/time.
|
|
99
102
|
* @param instrumentation - Instrumentation for metrics and tracing.
|
|
100
103
|
* @param l1Constants - L1 rollup constants.
|
|
@@ -106,8 +109,10 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
106
109
|
private readonly rollup: RollupContract,
|
|
107
110
|
private readonly l1Addresses: Pick<
|
|
108
111
|
L1ContractAddresses,
|
|
109
|
-
'registryAddress' | '
|
|
110
|
-
> & {
|
|
112
|
+
'rollupAddress' | 'registryAddress' | 'inboxAddress' | 'governanceProposerAddress'
|
|
113
|
+
> & {
|
|
114
|
+
slashingProposerAddress: EthAddress;
|
|
115
|
+
},
|
|
111
116
|
readonly dataStore: KVArchiverDataStore,
|
|
112
117
|
private config: {
|
|
113
118
|
pollingIntervalMs: number;
|
|
@@ -115,21 +120,30 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
115
120
|
skipValidateCheckpointAttestations?: boolean;
|
|
116
121
|
maxAllowedEthClientDriftSeconds: number;
|
|
117
122
|
ethereumAllowNoDebugHosts?: boolean;
|
|
123
|
+
skipHistoricalLogsCheck?: boolean;
|
|
118
124
|
},
|
|
119
125
|
private readonly blobClient: BlobClientInterface,
|
|
120
126
|
instrumentation: ArchiverInstrumentation,
|
|
121
|
-
protected override readonly l1Constants: L1RollupConstants & {
|
|
127
|
+
protected override readonly l1Constants: L1RollupConstants & {
|
|
128
|
+
l1StartBlockHash: Buffer32;
|
|
129
|
+
genesisArchiveRoot: Fr;
|
|
130
|
+
},
|
|
122
131
|
synchronizer: ArchiverL1Synchronizer,
|
|
123
132
|
events: ArchiverEmitter,
|
|
133
|
+
l2TipsCache?: L2TipsCache,
|
|
124
134
|
private readonly log: Logger = createLogger('archiver'),
|
|
125
135
|
) {
|
|
126
136
|
super(dataStore, l1Constants);
|
|
127
137
|
|
|
128
138
|
this.tracer = instrumentation.tracer;
|
|
139
|
+
this.instrumentation = instrumentation;
|
|
129
140
|
this.initialSyncPromise = promiseWithResolvers();
|
|
130
141
|
this.synchronizer = synchronizer;
|
|
131
142
|
this.events = events;
|
|
132
|
-
this.
|
|
143
|
+
this.l2TipsCache = l2TipsCache ?? new L2TipsCache(this.dataStore.blockStore);
|
|
144
|
+
this.updater = new ArchiverDataStoreUpdater(this.dataStore, this.l2TipsCache, {
|
|
145
|
+
rollupManaLimit: l1Constants.rollupManaLimit,
|
|
146
|
+
});
|
|
133
147
|
|
|
134
148
|
// Running promise starts with a small interval inbetween runs, so all iterations needed for the initial sync
|
|
135
149
|
// are done as fast as possible. This then gets updated once the initial sync completes.
|
|
@@ -158,7 +172,22 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
158
172
|
|
|
159
173
|
await this.blobClient.testSources();
|
|
160
174
|
await this.synchronizer.testEthereumNodeSynced();
|
|
161
|
-
await validateAndLogTraceAvailability(
|
|
175
|
+
await validateAndLogTraceAvailability(
|
|
176
|
+
this.debugClient,
|
|
177
|
+
this.config.ethereumAllowNoDebugHosts ?? false,
|
|
178
|
+
this.log.getBindings(),
|
|
179
|
+
);
|
|
180
|
+
await validateAndLogHistoricalLogsAvailability(
|
|
181
|
+
this.publicClient,
|
|
182
|
+
{
|
|
183
|
+
rollupAddress: this.l1Addresses.rollupAddress,
|
|
184
|
+
inboxAddress: this.l1Addresses.inboxAddress,
|
|
185
|
+
registryAddress: this.l1Addresses.registryAddress,
|
|
186
|
+
governanceProposerAddress: this.l1Addresses.governanceProposerAddress,
|
|
187
|
+
},
|
|
188
|
+
this.config.skipHistoricalLogsCheck ?? false,
|
|
189
|
+
this.log.getBindings(),
|
|
190
|
+
);
|
|
162
191
|
|
|
163
192
|
// Log initial state for the archiver
|
|
164
193
|
const { l1StartBlock } = this.l1Constants;
|
|
@@ -198,6 +227,10 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
198
227
|
});
|
|
199
228
|
}
|
|
200
229
|
|
|
230
|
+
public async setProposedCheckpoint(pending: ProposedCheckpointInput): Promise<void> {
|
|
231
|
+
await this.updater.setProposedCheckpoint(pending);
|
|
232
|
+
}
|
|
233
|
+
|
|
201
234
|
/**
|
|
202
235
|
* Processes all queued blocks, adding them to the store.
|
|
203
236
|
* Called at the beginning of each sync iteration.
|
|
@@ -212,13 +245,34 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
212
245
|
const queuedItems = this.blockQueue.splice(0, this.blockQueue.length);
|
|
213
246
|
this.log.debug(`Processing ${queuedItems.length} queued block(s)`);
|
|
214
247
|
|
|
248
|
+
// Calculate slot threshold for validation
|
|
249
|
+
const l1Timestamp = this.synchronizer.getL1Timestamp();
|
|
250
|
+
const slotAtNextL1Block =
|
|
251
|
+
l1Timestamp === undefined ? undefined : getSlotAtNextL1Block(l1Timestamp, this.l1Constants);
|
|
252
|
+
|
|
215
253
|
// Process each block individually to properly resolve/reject each promise
|
|
216
254
|
for (const { block, resolve, reject } of queuedItems) {
|
|
255
|
+
const blockSlot = block.header.globalVariables.slotNumber;
|
|
256
|
+
if (slotAtNextL1Block !== undefined && blockSlot < slotAtNextL1Block) {
|
|
257
|
+
this.log.warn(
|
|
258
|
+
`Rejecting proposed block ${block.number} for past slot ${blockSlot} (current is ${slotAtNextL1Block})`,
|
|
259
|
+
{ block: block.toBlockInfo(), l1Timestamp, slotAtNextL1Block },
|
|
260
|
+
);
|
|
261
|
+
reject(new Error(`Block ${block.number} is for past slot ${blockSlot} (current is ${slotAtNextL1Block})`));
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
|
|
217
265
|
try {
|
|
218
|
-
await this.updater.
|
|
266
|
+
const [durationMs] = await elapsed(() => this.updater.addProposedBlock(block));
|
|
267
|
+
this.instrumentation.processNewProposedBlock(durationMs, block);
|
|
219
268
|
this.log.debug(`Added block ${block.number} to store`);
|
|
220
269
|
resolve();
|
|
221
270
|
} catch (err: any) {
|
|
271
|
+
if (err instanceof BlockAlreadyCheckpointedError) {
|
|
272
|
+
this.log.debug(`Proposed block ${block.number} matches already checkpointed block, ignoring late proposal`);
|
|
273
|
+
resolve();
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
222
276
|
this.log.error(`Failed to add block ${block.number} to store: ${err.message}`);
|
|
223
277
|
reject(err);
|
|
224
278
|
}
|
|
@@ -310,16 +364,49 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
310
364
|
return Promise.resolve(this.synchronizer.getL1Timestamp());
|
|
311
365
|
}
|
|
312
366
|
|
|
313
|
-
public
|
|
367
|
+
public async getSyncedL2SlotNumber(): Promise<SlotNumber | undefined> {
|
|
368
|
+
// The synced L2 slot is the latest slot for which we have all L1 data,
|
|
369
|
+
// either because we have seen all L1 blocks for that slot, or because
|
|
370
|
+
// we have seen the corresponding checkpoint.
|
|
371
|
+
|
|
372
|
+
let slotFromL1Sync: SlotNumber | undefined;
|
|
314
373
|
const l1Timestamp = this.synchronizer.getL1Timestamp();
|
|
315
|
-
|
|
374
|
+
if (l1Timestamp !== undefined) {
|
|
375
|
+
const nextL1BlockSlot = getSlotAtNextL1Block(l1Timestamp, this.l1Constants);
|
|
376
|
+
if (Number(nextL1BlockSlot) > 0) {
|
|
377
|
+
slotFromL1Sync = SlotNumber.add(nextL1BlockSlot, -1);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
let slotFromCheckpoint: SlotNumber | undefined;
|
|
382
|
+
const latestCheckpointNumber = await this.store.getSynchedCheckpointNumber();
|
|
383
|
+
if (latestCheckpointNumber > 0) {
|
|
384
|
+
const checkpointData = await this.store.getCheckpointData(latestCheckpointNumber);
|
|
385
|
+
if (checkpointData) {
|
|
386
|
+
slotFromCheckpoint = checkpointData.header.slotNumber;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (slotFromL1Sync === undefined && slotFromCheckpoint === undefined) {
|
|
391
|
+
return undefined;
|
|
392
|
+
}
|
|
393
|
+
return SlotNumber(Math.max(slotFromL1Sync ?? 0, slotFromCheckpoint ?? 0));
|
|
316
394
|
}
|
|
317
395
|
|
|
318
|
-
public
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
396
|
+
public async getSyncedL2EpochNumber(): Promise<EpochNumber | undefined> {
|
|
397
|
+
const syncedSlot = await this.getSyncedL2SlotNumber();
|
|
398
|
+
if (syncedSlot === undefined) {
|
|
399
|
+
return undefined;
|
|
400
|
+
}
|
|
401
|
+
// An epoch is fully synced when all its slots are synced.
|
|
402
|
+
// We check if syncedSlot is the last slot of its epoch; if so, that epoch is fully synced.
|
|
403
|
+
// Otherwise, only the previous epoch is fully synced.
|
|
404
|
+
const epoch = getEpochAtSlot(syncedSlot, this.l1Constants);
|
|
405
|
+
const [, endSlot] = getSlotRangeForEpoch(epoch, this.l1Constants);
|
|
406
|
+
if (syncedSlot >= endSlot) {
|
|
407
|
+
return epoch;
|
|
408
|
+
}
|
|
409
|
+
return Number(epoch) > 0 ? EpochNumber(Number(epoch) - 1) : undefined;
|
|
323
410
|
}
|
|
324
411
|
|
|
325
412
|
public async isEpochComplete(epochNumber: EpochNumber): Promise<boolean> {
|
|
@@ -358,8 +445,8 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
358
445
|
return this.initialSyncComplete;
|
|
359
446
|
}
|
|
360
447
|
|
|
361
|
-
public
|
|
362
|
-
return this.updater.
|
|
448
|
+
public removeCheckpointsAfter(checkpointNumber: CheckpointNumber): Promise<boolean> {
|
|
449
|
+
return this.updater.removeCheckpointsAfter(checkpointNumber);
|
|
363
450
|
}
|
|
364
451
|
|
|
365
452
|
/** Used by TXE to add checkpoints directly without syncing from L1. */
|
|
@@ -367,119 +454,15 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
367
454
|
checkpoints: PublishedCheckpoint[],
|
|
368
455
|
pendingChainValidationStatus?: ValidateCheckpointResult,
|
|
369
456
|
): Promise<boolean> {
|
|
370
|
-
await this.updater.
|
|
457
|
+
await this.updater.addCheckpoints(checkpoints, pendingChainValidationStatus);
|
|
371
458
|
return true;
|
|
372
459
|
}
|
|
373
460
|
|
|
374
|
-
public
|
|
375
|
-
|
|
376
|
-
this.getBlockNumber(),
|
|
377
|
-
this.getProvenBlockNumber(),
|
|
378
|
-
this.getCheckpointedL2BlockNumber(),
|
|
379
|
-
this.getFinalizedL2BlockNumber(),
|
|
380
|
-
] as const);
|
|
381
|
-
|
|
382
|
-
const beforeInitialblockNumber = BlockNumber(INITIAL_L2_BLOCK_NUM - 1);
|
|
383
|
-
|
|
384
|
-
// Get the latest block header and checkpointed blocks for proven, finalised and checkpointed blocks
|
|
385
|
-
const [latestBlockHeader, provenCheckpointedBlock, finalizedCheckpointedBlock, checkpointedBlock] =
|
|
386
|
-
await Promise.all([
|
|
387
|
-
latestBlockNumber > beforeInitialblockNumber ? this.getBlockHeader(latestBlockNumber) : undefined,
|
|
388
|
-
provenBlockNumber > beforeInitialblockNumber ? this.getCheckpointedBlock(provenBlockNumber) : undefined,
|
|
389
|
-
finalizedBlockNumber > beforeInitialblockNumber ? this.getCheckpointedBlock(finalizedBlockNumber) : undefined,
|
|
390
|
-
checkpointedBlockNumber > beforeInitialblockNumber
|
|
391
|
-
? this.getCheckpointedBlock(checkpointedBlockNumber)
|
|
392
|
-
: undefined,
|
|
393
|
-
] as const);
|
|
394
|
-
|
|
395
|
-
if (latestBlockNumber > beforeInitialblockNumber && !latestBlockHeader) {
|
|
396
|
-
throw new Error(`Failed to retrieve latest block header for block ${latestBlockNumber}`);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// Checkpointed blocks must exist for proven, finalized and checkpointed tips if they are beyond the initial block number.
|
|
400
|
-
if (checkpointedBlockNumber > beforeInitialblockNumber && !checkpointedBlock?.block.header) {
|
|
401
|
-
throw new Error(
|
|
402
|
-
`Failed to retrieve checkpointed block header for block ${checkpointedBlockNumber} (latest block is ${latestBlockNumber})`,
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
if (provenBlockNumber > beforeInitialblockNumber && !provenCheckpointedBlock?.block.header) {
|
|
407
|
-
throw new Error(
|
|
408
|
-
`Failed to retrieve proven checkpointed for block ${provenBlockNumber} (latest block is ${latestBlockNumber})`,
|
|
409
|
-
);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
if (finalizedBlockNumber > beforeInitialblockNumber && !finalizedCheckpointedBlock?.block.header) {
|
|
413
|
-
throw new Error(
|
|
414
|
-
`Failed to retrieve finalized block header for block ${finalizedBlockNumber} (latest block is ${latestBlockNumber})`,
|
|
415
|
-
);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
const latestBlockHeaderHash = (await latestBlockHeader?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
|
|
419
|
-
const provenBlockHeaderHash = (await provenCheckpointedBlock?.block.header?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
|
|
420
|
-
const finalizedBlockHeaderHash =
|
|
421
|
-
(await finalizedCheckpointedBlock?.block.header?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
|
|
422
|
-
const checkpointedBlockHeaderHash = (await checkpointedBlock?.block.header?.hash()) ?? GENESIS_BLOCK_HEADER_HASH;
|
|
423
|
-
|
|
424
|
-
// Now attempt to retrieve checkpoints for proven, finalised and checkpointed blocks
|
|
425
|
-
const [[provenBlockCheckpoint], [finalizedBlockCheckpoint], [checkpointedBlockCheckpoint]] = await Promise.all([
|
|
426
|
-
provenCheckpointedBlock !== undefined
|
|
427
|
-
? await this.getCheckpoints(provenCheckpointedBlock?.checkpointNumber, 1)
|
|
428
|
-
: [undefined],
|
|
429
|
-
finalizedCheckpointedBlock !== undefined
|
|
430
|
-
? await this.getCheckpoints(finalizedCheckpointedBlock?.checkpointNumber, 1)
|
|
431
|
-
: [undefined],
|
|
432
|
-
checkpointedBlock !== undefined ? await this.getCheckpoints(checkpointedBlock?.checkpointNumber, 1) : [undefined],
|
|
433
|
-
]);
|
|
434
|
-
|
|
435
|
-
const initialcheckpointId: CheckpointId = {
|
|
436
|
-
number: CheckpointNumber.ZERO,
|
|
437
|
-
hash: GENESIS_CHECKPOINT_HEADER_HASH.toString(),
|
|
438
|
-
};
|
|
439
|
-
|
|
440
|
-
const makeCheckpointId = (checkpoint: PublishedCheckpoint | undefined) => {
|
|
441
|
-
if (checkpoint === undefined) {
|
|
442
|
-
return initialcheckpointId;
|
|
443
|
-
}
|
|
444
|
-
return {
|
|
445
|
-
number: checkpoint.checkpoint.number,
|
|
446
|
-
hash: checkpoint.checkpoint.hash().toString(),
|
|
447
|
-
};
|
|
448
|
-
};
|
|
449
|
-
|
|
450
|
-
const l2Tips: L2Tips = {
|
|
451
|
-
proposed: {
|
|
452
|
-
number: latestBlockNumber,
|
|
453
|
-
hash: latestBlockHeaderHash.toString(),
|
|
454
|
-
},
|
|
455
|
-
proven: {
|
|
456
|
-
block: {
|
|
457
|
-
number: provenBlockNumber,
|
|
458
|
-
hash: provenBlockHeaderHash.toString(),
|
|
459
|
-
},
|
|
460
|
-
checkpoint: makeCheckpointId(provenBlockCheckpoint),
|
|
461
|
-
},
|
|
462
|
-
finalized: {
|
|
463
|
-
block: {
|
|
464
|
-
number: finalizedBlockNumber,
|
|
465
|
-
hash: finalizedBlockHeaderHash.toString(),
|
|
466
|
-
},
|
|
467
|
-
checkpoint: makeCheckpointId(finalizedBlockCheckpoint),
|
|
468
|
-
},
|
|
469
|
-
checkpointed: {
|
|
470
|
-
block: {
|
|
471
|
-
number: checkpointedBlockNumber,
|
|
472
|
-
hash: checkpointedBlockHeaderHash.toString(),
|
|
473
|
-
},
|
|
474
|
-
checkpoint: makeCheckpointId(checkpointedBlockCheckpoint),
|
|
475
|
-
},
|
|
476
|
-
};
|
|
477
|
-
|
|
478
|
-
return l2Tips;
|
|
461
|
+
public getL2Tips(): Promise<L2Tips> {
|
|
462
|
+
return this.l2TipsCache.getL2Tips();
|
|
479
463
|
}
|
|
480
464
|
|
|
481
465
|
public async rollbackTo(targetL2BlockNumber: BlockNumber): Promise<void> {
|
|
482
|
-
// TODO(pw/mbps): This still assumes 1 block per checkpoint
|
|
483
466
|
const currentBlocks = await this.getL2Tips();
|
|
484
467
|
const currentL2Block = currentBlocks.proposed.number;
|
|
485
468
|
const currentProvenBlock = currentBlocks.proven.block.number;
|
|
@@ -487,13 +470,29 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
487
470
|
if (targetL2BlockNumber >= currentL2Block) {
|
|
488
471
|
throw new Error(`Target L2 block ${targetL2BlockNumber} must be less than current L2 block ${currentL2Block}`);
|
|
489
472
|
}
|
|
490
|
-
const blocksToUnwind = currentL2Block - targetL2BlockNumber;
|
|
491
473
|
const targetL2Block = await this.store.getCheckpointedBlock(targetL2BlockNumber);
|
|
492
474
|
if (!targetL2Block) {
|
|
493
475
|
throw new Error(`Target L2 block ${targetL2BlockNumber} not found`);
|
|
494
476
|
}
|
|
477
|
+
const targetCheckpointNumber = targetL2Block.checkpointNumber;
|
|
478
|
+
|
|
479
|
+
// Rollback operates at checkpoint granularity: the target block must be the last block of its checkpoint.
|
|
480
|
+
const checkpointData = await this.store.getCheckpointData(targetCheckpointNumber);
|
|
481
|
+
if (checkpointData) {
|
|
482
|
+
const lastBlockInCheckpoint = BlockNumber(checkpointData.startBlock + checkpointData.blockCount - 1);
|
|
483
|
+
if (targetL2BlockNumber !== lastBlockInCheckpoint) {
|
|
484
|
+
const previousCheckpointBoundary =
|
|
485
|
+
checkpointData.startBlock > 1 ? BlockNumber(checkpointData.startBlock - 1) : BlockNumber(0);
|
|
486
|
+
throw new Error(
|
|
487
|
+
`Target L2 block ${targetL2BlockNumber} is not at a checkpoint boundary. ` +
|
|
488
|
+
`Checkpoint ${targetCheckpointNumber} spans blocks ${checkpointData.startBlock} to ${lastBlockInCheckpoint}. ` +
|
|
489
|
+
`Use block ${lastBlockInCheckpoint} to roll back to this checkpoint, ` +
|
|
490
|
+
`or block ${previousCheckpointBoundary} to roll back to the previous one.`,
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
495
|
const targetL1BlockNumber = targetL2Block.l1.blockNumber;
|
|
496
|
-
const targetCheckpointNumber = CheckpointNumber.fromBlockNumber(targetL2BlockNumber);
|
|
497
496
|
const targetL1Block = await this.publicClient.getBlock({
|
|
498
497
|
blockNumber: targetL1BlockNumber,
|
|
499
498
|
includeTransactions: false,
|
|
@@ -502,21 +501,26 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
|
|
|
502
501
|
throw new Error(`Missing L1 block ${targetL1BlockNumber}`);
|
|
503
502
|
}
|
|
504
503
|
const targetL1BlockHash = Buffer32.fromString(targetL1Block.hash);
|
|
505
|
-
this.log.info(
|
|
506
|
-
|
|
507
|
-
|
|
504
|
+
this.log.info(
|
|
505
|
+
`Removing checkpoints after checkpoint ${targetCheckpointNumber} (target block ${targetL2BlockNumber})`,
|
|
506
|
+
);
|
|
507
|
+
await this.updater.removeCheckpointsAfter(targetCheckpointNumber);
|
|
508
|
+
this.log.info(`Rolling back L1 to L2 messages to checkpoint ${targetCheckpointNumber}`);
|
|
508
509
|
await this.store.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber);
|
|
509
510
|
this.log.info(`Setting L1 syncpoints to ${targetL1BlockNumber}`);
|
|
510
511
|
await this.store.setCheckpointSynchedL1BlockNumber(targetL1BlockNumber);
|
|
511
|
-
await this.store.
|
|
512
|
+
await this.store.setMessageSyncState(
|
|
513
|
+
{ l1BlockNumber: targetL1BlockNumber, l1BlockHash: targetL1BlockHash },
|
|
514
|
+
undefined,
|
|
515
|
+
);
|
|
512
516
|
if (targetL2BlockNumber < currentProvenBlock) {
|
|
513
|
-
this.log.info(`
|
|
514
|
-
await this.
|
|
517
|
+
this.log.info(`Rolling back proven L2 checkpoint to ${targetCheckpointNumber}`);
|
|
518
|
+
await this.updater.setProvenCheckpointNumber(targetCheckpointNumber);
|
|
519
|
+
}
|
|
520
|
+
const currentFinalizedBlock = currentBlocks.finalized.block.number;
|
|
521
|
+
if (targetL2BlockNumber < currentFinalizedBlock) {
|
|
522
|
+
this.log.info(`Rolling back finalized L2 checkpoint to ${targetCheckpointNumber}`);
|
|
523
|
+
await this.updater.setFinalizedCheckpointNumber(targetCheckpointNumber);
|
|
515
524
|
}
|
|
516
|
-
// TODO(palla/reorg): Set the finalized block when we add support for it.
|
|
517
|
-
// if (targetL2BlockNumber < currentFinalizedBlock) {
|
|
518
|
-
// this.log.info(`Clearing finalized L2 block number`);
|
|
519
|
-
// await this.store.setFinalizedL2BlockNumber(0);
|
|
520
|
-
// }
|
|
521
525
|
}
|
|
522
526
|
}
|
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,13 +50,17 @@ 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: {
|
|
50
57
|
description: 'Skip validating checkpoint attestations (for testing purposes only)',
|
|
51
58
|
...booleanConfigHelper(false),
|
|
52
59
|
},
|
|
60
|
+
skipPromoteProposedCheckpointDuringL1Sync: {
|
|
61
|
+
description: 'Skip promoting proposed checkpoints during L1 sync (for testing purposes only)',
|
|
62
|
+
...booleanConfigHelper(false),
|
|
63
|
+
},
|
|
53
64
|
maxAllowedEthClientDriftSeconds: {
|
|
54
65
|
env: 'MAX_ALLOWED_ETH_CLIENT_DRIFT_SECONDS',
|
|
55
66
|
description: 'Maximum allowed drift in seconds between the Ethereum client and current time.',
|
|
@@ -60,6 +71,13 @@ export const archiverConfigMappings: ConfigMappingsType<ArchiverConfig> = {
|
|
|
60
71
|
description: 'Whether to allow starting the archiver without debug/trace method support on Ethereum hosts',
|
|
61
72
|
...booleanConfigHelper(true),
|
|
62
73
|
},
|
|
74
|
+
archiverSkipHistoricalLogsCheck: {
|
|
75
|
+
env: 'ARCHIVER_SKIP_HISTORICAL_LOGS_CHECK',
|
|
76
|
+
description:
|
|
77
|
+
'Skip the startup check that probes the L1 RPC for historical Rollup contract logs. ' +
|
|
78
|
+
'Set to true to bypass the check when the connected RPC node is known to prune old logs.',
|
|
79
|
+
...booleanConfigHelper(false),
|
|
80
|
+
},
|
|
63
81
|
...chainConfigMappings,
|
|
64
82
|
...l1ReaderConfigMappings,
|
|
65
83
|
viemPollingIntervalMS: {
|
|
@@ -89,7 +107,9 @@ export function mapArchiverConfig(config: Partial<ArchiverConfig>) {
|
|
|
89
107
|
pollingIntervalMs: config.archiverPollingIntervalMS,
|
|
90
108
|
batchSize: config.archiverBatchSize,
|
|
91
109
|
skipValidateCheckpointAttestations: config.skipValidateCheckpointAttestations,
|
|
110
|
+
skipPromoteProposedCheckpointDuringL1Sync: config.skipPromoteProposedCheckpointDuringL1Sync,
|
|
92
111
|
maxAllowedEthClientDriftSeconds: config.maxAllowedEthClientDriftSeconds,
|
|
93
112
|
ethereumAllowNoDebugHosts: config.ethereumAllowNoDebugHosts,
|
|
113
|
+
skipHistoricalLogsCheck: config.archiverSkipHistoricalLogsCheck,
|
|
94
114
|
};
|
|
95
115
|
}
|