@aztec/archiver 0.0.1-commit.f504929 → 0.0.1-commit.f5d02921e
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 +5 -5
- package/dest/archiver.d.ts.map +1 -1
- package/dest/archiver.js +57 -19
- package/dest/config.d.ts +3 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +2 -1
- package/dest/errors.d.ts +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/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/l1_synchronizer.d.ts +2 -1
- package/dest/modules/l1_synchronizer.d.ts.map +1 -1
- package/dest/modules/l1_synchronizer.js +60 -25
- 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 +49 -16
- package/dest/store/block_store.d.ts.map +1 -1
- package/dest/store/block_store.js +243 -118
- 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 +46 -19
- package/dest/store/kv_archiver_store.d.ts.map +1 -1
- package/dest/store/kv_archiver_store.js +57 -22
- 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 +25 -5
- 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 +93 -16
- 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 +14 -1
- 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 +39 -5
- 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 -1
- package/package.json +13 -13
- package/src/archiver.ts +65 -21
- package/src/config.ts +8 -1
- package/src/errors.ts +70 -26
- package/src/factory.ts +23 -15
- package/src/l1/calldata_retriever.ts +2 -1
- package/src/modules/data_source_base.ts +26 -7
- package/src/modules/data_store_updater.ts +96 -107
- package/src/modules/l1_synchronizer.ts +72 -32
- package/src/modules/validation.ts +2 -2
- package/src/store/block_store.ts +312 -138
- 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 +83 -34
- package/src/store/l2_tips_cache.ts +50 -11
- package/src/store/log_store.ts +126 -27
- package/src/store/message_store.ts +20 -1
- package/src/test/fake_l1_state.ts +50 -9
- 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 -1
|
@@ -3,9 +3,10 @@ import { EpochCache } from '@aztec/epoch-cache';
|
|
|
3
3
|
import { InboxContract, RollupContract } from '@aztec/ethereum/contracts';
|
|
4
4
|
import type { L1BlockId } from '@aztec/ethereum/l1-types';
|
|
5
5
|
import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
|
|
6
|
+
import { asyncPool } from '@aztec/foundation/async-pool';
|
|
6
7
|
import { maxBigint } from '@aztec/foundation/bigint';
|
|
7
8
|
import { BlockNumber, CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
|
|
8
|
-
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
9
|
+
import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
|
|
9
10
|
import { pick } from '@aztec/foundation/collection';
|
|
10
11
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
11
12
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
@@ -69,13 +70,18 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
69
70
|
private readonly epochCache: EpochCache,
|
|
70
71
|
private readonly dateProvider: DateProvider,
|
|
71
72
|
private readonly instrumentation: ArchiverInstrumentation,
|
|
72
|
-
private readonly l1Constants: L1RollupConstants & {
|
|
73
|
+
private readonly l1Constants: L1RollupConstants & {
|
|
74
|
+
l1StartBlockHash: Buffer32;
|
|
75
|
+
genesisArchiveRoot: Fr;
|
|
76
|
+
},
|
|
73
77
|
private readonly events: ArchiverEmitter,
|
|
74
78
|
tracer: Tracer,
|
|
75
79
|
l2TipsCache?: L2TipsCache,
|
|
76
80
|
private readonly log: Logger = createLogger('archiver:l1-sync'),
|
|
77
81
|
) {
|
|
78
|
-
this.updater = new ArchiverDataStoreUpdater(this.store, l2TipsCache
|
|
82
|
+
this.updater = new ArchiverDataStoreUpdater(this.store, l2TipsCache, {
|
|
83
|
+
rollupManaLimit: l1Constants.rollupManaLimit,
|
|
84
|
+
});
|
|
79
85
|
this.tracer = tracer;
|
|
80
86
|
}
|
|
81
87
|
|
|
@@ -211,6 +217,9 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
211
217
|
this.instrumentation.updateL1BlockHeight(currentL1BlockNumber);
|
|
212
218
|
}
|
|
213
219
|
|
|
220
|
+
// Update the finalized L2 checkpoint based on L1 finality.
|
|
221
|
+
await this.updateFinalizedCheckpoint();
|
|
222
|
+
|
|
214
223
|
// After syncing has completed, update the current l1 block number and timestamp,
|
|
215
224
|
// otherwise we risk announcing to the world that we've synced to a given point,
|
|
216
225
|
// but the corresponding blocks have not been processed (see #12631).
|
|
@@ -226,6 +235,30 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
226
235
|
});
|
|
227
236
|
}
|
|
228
237
|
|
|
238
|
+
/** Query L1 for its finalized block and update the finalized checkpoint accordingly. */
|
|
239
|
+
private async updateFinalizedCheckpoint(): Promise<void> {
|
|
240
|
+
try {
|
|
241
|
+
const finalizedL1Block = await this.publicClient.getBlock({ blockTag: 'finalized', includeTransactions: false });
|
|
242
|
+
const finalizedL1BlockNumber = finalizedL1Block.number;
|
|
243
|
+
const finalizedCheckpointNumber = await this.rollup.getProvenCheckpointNumber({
|
|
244
|
+
blockNumber: finalizedL1BlockNumber,
|
|
245
|
+
});
|
|
246
|
+
const localFinalizedCheckpointNumber = await this.store.getFinalizedCheckpointNumber();
|
|
247
|
+
if (localFinalizedCheckpointNumber !== finalizedCheckpointNumber) {
|
|
248
|
+
await this.updater.setFinalizedCheckpointNumber(finalizedCheckpointNumber);
|
|
249
|
+
this.log.info(`Updated finalized chain to checkpoint ${finalizedCheckpointNumber}`, {
|
|
250
|
+
finalizedCheckpointNumber,
|
|
251
|
+
finalizedL1BlockNumber,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
} catch (err: any) {
|
|
255
|
+
// The rollup contract may not exist at the finalized L1 block right after deployment.
|
|
256
|
+
if (!err?.message?.includes('returned no data')) {
|
|
257
|
+
this.log.warn(`Failed to update finalized checkpoint: ${err}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
229
262
|
/** Prune all proposed local blocks that should have been checkpointed by now. */
|
|
230
263
|
private async pruneUncheckpointedBlocks(currentL1Timestamp: bigint) {
|
|
231
264
|
const [lastCheckpointedBlockNumber, lastProposedBlockNumber] = await Promise.all([
|
|
@@ -239,29 +272,32 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
239
272
|
return;
|
|
240
273
|
}
|
|
241
274
|
|
|
242
|
-
// What's the slot
|
|
275
|
+
// What's the slot at the next L1 block? All blocks for slots strictly before this one should've been checkpointed by now.
|
|
276
|
+
const slotAtNextL1Block = getSlotAtNextL1Block(currentL1Timestamp, this.l1Constants);
|
|
243
277
|
const firstUncheckpointedBlockNumber = BlockNumber(lastCheckpointedBlockNumber + 1);
|
|
278
|
+
|
|
279
|
+
// What's the slot of the first uncheckpointed block?
|
|
244
280
|
const [firstUncheckpointedBlockHeader] = await this.store.getBlockHeaders(firstUncheckpointedBlockNumber, 1);
|
|
245
281
|
const firstUncheckpointedBlockSlot = firstUncheckpointedBlockHeader?.getSlot();
|
|
246
282
|
|
|
247
|
-
|
|
248
|
-
|
|
283
|
+
if (firstUncheckpointedBlockSlot === undefined || firstUncheckpointedBlockSlot >= slotAtNextL1Block) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
249
286
|
|
|
250
|
-
// Prune provisional blocks from slots that have ended without being checkpointed
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
287
|
+
// Prune provisional blocks from slots that have ended without being checkpointed.
|
|
288
|
+
// This also clears any proposed checkpoint whose blocks are being pruned.
|
|
289
|
+
this.log.warn(
|
|
290
|
+
`Pruning blocks after block ${lastCheckpointedBlockNumber} due to slot ${firstUncheckpointedBlockSlot} not being checkpointed`,
|
|
291
|
+
{ firstUncheckpointedBlockHeader: firstUncheckpointedBlockHeader.toInspect(), slotAtNextL1Block },
|
|
292
|
+
);
|
|
293
|
+
const prunedBlocks = await this.updater.removeUncheckpointedBlocksAfter(lastCheckpointedBlockNumber);
|
|
257
294
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
295
|
+
if (prunedBlocks.length > 0) {
|
|
296
|
+
this.events.emit(L2BlockSourceEvents.L2PruneUncheckpointed, {
|
|
297
|
+
type: L2BlockSourceEvents.L2PruneUncheckpointed,
|
|
298
|
+
slotNumber: firstUncheckpointedBlockSlot,
|
|
299
|
+
blocks: prunedBlocks,
|
|
300
|
+
});
|
|
265
301
|
}
|
|
266
302
|
}
|
|
267
303
|
|
|
@@ -304,17 +340,20 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
304
340
|
|
|
305
341
|
const checkpointsToUnwind = localPendingCheckpointNumber - provenCheckpointNumber;
|
|
306
342
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
checkpoints
|
|
314
|
-
.filter(isDefined)
|
|
315
|
-
.map(cp => this.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber))),
|
|
343
|
+
// Fetch checkpoints and blocks in bounded batches to avoid unbounded concurrent
|
|
344
|
+
// promises when the gap between local pending and proven checkpoint numbers is large.
|
|
345
|
+
const BATCH_SIZE = 10;
|
|
346
|
+
const indices = Array.from({ length: checkpointsToUnwind }, (_, i) => CheckpointNumber(i + pruneFrom));
|
|
347
|
+
const checkpoints = (await asyncPool(BATCH_SIZE, indices, idx => this.store.getCheckpointData(idx))).filter(
|
|
348
|
+
isDefined,
|
|
316
349
|
);
|
|
317
|
-
const newBlocks =
|
|
350
|
+
const newBlocks = (
|
|
351
|
+
await asyncPool(BATCH_SIZE, checkpoints, cp =>
|
|
352
|
+
this.store.getBlocksForCheckpoint(CheckpointNumber(cp.checkpointNumber)),
|
|
353
|
+
)
|
|
354
|
+
)
|
|
355
|
+
.filter(isDefined)
|
|
356
|
+
.flat();
|
|
318
357
|
|
|
319
358
|
// Emit an event for listening services to react to the chain prune
|
|
320
359
|
this.events.emit(L2BlockSourceEvents.L2PruneUnproven, {
|
|
@@ -362,6 +401,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
362
401
|
const localMessagesInserted = await this.store.getTotalL1ToL2MessageCount();
|
|
363
402
|
const localLastMessage = await this.store.getLastL1ToL2Message();
|
|
364
403
|
const remoteMessagesState = await this.inbox.getState({ blockNumber: currentL1BlockNumber });
|
|
404
|
+
await this.store.setInboxTreeInProgress(remoteMessagesState.treeInProgress);
|
|
365
405
|
|
|
366
406
|
this.log.trace(`Retrieved remote inbox state at L1 block ${currentL1BlockNumber}.`, {
|
|
367
407
|
localMessagesInserted,
|
|
@@ -372,7 +412,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
372
412
|
// Compare message count and rolling hash. If they match, no need to retrieve anything.
|
|
373
413
|
if (
|
|
374
414
|
remoteMessagesState.totalMessagesInserted === localMessagesInserted &&
|
|
375
|
-
remoteMessagesState.messagesRollingHash.equals(localLastMessage?.rollingHash ??
|
|
415
|
+
remoteMessagesState.messagesRollingHash.equals(localLastMessage?.rollingHash ?? Buffer16.ZERO)
|
|
376
416
|
) {
|
|
377
417
|
this.log.trace(
|
|
378
418
|
`No L1 to L2 messages to query between L1 blocks ${messagesSyncPoint.l1BlockNumber} and ${currentL1BlockNumber}.`,
|
|
@@ -822,7 +862,7 @@ export class ArchiverL1Synchronizer implements Traceable {
|
|
|
822
862
|
const prunedCheckpointNumber = result.prunedBlocks[0].checkpointNumber;
|
|
823
863
|
const prunedSlotNumber = result.prunedBlocks[0].header.globalVariables.slotNumber;
|
|
824
864
|
|
|
825
|
-
this.log.
|
|
865
|
+
this.log.info(
|
|
826
866
|
`Pruned ${result.prunedBlocks.length} mismatching blocks for checkpoint ${prunedCheckpointNumber}`,
|
|
827
867
|
{ prunedBlocks: result.prunedBlocks.map(b => b.toBlockInfo()), prunedSlotNumber, prunedCheckpointNumber },
|
|
828
868
|
);
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
getAttestationInfoFromPayload,
|
|
10
10
|
} from '@aztec/stdlib/block';
|
|
11
11
|
import type { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
12
|
-
import { type L1RollupConstants, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
12
|
+
import { type L1RollupConstants, computeQuorum, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
13
13
|
import { ConsensusPayload } from '@aztec/stdlib/p2p';
|
|
14
14
|
|
|
15
15
|
export type { ValidateCheckpointResult };
|
|
@@ -66,7 +66,7 @@ export async function validateCheckpointAttestations(
|
|
|
66
66
|
return { valid: true };
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const requiredAttestationCount =
|
|
69
|
+
const requiredAttestationCount = computeQuorum(committee.length);
|
|
70
70
|
|
|
71
71
|
const failedValidationResult = <TReason extends ValidateCheckpointNegativeResult['reason']>(reason: TReason) => ({
|
|
72
72
|
valid: false as const,
|