@aztec/sequencer-client 0.0.1-commit.9d2bcf6d → 0.0.1-commit.9d619b6c6
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/client/sequencer-client.d.ts +17 -7
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +63 -30
- package/dest/config.d.ts +26 -7
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +52 -36
- package/dest/global_variable_builder/fee_predictor.d.ts +37 -0
- package/dest/global_variable_builder/fee_predictor.d.ts.map +1 -0
- package/dest/global_variable_builder/fee_predictor.js +128 -0
- package/dest/global_variable_builder/fee_provider.d.ts +21 -0
- package/dest/global_variable_builder/fee_provider.d.ts.map +1 -0
- package/dest/global_variable_builder/fee_provider.js +58 -0
- package/dest/global_variable_builder/global_builder.d.ts +15 -16
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +16 -50
- package/dest/global_variable_builder/index.d.ts +4 -2
- package/dest/global_variable_builder/index.d.ts.map +1 -1
- package/dest/global_variable_builder/index.js +2 -0
- package/dest/publisher/config.d.ts +47 -17
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +121 -42
- package/dest/publisher/index.d.ts +2 -1
- package/dest/publisher/index.d.ts.map +1 -1
- package/dest/publisher/l1_tx_failed_store/factory.d.ts +11 -0
- package/dest/publisher/l1_tx_failed_store/factory.d.ts.map +1 -0
- package/dest/publisher/l1_tx_failed_store/factory.js +22 -0
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +58 -0
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +1 -0
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.js +1 -0
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts +15 -0
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts.map +1 -0
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.js +34 -0
- package/dest/publisher/l1_tx_failed_store/index.d.ts +4 -0
- package/dest/publisher/l1_tx_failed_store/index.d.ts.map +1 -0
- package/dest/publisher/l1_tx_failed_store/index.js +2 -0
- package/dest/publisher/sequencer-publisher-factory.d.ts +11 -5
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +27 -3
- package/dest/publisher/sequencer-publisher.d.ts +75 -48
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +432 -139
- package/dest/sequencer/chain_state_overrides.d.ts +25 -0
- package/dest/sequencer/chain_state_overrides.d.ts.map +1 -0
- package/dest/sequencer/chain_state_overrides.js +39 -0
- package/dest/sequencer/checkpoint_proposal_job.d.ts +42 -10
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +531 -216
- package/dest/sequencer/checkpoint_proposal_job_metrics.d.ts +34 -0
- package/dest/sequencer/checkpoint_proposal_job_metrics.d.ts.map +1 -0
- package/dest/sequencer/checkpoint_proposal_job_metrics.js +72 -0
- package/dest/sequencer/checkpoint_voter.d.ts +1 -2
- package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_voter.js +2 -5
- package/dest/sequencer/events.d.ts +7 -1
- package/dest/sequencer/events.d.ts.map +1 -1
- package/dest/sequencer/metrics.d.ts +21 -8
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +110 -21
- package/dest/sequencer/sequencer.d.ts +43 -17
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +158 -96
- package/dest/sequencer/timetable.d.ts +17 -6
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +51 -46
- package/dest/sequencer/types.d.ts +2 -2
- package/dest/sequencer/types.d.ts.map +1 -1
- package/dest/test/index.d.ts +3 -5
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.d.ts +11 -11
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.js +45 -34
- package/dest/test/utils.d.ts +3 -3
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +5 -4
- package/package.json +27 -28
- package/src/client/sequencer-client.ts +81 -30
- package/src/config.ts +70 -46
- package/src/global_variable_builder/README.md +44 -0
- package/src/global_variable_builder/fee_predictor.ts +172 -0
- package/src/global_variable_builder/fee_provider.ts +75 -0
- package/src/global_variable_builder/global_builder.ts +27 -63
- package/src/global_variable_builder/index.ts +3 -1
- package/src/publisher/config.ts +157 -45
- package/src/publisher/index.ts +3 -0
- package/src/publisher/l1_tx_failed_store/factory.ts +32 -0
- package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +57 -0
- package/src/publisher/l1_tx_failed_store/file_store_failed_tx_store.ts +46 -0
- package/src/publisher/l1_tx_failed_store/index.ts +3 -0
- package/src/publisher/sequencer-publisher-factory.ts +38 -9
- package/src/publisher/sequencer-publisher.ts +499 -198
- package/src/sequencer/README.md +83 -13
- package/src/sequencer/chain_state_overrides.ts +87 -0
- package/src/sequencer/checkpoint_proposal_job.ts +678 -237
- package/src/sequencer/checkpoint_proposal_job_metrics.ts +128 -0
- package/src/sequencer/checkpoint_voter.ts +1 -12
- package/src/sequencer/events.ts +6 -1
- package/src/sequencer/metrics.ts +124 -26
- package/src/sequencer/sequencer.ts +227 -108
- package/src/sequencer/timetable.ts +70 -57
- package/src/sequencer/types.ts +1 -1
- package/src/test/index.ts +2 -4
- package/src/test/mock_checkpoint_builder.ts +63 -49
- package/src/test/utils.ts +5 -2
|
@@ -373,14 +373,14 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
|
|
|
373
373
|
var _dec, _dec1, _dec2, _dec3, _initProto;
|
|
374
374
|
import { getKzg } from '@aztec/blob-lib';
|
|
375
375
|
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
376
|
-
import { NoCommitteeError } from '@aztec/ethereum/contracts';
|
|
376
|
+
import { NoCommitteeError, SimulationOverridesBuilder } from '@aztec/ethereum/contracts';
|
|
377
377
|
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
378
378
|
import { merge, omit, pick } from '@aztec/foundation/collection';
|
|
379
379
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
380
380
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
381
381
|
import { createLogger } from '@aztec/foundation/log';
|
|
382
382
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
383
|
-
import {
|
|
383
|
+
import { getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
384
384
|
import { SequencerConfigSchema } from '@aztec/stdlib/interfaces/server';
|
|
385
385
|
import { pickFromSchema } from '@aztec/stdlib/schemas';
|
|
386
386
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
@@ -388,6 +388,7 @@ import { Attributes, getTelemetryClient, trackSpan } from '@aztec/telemetry-clie
|
|
|
388
388
|
import EventEmitter from 'node:events';
|
|
389
389
|
import { DefaultSequencerConfig } from '../config.js';
|
|
390
390
|
import { CheckpointProposalJob } from './checkpoint_proposal_job.js';
|
|
391
|
+
import { CheckpointProposalJobMetrics } from './checkpoint_proposal_job_metrics.js';
|
|
391
392
|
import { CheckpointVoter } from './checkpoint_voter.js';
|
|
392
393
|
import { SequencerInterruptedError, SequencerTooSlowError } from './errors.js';
|
|
393
394
|
import { SequencerMetrics } from './metrics.js';
|
|
@@ -449,19 +450,14 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
449
450
|
runningPromise;
|
|
450
451
|
state;
|
|
451
452
|
metrics;
|
|
453
|
+
checkpointProposalJobMetrics;
|
|
452
454
|
/** The last slot for which we attempted to perform our voting duties with degraded block production */ lastSlotForFallbackVote;
|
|
453
455
|
/** The last slot for which we logged "no committee" warning, to avoid spam */ lastSlotForNoCommitteeWarning;
|
|
454
456
|
/** The last slot for which we triggered a checkpoint proposal job, to prevent duplicate attempts. */ lastSlotForCheckpointProposalJob;
|
|
455
457
|
/** Last successful checkpoint proposed */ lastCheckpointProposed;
|
|
456
458
|
/** The last epoch for which we logged strategy comparison in fisherman mode. */ lastEpochForStrategyComparison;
|
|
459
|
+
/** The last checkpoint proposal job, tracked so we can await its pending L1 submission during shutdown. */ lastCheckpointProposalJob;
|
|
457
460
|
/** The maximum number of seconds that the sequencer can be into a slot to transition to a particular state. */ timetable;
|
|
458
|
-
// This shouldn't be here as this gets re-created each time we build/propose a block.
|
|
459
|
-
// But we have a number of tests that abuse/rely on this class having a permanent publisher.
|
|
460
|
-
// As long as those tests only configure a single publisher they will continue to work.
|
|
461
|
-
// This will get re-assigned every time the sequencer goes to build a new block to a publisher that is valid
|
|
462
|
-
// for the block proposer.
|
|
463
|
-
// TODO(palla/mbps): Remove this field and fix tests
|
|
464
|
-
publisher;
|
|
465
461
|
/** Config for the sequencer */ config;
|
|
466
462
|
constructor(publisherFactory, validatorClient, globalsBuilder, p2pClient, worldState, slasherClient, l2BlockSource, l1ToL2MessageSource, checkpointsBuilder, l1Constants, dateProvider, epochCache, rollupContract, config, telemetry = getTelemetryClient(), log = createLogger('sequencer')){
|
|
467
463
|
super(), this.publisherFactory = publisherFactory, this.validatorClient = validatorClient, this.globalsBuilder = globalsBuilder, this.p2pClient = p2pClient, this.worldState = worldState, this.slasherClient = slasherClient, this.l2BlockSource = l2BlockSource, this.l1ToL2MessageSource = l1ToL2MessageSource, this.checkpointsBuilder = checkpointsBuilder, this.l1Constants = l1Constants, this.dateProvider = dateProvider, this.epochCache = epochCache, this.rollupContract = rollupContract, this.telemetry = telemetry, this.log = log, this.state = (_initProto(this), SequencerState.STOPPED), this.config = DefaultSequencerConfig;
|
|
@@ -470,24 +466,26 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
470
466
|
this.log = log.createChild('[FISHERMAN]');
|
|
471
467
|
}
|
|
472
468
|
this.metrics = new SequencerMetrics(telemetry, this.rollupContract, 'Sequencer');
|
|
469
|
+
this.checkpointProposalJobMetrics = new CheckpointProposalJobMetrics(telemetry);
|
|
473
470
|
this.updateConfig(config);
|
|
474
471
|
}
|
|
475
472
|
/** Updates sequencer config by the defined values and updates the timetable */ updateConfig(config) {
|
|
476
473
|
const filteredConfig = pickFromSchema(config, SequencerConfigSchema);
|
|
477
|
-
this.log.info(`Updated sequencer config`, omit(filteredConfig, '
|
|
474
|
+
this.log.info(`Updated sequencer config`, omit(filteredConfig, 'txPublicSetupAllowListExtend'));
|
|
478
475
|
this.config = merge(this.config, filteredConfig);
|
|
476
|
+
const p2pPropagationTime = this.config.attestationPropagationTime;
|
|
479
477
|
this.timetable = new SequencerTimetable({
|
|
480
478
|
ethereumSlotDuration: this.l1Constants.ethereumSlotDuration,
|
|
481
479
|
aztecSlotDuration: this.aztecSlotDuration,
|
|
482
480
|
l1PublishingTime: this.l1PublishingTime,
|
|
483
|
-
p2pPropagationTime
|
|
481
|
+
p2pPropagationTime,
|
|
484
482
|
blockDurationMs: this.config.blockDurationMs,
|
|
485
|
-
enforce: this.config.enforceTimeTable
|
|
483
|
+
enforce: this.config.enforceTimeTable,
|
|
484
|
+
pipelining: this.epochCache.isProposerPipeliningEnabled()
|
|
486
485
|
}, this.metrics, this.log);
|
|
487
486
|
}
|
|
488
|
-
/** Initializes the sequencer (precomputes tables
|
|
487
|
+
/** Initializes the sequencer (precomputes tables). Takes about 3s. */ init() {
|
|
489
488
|
getKzg();
|
|
490
|
-
this.publisher = (await this.publisherFactory.create(undefined)).publisher;
|
|
491
489
|
}
|
|
492
490
|
/** Starts the sequencer and moves to IDLE state. */ start() {
|
|
493
491
|
this.runningPromise = new RunningPromise(this.safeWork.bind(this), this.log, this.config.sequencerPollingIntervalMS);
|
|
@@ -497,13 +495,17 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
497
495
|
this.runningPromise.start();
|
|
498
496
|
this.log.info('Started sequencer');
|
|
499
497
|
}
|
|
498
|
+
/** Triggers an immediate run of the sequencer, bypassing the polling interval. */ trigger() {
|
|
499
|
+
return this.runningPromise?.trigger();
|
|
500
|
+
}
|
|
500
501
|
/** Stops the sequencer from building blocks and moves to STOPPED state. */ async stop() {
|
|
501
502
|
this.log.info(`Stopping sequencer`);
|
|
502
503
|
this.setState(SequencerState.STOPPING, undefined, {
|
|
503
504
|
force: true
|
|
504
505
|
});
|
|
505
|
-
this.
|
|
506
|
+
await this.publisherFactory.stopAll();
|
|
506
507
|
await this.runningPromise?.stop();
|
|
508
|
+
await this.lastCheckpointProposalJob?.awaitPendingSubmission();
|
|
507
509
|
this.setState(SequencerState.STOPPED, undefined, {
|
|
508
510
|
force: true
|
|
509
511
|
});
|
|
@@ -517,7 +519,6 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
517
519
|
error: err
|
|
518
520
|
});
|
|
519
521
|
if (err instanceof SequencerTooSlowError) {
|
|
520
|
-
// TODO(palla/mbps): Add missing states
|
|
521
522
|
// Log as warn only if we had to abort halfway through the block proposal
|
|
522
523
|
const logLvl = [
|
|
523
524
|
SequencerState.INITIALIZING_CHECKPOINT,
|
|
@@ -548,22 +549,25 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
548
549
|
* - Submit checkpoint
|
|
549
550
|
*/ async work() {
|
|
550
551
|
this.setState(SequencerState.SYNCHRONIZING, undefined);
|
|
551
|
-
const { slot, ts,
|
|
552
|
+
const { slot, ts, nowSeconds, epoch } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
553
|
+
const { slot: targetSlot, epoch: targetEpoch } = this.epochCache.getTargetEpochAndSlotInNextL1Slot();
|
|
552
554
|
// Check if we are synced and it's our slot, grab a publisher, check previous block invalidation, etc
|
|
553
|
-
const checkpointProposalJob = await this.prepareCheckpointProposal(epoch,
|
|
555
|
+
const checkpointProposalJob = await this.prepareCheckpointProposal(slot, targetSlot, epoch, targetEpoch, ts, nowSeconds);
|
|
554
556
|
if (!checkpointProposalJob) {
|
|
555
557
|
return;
|
|
556
558
|
}
|
|
559
|
+
// Track the job so we can await its pending L1 submission during shutdown
|
|
560
|
+
this.lastCheckpointProposalJob = checkpointProposalJob;
|
|
557
561
|
// Execute the checkpoint proposal job
|
|
558
562
|
const checkpoint = await checkpointProposalJob.execute();
|
|
559
563
|
// Update last checkpoint proposed (currently unused)
|
|
560
564
|
if (checkpoint) {
|
|
561
565
|
this.lastCheckpointProposed = checkpoint;
|
|
562
566
|
}
|
|
563
|
-
// Log fee strategy comparison if on fisherman
|
|
564
|
-
if (this.config.fishermanMode && (this.lastEpochForStrategyComparison === undefined ||
|
|
565
|
-
this.logStrategyComparison(
|
|
566
|
-
this.lastEpochForStrategyComparison =
|
|
567
|
+
// Log fee strategy comparison if on fisherman (uses target epoch since we mirror the proposer's perspective)
|
|
568
|
+
if (this.config.fishermanMode && (this.lastEpochForStrategyComparison === undefined || targetEpoch > this.lastEpochForStrategyComparison)) {
|
|
569
|
+
this.logStrategyComparison(targetEpoch, checkpointProposalJob.getPublisher());
|
|
570
|
+
this.lastEpochForStrategyComparison = targetEpoch;
|
|
567
571
|
}
|
|
568
572
|
return checkpoint;
|
|
569
573
|
}
|
|
@@ -571,17 +575,17 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
571
575
|
* Prepares the checkpoint proposal by performing all necessary checks and setup.
|
|
572
576
|
* This is the initial step in the main loop.
|
|
573
577
|
* @returns CheckpointProposalJob if successful, undefined if we are not yet synced or are not the proposer.
|
|
574
|
-
*/ async prepareCheckpointProposal(epoch,
|
|
575
|
-
// Check we have not already processed this slot (cheapest check)
|
|
578
|
+
*/ async prepareCheckpointProposal(slot, targetSlot, epoch, targetEpoch, ts, nowSeconds) {
|
|
579
|
+
// Check we have not already processed this target slot (cheapest check)
|
|
576
580
|
// We only check this if enforce timetable is set, since we want to keep processing the same slot if we are not
|
|
577
581
|
// running against actual time (eg when we use sandbox-style automining)
|
|
578
|
-
if (this.lastSlotForCheckpointProposalJob && this.lastSlotForCheckpointProposalJob >=
|
|
579
|
-
this.log.trace(`
|
|
582
|
+
if (this.lastSlotForCheckpointProposalJob && this.lastSlotForCheckpointProposalJob >= targetSlot && this.config.enforceTimeTable) {
|
|
583
|
+
this.log.trace(`Target slot ${targetSlot} has already been processed`);
|
|
580
584
|
return undefined;
|
|
581
585
|
}
|
|
582
|
-
// But if we have already proposed for this slot,
|
|
583
|
-
if (this.lastCheckpointProposed && this.lastCheckpointProposed.header.slotNumber >=
|
|
584
|
-
this.log.trace(`Slot ${
|
|
586
|
+
// But if we have already proposed for this slot, then we definitely have to skip it, automining or not
|
|
587
|
+
if (this.lastCheckpointProposed && this.lastCheckpointProposed.header.slotNumber >= targetSlot) {
|
|
588
|
+
this.log.trace(`Slot ${targetSlot} has already been published as checkpoint ${this.lastCheckpointProposed.number}`);
|
|
585
589
|
return undefined;
|
|
586
590
|
}
|
|
587
591
|
// Check all components are synced to latest as seen by the archiver (queries all subsystems)
|
|
@@ -592,16 +596,18 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
592
596
|
if (!syncedTo) {
|
|
593
597
|
await this.tryVoteWhenSyncFails({
|
|
594
598
|
slot,
|
|
599
|
+
targetSlot,
|
|
595
600
|
ts
|
|
596
601
|
});
|
|
597
602
|
return undefined;
|
|
598
603
|
}
|
|
599
|
-
// If escape hatch is open for
|
|
604
|
+
// If escape hatch is open for the target epoch, do not start checkpoint proposal work and do not attempt invalidations.
|
|
600
605
|
// Still perform governance/slashing voting (as proposer) once per slot.
|
|
601
|
-
|
|
606
|
+
// When pipelining, we check the target epoch (slot+1's epoch) since that's the epoch we're building for.
|
|
607
|
+
const isEscapeHatchOpen = await this.epochCache.isEscapeHatchOpen(targetEpoch);
|
|
602
608
|
if (isEscapeHatchOpen) {
|
|
603
609
|
this.setState(SequencerState.PROPOSER_CHECK, slot);
|
|
604
|
-
const [canPropose, proposer] = await this.checkCanPropose(
|
|
610
|
+
const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
|
|
605
611
|
if (canPropose) {
|
|
606
612
|
await this.tryVoteWhenEscapeHatchOpen({
|
|
607
613
|
slot,
|
|
@@ -619,29 +625,36 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
619
625
|
// Next checkpoint follows from the last synced one
|
|
620
626
|
const checkpointNumber = CheckpointNumber(syncedTo.checkpointNumber + 1);
|
|
621
627
|
const logCtx = {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
syncedToL2Slot: getSlotAtTimestamp(syncedTo.l1Timestamp, this.l1Constants),
|
|
628
|
+
nowSeconds,
|
|
629
|
+
syncedToL2Slot: syncedTo.syncedL2Slot,
|
|
625
630
|
slot,
|
|
631
|
+
targetSlot,
|
|
626
632
|
slotTs: ts,
|
|
627
633
|
checkpointNumber,
|
|
628
634
|
isPendingChainValid: pick(syncedTo.pendingChainValidationStatus, 'valid', 'reason', 'invalidIndex')
|
|
629
635
|
};
|
|
630
|
-
// Check that we are a proposer for the
|
|
636
|
+
// Check that we are a proposer for the target slot.
|
|
631
637
|
this.setState(SequencerState.PROPOSER_CHECK, slot);
|
|
632
|
-
const [canPropose, proposer] = await this.checkCanPropose(
|
|
638
|
+
const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
|
|
633
639
|
// If we are not a proposer check if we should invalidate an invalid checkpoint, and bail
|
|
634
640
|
if (!canPropose) {
|
|
635
641
|
await this.considerInvalidatingCheckpoint(syncedTo, slot);
|
|
636
642
|
return undefined;
|
|
637
643
|
}
|
|
638
|
-
//
|
|
639
|
-
|
|
640
|
-
|
|
644
|
+
// Guard: don't exceed 1-deep pipeline. Without a proposed checkpoint, we can only build
|
|
645
|
+
// confirmed + 1. With a proposed checkpoint, we can build confirmed + 2.
|
|
646
|
+
const confirmedCkpt = syncedTo.checkpointedCheckpointNumber;
|
|
647
|
+
if (checkpointNumber > confirmedCkpt + 2) {
|
|
648
|
+
this.log.verbose(`Skipping slot ${targetSlot}: checkpoint ${checkpointNumber} exceeds max pipeline depth (confirmed=${confirmedCkpt})`);
|
|
649
|
+
return undefined;
|
|
650
|
+
}
|
|
651
|
+
// Check that the target slot is not taken by a block already (should never happen, since only us can propose for this slot)
|
|
652
|
+
if (syncedTo.blockData && syncedTo.blockData.header.getSlot() >= targetSlot) {
|
|
653
|
+
this.log.warn(`Cannot propose block at target slot ${targetSlot} since that slot was taken by block ${syncedTo.blockNumber}`, {
|
|
641
654
|
...logCtx,
|
|
642
|
-
block: syncedTo.
|
|
655
|
+
block: syncedTo.blockData.header.toInspect()
|
|
643
656
|
});
|
|
644
|
-
this.metrics.
|
|
657
|
+
this.metrics.recordCheckpointPrecheckFailed('slot_already_taken');
|
|
645
658
|
return undefined;
|
|
646
659
|
}
|
|
647
660
|
// We now need to get ourselves a publisher.
|
|
@@ -651,37 +664,58 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
651
664
|
const proposerForPublisher = this.config.fishermanMode ? undefined : proposer;
|
|
652
665
|
const { attestorAddress, publisher } = await this.publisherFactory.create(proposerForPublisher);
|
|
653
666
|
this.log.verbose(`Created publisher at address ${publisher.getSenderAddress()} for attestor ${attestorAddress}`);
|
|
654
|
-
this.publisher = publisher;
|
|
655
667
|
// In fisherman mode, set the actual proposer's address for simulations
|
|
656
668
|
if (this.config.fishermanMode && proposer) {
|
|
657
669
|
publisher.setProposerAddressForSimulation(proposer);
|
|
658
670
|
this.log.debug(`Set proposer address ${proposer} for simulation in fisherman mode`);
|
|
659
671
|
}
|
|
660
672
|
// Prepare invalidation request if the pending chain is invalid (returns undefined if no need)
|
|
661
|
-
|
|
662
|
-
//
|
|
663
|
-
//
|
|
664
|
-
|
|
673
|
+
let invalidateCheckpoint = await publisher.simulateInvalidateCheckpoint(syncedTo.pendingChainValidationStatus);
|
|
674
|
+
// Determine the correct archive and L1 state overrides for the canProposeAt check.
|
|
675
|
+
// The L1 contract reads archives[proposedCheckpointNumber] and compares it with the provided archive.
|
|
676
|
+
// When invalidating or pipelining, the local archive may differ from L1's, so we adjust accordingly.
|
|
677
|
+
let archiveForCheck = syncedTo.archive;
|
|
678
|
+
const l1SimulationOverridesBuilder = new SimulationOverridesBuilder();
|
|
679
|
+
if (this.epochCache.isProposerPipeliningEnabled() && syncedTo.hasProposedCheckpoint) {
|
|
680
|
+
// Parent checkpoint hasn't landed on L1 yet. Override both the proposed checkpoint number
|
|
681
|
+
// and the archive at that checkpoint so L1 simulation sees the correct chain tip.
|
|
682
|
+
const parentCheckpointNumber = CheckpointNumber(checkpointNumber - 1);
|
|
683
|
+
l1SimulationOverridesBuilder.forPendingCheckpoint(parentCheckpointNumber).withPendingArchive(syncedTo.archive);
|
|
684
|
+
this.metrics.recordPipelineDepth(1);
|
|
685
|
+
this.log.verbose(`Building on top of proposed checkpoint (pending=${syncedTo.proposedCheckpointData?.checkpointNumber})`);
|
|
686
|
+
// Clear the invalidation - the proposed checkpoint should handle it.
|
|
687
|
+
invalidateCheckpoint = undefined;
|
|
688
|
+
} else if (invalidateCheckpoint) {
|
|
689
|
+
// After invalidation, L1 will roll back to checkpoint N-1. The archive at N-1 already
|
|
690
|
+
// exists on L1, so we just pass the matching archive (the lastArchive of the invalid checkpoint).
|
|
691
|
+
archiveForCheck = invalidateCheckpoint.lastArchive;
|
|
692
|
+
l1SimulationOverridesBuilder.forPendingCheckpoint(invalidateCheckpoint.forcePendingCheckpointNumber);
|
|
693
|
+
this.metrics.recordPipelineDepth(0);
|
|
694
|
+
} else {
|
|
695
|
+
this.metrics.recordPipelineDepth(0);
|
|
696
|
+
}
|
|
697
|
+
const simulationOverridesPlan = l1SimulationOverridesBuilder.build();
|
|
698
|
+
const canProposeCheck = await publisher.canProposeAt(archiveForCheck, proposer ?? EthAddress.ZERO, simulationOverridesPlan);
|
|
665
699
|
if (canProposeCheck === undefined) {
|
|
666
700
|
this.log.warn(`Cannot propose checkpoint ${checkpointNumber} at slot ${slot} due to failed rollup contract check`, logCtx);
|
|
667
701
|
this.emit('proposer-rollup-check-failed', {
|
|
668
702
|
reason: 'Rollup contract check failed',
|
|
669
703
|
slot
|
|
670
704
|
});
|
|
671
|
-
this.metrics.
|
|
705
|
+
this.metrics.recordCheckpointPrecheckFailed('rollup_contract_check_failed');
|
|
672
706
|
return undefined;
|
|
673
707
|
}
|
|
674
|
-
if (canProposeCheck.slot !==
|
|
675
|
-
this.log.warn(`Cannot propose block due to slot mismatch with rollup contract (this can be caused by a clock out of sync). Expected slot ${
|
|
708
|
+
if (canProposeCheck.slot !== targetSlot) {
|
|
709
|
+
this.log.warn(`Cannot propose block due to slot mismatch with rollup contract (this can be caused by a clock out of sync). Expected slot ${targetSlot} but got ${canProposeCheck.slot}.`, {
|
|
676
710
|
...logCtx,
|
|
677
711
|
rollup: canProposeCheck,
|
|
678
|
-
expectedSlot:
|
|
712
|
+
expectedSlot: targetSlot
|
|
679
713
|
});
|
|
680
714
|
this.emit('proposer-rollup-check-failed', {
|
|
681
715
|
reason: 'Slot mismatch',
|
|
682
716
|
slot
|
|
683
717
|
});
|
|
684
|
-
this.metrics.
|
|
718
|
+
this.metrics.recordCheckpointPrecheckFailed('slot_mismatch');
|
|
685
719
|
return undefined;
|
|
686
720
|
}
|
|
687
721
|
if (canProposeCheck.checkpointNumber !== checkpointNumber) {
|
|
@@ -694,19 +728,26 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
694
728
|
reason: 'Block mismatch',
|
|
695
729
|
slot
|
|
696
730
|
});
|
|
697
|
-
this.metrics.
|
|
731
|
+
this.metrics.recordCheckpointPrecheckFailed('block_number_mismatch');
|
|
698
732
|
return undefined;
|
|
699
733
|
}
|
|
700
|
-
this.lastSlotForCheckpointProposalJob =
|
|
701
|
-
this.
|
|
734
|
+
this.lastSlotForCheckpointProposalJob = targetSlot;
|
|
735
|
+
await this.p2pClient.prepareForSlot(targetSlot);
|
|
736
|
+
this.log.info(`Preparing checkpoint proposal ${checkpointNumber} for target slot ${targetSlot} during wall-clock slot ${slot}`, {
|
|
702
737
|
...logCtx,
|
|
703
|
-
proposer
|
|
738
|
+
proposer,
|
|
739
|
+
pipeliningEnabled: this.epochCache.isProposerPipeliningEnabled()
|
|
704
740
|
});
|
|
705
741
|
// Create and return the checkpoint proposal job
|
|
706
|
-
return this.createCheckpointProposalJob(
|
|
742
|
+
return this.createCheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedTo.blockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, syncedTo.proposedCheckpointData);
|
|
707
743
|
}
|
|
708
|
-
createCheckpointProposalJob(
|
|
709
|
-
return new CheckpointProposalJob(
|
|
744
|
+
createCheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedToBlockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, proposedCheckpointData) {
|
|
745
|
+
return new CheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedToBlockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, this.validatorClient, this.globalsBuilder, this.p2pClient, this.worldState, this.l1ToL2MessageSource, this.l2BlockSource, this.checkpointsBuilder, this.l2BlockSource, this.l1Constants, this.config, this.timetable, this.slasherClient, this.epochCache, this.dateProvider, this.metrics, this.checkpointProposalJobMetrics.createRecorder(), this, this.setState.bind(this), this.tracer, this.log.getBindings(), proposedCheckpointData);
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Returns the current sequencer state.
|
|
749
|
+
*/ getState() {
|
|
750
|
+
return this.state;
|
|
710
751
|
}
|
|
711
752
|
/**
|
|
712
753
|
* Internal helper for setting the sequencer state and checks if we have enough time left in the slot to transition to the new state.
|
|
@@ -748,16 +789,15 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
748
789
|
* Returns whether all dependencies have caught up.
|
|
749
790
|
* We don't check against the previous block submitted since it may have been reorg'd out.
|
|
750
791
|
*/ async checkSync(args) {
|
|
751
|
-
// Check that the archiver
|
|
752
|
-
//
|
|
753
|
-
//
|
|
754
|
-
const
|
|
755
|
-
const { slot
|
|
756
|
-
if (
|
|
792
|
+
// Check that the archiver has fully synced the L2 slot before the one we want to propose in.
|
|
793
|
+
// The archiver reports sync progress via L1 block timestamps and synced checkpoint slots.
|
|
794
|
+
// See getSyncedL2SlotNumber for how missed L1 blocks are handled.
|
|
795
|
+
const syncedL2Slot = await this.l2BlockSource.getSyncedL2SlotNumber();
|
|
796
|
+
const { slot } = args;
|
|
797
|
+
if (syncedL2Slot === undefined || syncedL2Slot + 1 < slot) {
|
|
757
798
|
this.log.debug(`Cannot propose block at next L2 slot ${slot} due to pending sync from L1`, {
|
|
758
799
|
slot,
|
|
759
|
-
|
|
760
|
-
l1Timestamp
|
|
800
|
+
syncedL2Slot
|
|
761
801
|
});
|
|
762
802
|
return undefined;
|
|
763
803
|
}
|
|
@@ -766,22 +806,30 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
766
806
|
number: syncSummary.latestBlockNumber,
|
|
767
807
|
hash: syncSummary.latestBlockHash
|
|
768
808
|
})),
|
|
769
|
-
this.l2BlockSource.getL2Tips().then((t)=>
|
|
809
|
+
this.l2BlockSource.getL2Tips().then((t)=>({
|
|
810
|
+
proposed: t.proposed,
|
|
811
|
+
checkpointed: t.checkpointed,
|
|
812
|
+
proposedCheckpoint: t.proposedCheckpoint
|
|
813
|
+
})),
|
|
770
814
|
this.p2pClient.getStatus().then((p2p)=>p2p.syncedToL2Block),
|
|
771
|
-
this.l1ToL2MessageSource.getL2Tips().then((t)=>
|
|
772
|
-
|
|
815
|
+
this.l1ToL2MessageSource.getL2Tips().then((t)=>({
|
|
816
|
+
proposed: t.proposed,
|
|
817
|
+
checkpointed: t.checkpointed
|
|
818
|
+
})),
|
|
819
|
+
this.l2BlockSource.getPendingChainValidationStatus(),
|
|
820
|
+
this.l2BlockSource.getProposedCheckpointOnly()
|
|
773
821
|
]);
|
|
774
|
-
const [worldState,
|
|
822
|
+
const [worldState, l2Tips, p2p, l1ToL2MessageSourceTips, pendingChainValidationStatus, proposedCheckpointData] = syncedBlocks;
|
|
775
823
|
// Handle zero as a special case, since the block hash won't match across services if we're changing the prefilled data for the genesis block,
|
|
776
824
|
// as the world state can compute the new genesis block hash, but other components use the hardcoded constant.
|
|
777
825
|
// TODO(palla/mbps): Fix the above. All components should be able to handle dynamic genesis block hashes.
|
|
778
|
-
const result =
|
|
826
|
+
const result = l2Tips.proposed.number === 0 && l2Tips.checkpointed.block.number === 0 && l2Tips.checkpointed.checkpoint.number === 0 && worldState.number === 0 && p2p.number === 0 && l1ToL2MessageSourceTips.proposed.number === 0 && l1ToL2MessageSourceTips.checkpointed.block.number === 0 && l1ToL2MessageSourceTips.checkpointed.checkpoint.number === 0 || worldState.hash === l2Tips.proposed.hash && p2p.hash === l2Tips.proposed.hash && l1ToL2MessageSourceTips.proposed.hash === l2Tips.proposed.hash && l1ToL2MessageSourceTips.checkpointed.block.hash === l2Tips.checkpointed.block.hash && l1ToL2MessageSourceTips.checkpointed.checkpoint.hash === l2Tips.checkpointed.checkpoint.hash;
|
|
779
827
|
if (!result) {
|
|
780
828
|
this.log.debug(`Sequencer sync check failed`, {
|
|
781
829
|
worldState,
|
|
782
|
-
l2BlockSource,
|
|
830
|
+
l2BlockSource: l2Tips.proposed,
|
|
783
831
|
p2p,
|
|
784
|
-
|
|
832
|
+
l1ToL2MessageSourceTips
|
|
785
833
|
});
|
|
786
834
|
return undefined;
|
|
787
835
|
}
|
|
@@ -791,46 +839,52 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
791
839
|
const archive = new Fr((await this.worldState.getCommitted().getTreeInfo(MerkleTreeId.ARCHIVE)).root);
|
|
792
840
|
return {
|
|
793
841
|
checkpointNumber: CheckpointNumber.ZERO,
|
|
842
|
+
checkpointedCheckpointNumber: CheckpointNumber.ZERO,
|
|
794
843
|
blockNumber: BlockNumber.ZERO,
|
|
795
844
|
archive,
|
|
796
|
-
|
|
845
|
+
hasProposedCheckpoint: false,
|
|
846
|
+
syncedL2Slot,
|
|
797
847
|
pendingChainValidationStatus
|
|
798
848
|
};
|
|
799
849
|
}
|
|
800
|
-
const
|
|
801
|
-
if (!
|
|
850
|
+
const blockData = await this.l2BlockSource.getBlockData(blockNumber);
|
|
851
|
+
if (!blockData) {
|
|
802
852
|
// this shouldn't really happen because a moment ago we checked that all components were in sync
|
|
803
|
-
this.log.error(`Failed to get L2 block ${blockNumber} from the archiver with all components in sync`);
|
|
853
|
+
this.log.error(`Failed to get L2 block data ${blockNumber} from the archiver with all components in sync`);
|
|
804
854
|
return undefined;
|
|
805
855
|
}
|
|
856
|
+
const hasProposedCheckpoint = l2Tips.proposedCheckpoint.checkpoint.number > l2Tips.checkpointed.checkpoint.number;
|
|
806
857
|
return {
|
|
807
|
-
|
|
808
|
-
blockNumber:
|
|
809
|
-
checkpointNumber:
|
|
810
|
-
|
|
811
|
-
|
|
858
|
+
blockData,
|
|
859
|
+
blockNumber: blockData.header.getBlockNumber(),
|
|
860
|
+
checkpointNumber: blockData.checkpointNumber,
|
|
861
|
+
checkpointedCheckpointNumber: l2Tips.checkpointed.checkpoint.number,
|
|
862
|
+
archive: blockData.archive.root,
|
|
863
|
+
hasProposedCheckpoint,
|
|
864
|
+
proposedCheckpointData,
|
|
865
|
+
syncedL2Slot,
|
|
812
866
|
pendingChainValidationStatus
|
|
813
867
|
};
|
|
814
868
|
}
|
|
815
869
|
/**
|
|
816
870
|
* Checks if we are the proposer for the next slot.
|
|
817
871
|
* @returns True if we can propose, and the proposer address (undefined if anyone can propose)
|
|
818
|
-
*/ async checkCanPropose(
|
|
872
|
+
*/ async checkCanPropose(targetSlot) {
|
|
819
873
|
let proposer;
|
|
820
874
|
try {
|
|
821
|
-
proposer = await this.epochCache.getProposerAttesterAddressInSlot(
|
|
875
|
+
proposer = await this.epochCache.getProposerAttesterAddressInSlot(targetSlot);
|
|
822
876
|
} catch (e) {
|
|
823
877
|
if (e instanceof NoCommitteeError) {
|
|
824
|
-
if (this.lastSlotForNoCommitteeWarning !==
|
|
825
|
-
this.lastSlotForNoCommitteeWarning =
|
|
826
|
-
this.log.warn(`Cannot propose at
|
|
878
|
+
if (this.lastSlotForNoCommitteeWarning !== targetSlot) {
|
|
879
|
+
this.lastSlotForNoCommitteeWarning = targetSlot;
|
|
880
|
+
this.log.warn(`Cannot propose at target slot ${targetSlot} since the committee does not exist on L1`);
|
|
827
881
|
}
|
|
828
882
|
return [
|
|
829
883
|
false,
|
|
830
884
|
undefined
|
|
831
885
|
];
|
|
832
886
|
}
|
|
833
|
-
this.log.error(`Error getting proposer for slot ${
|
|
887
|
+
this.log.error(`Error getting proposer for target slot ${targetSlot}`, e);
|
|
834
888
|
return [
|
|
835
889
|
false,
|
|
836
890
|
undefined
|
|
@@ -853,7 +907,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
853
907
|
const validatorAddresses = this.validatorClient.getValidatorAddresses();
|
|
854
908
|
const weAreProposer = validatorAddresses.some((addr)=>addr.equals(proposer));
|
|
855
909
|
if (!weAreProposer) {
|
|
856
|
-
this.log.debug(`Cannot propose at slot ${
|
|
910
|
+
this.log.debug(`Cannot propose at target slot ${targetSlot} since we are not a proposer`, {
|
|
911
|
+
targetSlot,
|
|
857
912
|
validatorAddresses,
|
|
858
913
|
proposer
|
|
859
914
|
});
|
|
@@ -862,6 +917,10 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
862
917
|
proposer
|
|
863
918
|
];
|
|
864
919
|
}
|
|
920
|
+
this.log.info(`We are the proposer for pipeline slot ${targetSlot}`, {
|
|
921
|
+
targetSlot,
|
|
922
|
+
proposer
|
|
923
|
+
});
|
|
865
924
|
return [
|
|
866
925
|
true,
|
|
867
926
|
proposer
|
|
@@ -871,7 +930,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
871
930
|
* Tries to vote on slashing actions and governance when the sync check fails but we're past the max time for initializing a proposal.
|
|
872
931
|
* This allows the sequencer to participate in governance/slashing votes even when it cannot build blocks.
|
|
873
932
|
*/ async tryVoteWhenSyncFails(args) {
|
|
874
|
-
const { slot } = args;
|
|
933
|
+
const { slot, targetSlot } = args;
|
|
875
934
|
// Prevent duplicate attempts in the same slot
|
|
876
935
|
if (this.lastSlotForFallbackVote === slot) {
|
|
877
936
|
this.log.trace(`Already attempted to vote in slot ${slot} (skipping)`);
|
|
@@ -894,7 +953,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
894
953
|
maxAllowedTime
|
|
895
954
|
});
|
|
896
955
|
// Check if we're a proposer or proposal is open
|
|
897
|
-
const [canPropose, proposer] = await this.checkCanPropose(
|
|
956
|
+
const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
|
|
898
957
|
if (!canPropose) {
|
|
899
958
|
this.log.trace(`Cannot vote in slot ${slot} since we are not a proposer`, {
|
|
900
959
|
slot,
|
|
@@ -910,8 +969,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
910
969
|
attestorAddress,
|
|
911
970
|
slot
|
|
912
971
|
});
|
|
913
|
-
// Enqueue governance and slashing votes
|
|
914
|
-
const voter = new CheckpointVoter(
|
|
972
|
+
// Enqueue governance and slashing votes (voter uses the target slot for L1 submission)
|
|
973
|
+
const voter = new CheckpointVoter(targetSlot, publisher, attestorAddress, this.validatorClient, this.slasherClient, this.l1Constants, this.config, this.metrics, this.log);
|
|
915
974
|
const votesPromises = voter.enqueueVotes();
|
|
916
975
|
const votes = await Promise.all(votesPromises);
|
|
917
976
|
if (votes.every((p)=>!p)) {
|
|
@@ -958,7 +1017,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
958
1017
|
* have the proposer try to invalidate, but if they fail, the sequencers in the committee are expected to try,
|
|
959
1018
|
* and if they fail, any sequencer will try as well.
|
|
960
1019
|
*/ async considerInvalidatingCheckpoint(syncedTo, currentSlot) {
|
|
961
|
-
const { pendingChainValidationStatus,
|
|
1020
|
+
const { pendingChainValidationStatus, syncedL2Slot } = syncedTo;
|
|
962
1021
|
if (pendingChainValidationStatus.valid) {
|
|
963
1022
|
return;
|
|
964
1023
|
}
|
|
@@ -969,7 +1028,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
969
1028
|
const { secondsBeforeInvalidatingBlockAsCommitteeMember, secondsBeforeInvalidatingBlockAsNonCommitteeMember } = this.config;
|
|
970
1029
|
const logData = {
|
|
971
1030
|
invalidL1Timestamp: invalidCheckpointTimestamp,
|
|
972
|
-
|
|
1031
|
+
syncedL2Slot,
|
|
973
1032
|
invalidCheckpoint: pendingChainValidationStatus.checkpoint,
|
|
974
1033
|
secondsBeforeInvalidatingBlockAsCommitteeMember,
|
|
975
1034
|
secondsBeforeInvalidatingBlockAsNonCommitteeMember,
|
|
@@ -1060,6 +1119,9 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
1060
1119
|
getValidatorAddresses() {
|
|
1061
1120
|
return this.validatorClient?.getValidatorAddresses();
|
|
1062
1121
|
}
|
|
1122
|
+
/** Updates the publisher factory's node keystore adapter after a keystore reload. */ updatePublisherNodeKeyStore(adapter) {
|
|
1123
|
+
this.publisherFactory.updateNodeKeyStore(adapter);
|
|
1124
|
+
}
|
|
1063
1125
|
getConfig() {
|
|
1064
1126
|
return this.config;
|
|
1065
1127
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
1
2
|
import type { SequencerMetrics } from './metrics.js';
|
|
2
3
|
import { SequencerState } from './utils.js';
|
|
3
|
-
export declare const MIN_EXECUTION_TIME = 2;
|
|
4
|
-
export declare const CHECKPOINT_INITIALIZATION_TIME = 1;
|
|
5
|
-
export declare const CHECKPOINT_ASSEMBLE_TIME = 1;
|
|
6
4
|
export declare class SequencerTimetable {
|
|
7
5
|
private readonly metrics?;
|
|
8
|
-
private readonly log
|
|
6
|
+
private readonly log?;
|
|
7
|
+
private readonly checkpointTiming;
|
|
9
8
|
/**
|
|
10
9
|
* How late into the slot can we be to start working. Computed as the total time needed for assembling and publishing a block,
|
|
11
10
|
* assuming an execution time equal to `minExecutionTime`, subtracted from the slot duration. This means that, if the proposer
|
|
@@ -52,6 +51,13 @@ export declare class SequencerTimetable {
|
|
|
52
51
|
readonly blockDuration: number | undefined;
|
|
53
52
|
/** Maximum number of blocks that can be built in this slot configuration */
|
|
54
53
|
readonly maxNumberOfBlocks: number;
|
|
54
|
+
/** Whether pipelining is enabled (checkpoint finalization deferred to next slot). */
|
|
55
|
+
readonly pipelining: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* How far into the target slot attestation collection can extend when pipelining.
|
|
58
|
+
* Covers validator re-execution (one block duration) plus one-way attestation return.
|
|
59
|
+
*/
|
|
60
|
+
readonly pipeliningAttestationGracePeriod: number;
|
|
55
61
|
constructor(opts: {
|
|
56
62
|
ethereumSlotDuration: number;
|
|
57
63
|
aztecSlotDuration: number;
|
|
@@ -59,7 +65,12 @@ export declare class SequencerTimetable {
|
|
|
59
65
|
p2pPropagationTime?: number;
|
|
60
66
|
blockDurationMs?: number;
|
|
61
67
|
enforce: boolean;
|
|
62
|
-
|
|
68
|
+
pipelining?: boolean;
|
|
69
|
+
}, metrics?: SequencerMetrics | undefined, log?: Logger | undefined);
|
|
70
|
+
getCheckpointAssemblyDeadline(): number;
|
|
71
|
+
getCheckpointAttestationDeadline(): number;
|
|
72
|
+
getCheckpointAttestationStartDeadline(): number;
|
|
73
|
+
getCheckpointPublishingDeadline(): number;
|
|
63
74
|
getMaxAllowedTime(state: Extract<SequencerState, SequencerState.STOPPED | SequencerState.IDLE | SequencerState.SYNCHRONIZING>): undefined;
|
|
64
75
|
getMaxAllowedTime(state: Exclude<SequencerState, SequencerState.STOPPED | SequencerState.IDLE | SequencerState.SYNCHRONIZING>): number;
|
|
65
76
|
getMaxAllowedTime(state: SequencerState): number | undefined;
|
|
@@ -87,4 +98,4 @@ export declare class SequencerTimetable {
|
|
|
87
98
|
isLastBlock: boolean;
|
|
88
99
|
};
|
|
89
100
|
}
|
|
90
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
101
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZXRhYmxlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL3RpbWV0YWJsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQVdwRCxPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNyRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRTVDLHFCQUFhLGtCQUFrQjtJQWlGM0IsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUM7SUFDekIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUM7SUFqRnZCLE9BQU8sQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLENBQW1CO0lBRXBEOzs7O09BSUc7SUFDSCxTQUFnQixrQkFBa0IsRUFBRSxNQUFNLENBQUM7SUFFM0M7Ozs7T0FJRztJQUNILFNBQWdCLG9CQUFvQixFQUFFLE1BQU0sQ0FBQztJQUU3Qzs7Ozs7T0FLRztJQUNILFNBQWdCLDBCQUEwQixFQUFFLE1BQU0sQ0FBQztJQUVuRDs7OztPQUlHO0lBQ0gsU0FBZ0IsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDO0lBRXpDOzs7T0FHRztJQUNILFNBQWdCLGdCQUFnQixFQUFFLE1BQU0sQ0FBc0I7SUFFOUQsdURBQXVEO0lBQ3ZELFNBQWdCLDRCQUE0QixFQUFFLE1BQU0sQ0FBa0M7SUFFdEYsbUdBQW1HO0lBQ25HLFNBQWdCLGtCQUFrQixFQUFFLE1BQU0sQ0FBQztJQUUzQyxtRkFBbUY7SUFDbkYsU0FBZ0Isc0JBQXNCLEVBQUUsTUFBTSxDQUE0QjtJQUUxRSx3Q0FBd0M7SUFDeEMsU0FBZ0Isb0JBQW9CLEVBQUUsTUFBTSxDQUFDO0lBRTdDLGtGQUFrRjtJQUNsRixTQUFnQixpQkFBaUIsRUFBRSxNQUFNLENBQUM7SUFFMUMsNERBQTREO0lBQzVELFNBQWdCLE9BQU8sRUFBRSxPQUFPLENBQUM7SUFFakMsb0dBQW9HO0lBQ3BHLFNBQWdCLGFBQWEsRUFBRSxNQUFNLEdBQUcsU0FBUyxDQUFDO0lBRWxELDRFQUE0RTtJQUM1RSxTQUFnQixpQkFBaUIsRUFBRSxNQUFNLENBQUM7SUFFMUMscUZBQXFGO0lBQ3JGLFNBQWdCLFVBQVUsRUFBRSxPQUFPLENBQUM7SUFFcEM7OztPQUdHO0lBQ0gsU0FBZ0IsZ0NBQWdDLEVBQUUsTUFBTSxDQUFDO0lBRXpELFlBQ0UsSUFBSSxFQUFFO1FBQ0osb0JBQW9CLEVBQUUsTUFBTSxDQUFDO1FBQzdCLGlCQUFpQixFQUFFLE1BQU0sQ0FBQztRQUMxQixnQkFBZ0IsRUFBRSxNQUFNLENBQUM7UUFDekIsa0JBQWtCLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDNUIsZUFBZSxDQUFDLEVBQUUsTUFBTSxDQUFDO1FBQ3pCLE9BQU8sRUFBRSxPQUFPLENBQUM7UUFDakIsVUFBVSxDQUFDLEVBQUUsT0FBTyxDQUFDO0tBQ3RCLEVBQ2dCLE9BQU8sQ0FBQyw4QkFBa0IsRUFDMUIsR0FBRyxDQUFDLG9CQUFRLEVBd0Q5QjtJQUVNLDZCQUE2QixJQUFJLE1BQU0sQ0FFN0M7SUFFTSxnQ0FBZ0MsSUFBSSxNQUFNLENBRWhEO0lBRU0scUNBQXFDLElBQUksTUFBTSxDQUVyRDtJQUVNLCtCQUErQixJQUFJLE1BQU0sQ0FFL0M7SUFFTSxpQkFBaUIsQ0FDdEIsS0FBSyxFQUFFLE9BQU8sQ0FBQyxjQUFjLEVBQUUsY0FBYyxDQUFDLE9BQU8sR0FBRyxjQUFjLENBQUMsSUFBSSxHQUFHLGNBQWMsQ0FBQyxhQUFhLENBQUMsR0FDMUcsU0FBUyxDQUFDO0lBQ04saUJBQWlCLENBQ3RCLEtBQUssRUFBRSxPQUFPLENBQUMsY0FBYyxFQUFFLGNBQWMsQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLElBQUksR0FBRyxjQUFjLENBQUMsYUFBYSxDQUFDLEdBQzFHLE1BQU0sQ0FBQztJQUNILGlCQUFpQixDQUFDLEtBQUssRUFBRSxjQUFjLEdBQUcsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQTRCN0QsY0FBYyxDQUFDLFFBQVEsRUFBRSxjQUFjLEVBQUUsZUFBZSxFQUFFLE1BQU0sUUFpQnRFO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSSxpQkFBaUIsQ0FDdEIsZUFBZSxFQUFFLE1BQU0sR0FFckI7UUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDO1FBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQztRQUFDLFdBQVcsRUFBRSxJQUFJLENBQUE7S0FBRSxHQUMxRDtRQUFFLFFBQVEsRUFBRSxLQUFLLENBQUM7UUFBQyxRQUFRLEVBQUUsU0FBUyxDQUFDO1FBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQTtLQUFFLEdBQzVEO1FBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQztRQUFDLFFBQVEsRUFBRSxNQUFNLENBQUM7UUFBQyxXQUFXLEVBQUUsT0FBTyxDQUFBO0tBQUUsQ0F3RGhFO0NBQ0YifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timetable.d.ts","sourceRoot":"","sources":["../../src/sequencer/timetable.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"timetable.d.ts","sourceRoot":"","sources":["../../src/sequencer/timetable.ts"],"names":[],"mappings":"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"}
|