@aztec/sequencer-client 0.0.1-commit.9372f48 → 0.0.1-commit.949a33fd8
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 +57 -37
- 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 +434 -141
- 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 +52 -10
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +718 -245
- 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 +23 -8
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +117 -21
- package/dest/sequencer/sequencer.d.ts +54 -19
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +190 -99
- 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 +10 -8
- package/package.json +27 -28
- package/src/client/sequencer-client.ts +81 -30
- package/src/config.ts +75 -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 +501 -202
- package/src/sequencer/README.md +82 -13
- package/src/sequencer/chain_state_overrides.ts +87 -0
- package/src/sequencer/checkpoint_proposal_job.ts +889 -265
- 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 +132 -25
- package/src/sequencer/sequencer.ts +266 -113
- 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 +31 -10
|
@@ -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';
|
|
@@ -448,45 +449,52 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
448
449
|
}
|
|
449
450
|
runningPromise;
|
|
450
451
|
state;
|
|
452
|
+
stateSlotNumber;
|
|
453
|
+
stateEnteredAtMs;
|
|
451
454
|
metrics;
|
|
455
|
+
checkpointProposalJobMetrics;
|
|
456
|
+
stateLog;
|
|
452
457
|
/** The last slot for which we attempted to perform our voting duties with degraded block production */ lastSlotForFallbackVote;
|
|
458
|
+
/** The last slot for which we logged "no committee" warning, to avoid spam */ lastSlotForNoCommitteeWarning;
|
|
453
459
|
/** The last slot for which we triggered a checkpoint proposal job, to prevent duplicate attempts. */ lastSlotForCheckpointProposalJob;
|
|
454
460
|
/** Last successful checkpoint proposed */ lastCheckpointProposed;
|
|
455
461
|
/** The last epoch for which we logged strategy comparison in fisherman mode. */ lastEpochForStrategyComparison;
|
|
462
|
+
/** The last checkpoint proposal job, tracked so we can await its pending L1 submission during shutdown. */ lastCheckpointProposalJob;
|
|
456
463
|
/** The maximum number of seconds that the sequencer can be into a slot to transition to a particular state. */ timetable;
|
|
457
|
-
// This shouldn't be here as this gets re-created each time we build/propose a block.
|
|
458
|
-
// But we have a number of tests that abuse/rely on this class having a permanent publisher.
|
|
459
|
-
// As long as those tests only configure a single publisher they will continue to work.
|
|
460
|
-
// This will get re-assigned every time the sequencer goes to build a new block to a publisher that is valid
|
|
461
|
-
// for the block proposer.
|
|
462
|
-
// TODO(palla/mbps): Remove this field and fix tests
|
|
463
|
-
publisher;
|
|
464
464
|
/** Config for the sequencer */ config;
|
|
465
|
+
signatureContext;
|
|
465
466
|
constructor(publisherFactory, validatorClient, globalsBuilder, p2pClient, worldState, slasherClient, l2BlockSource, l1ToL2MessageSource, checkpointsBuilder, l1Constants, dateProvider, epochCache, rollupContract, config, telemetry = getTelemetryClient(), log = createLogger('sequencer')){
|
|
466
|
-
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;
|
|
467
|
+
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.stateEnteredAtMs = performance.now(), this.config = DefaultSequencerConfig;
|
|
468
|
+
this.stateLog = log.createChild('state');
|
|
467
469
|
// Add [FISHERMAN] prefix to logger if in fisherman mode
|
|
468
470
|
if (config.fishermanMode) {
|
|
469
471
|
this.log = log.createChild('[FISHERMAN]');
|
|
470
472
|
}
|
|
473
|
+
this.signatureContext = {
|
|
474
|
+
chainId: config.l1ChainId,
|
|
475
|
+
rollupAddress: config.l1Contracts.rollupAddress
|
|
476
|
+
};
|
|
471
477
|
this.metrics = new SequencerMetrics(telemetry, this.rollupContract, 'Sequencer');
|
|
478
|
+
this.checkpointProposalJobMetrics = new CheckpointProposalJobMetrics(telemetry);
|
|
472
479
|
this.updateConfig(config);
|
|
473
480
|
}
|
|
474
481
|
/** Updates sequencer config by the defined values and updates the timetable */ updateConfig(config) {
|
|
475
482
|
const filteredConfig = pickFromSchema(config, SequencerConfigSchema);
|
|
476
|
-
this.log.info(`Updated sequencer config`, omit(filteredConfig, '
|
|
483
|
+
this.log.info(`Updated sequencer config`, omit(filteredConfig, 'txPublicSetupAllowListExtend'));
|
|
477
484
|
this.config = merge(this.config, filteredConfig);
|
|
485
|
+
const p2pPropagationTime = this.config.attestationPropagationTime;
|
|
478
486
|
this.timetable = new SequencerTimetable({
|
|
479
487
|
ethereumSlotDuration: this.l1Constants.ethereumSlotDuration,
|
|
480
488
|
aztecSlotDuration: this.aztecSlotDuration,
|
|
481
489
|
l1PublishingTime: this.l1PublishingTime,
|
|
482
|
-
p2pPropagationTime
|
|
490
|
+
p2pPropagationTime,
|
|
483
491
|
blockDurationMs: this.config.blockDurationMs,
|
|
484
|
-
enforce: this.config.enforceTimeTable
|
|
492
|
+
enforce: this.config.enforceTimeTable,
|
|
493
|
+
pipelining: this.epochCache.isProposerPipeliningEnabled()
|
|
485
494
|
}, this.metrics, this.log);
|
|
486
495
|
}
|
|
487
|
-
/** Initializes the sequencer (precomputes tables
|
|
496
|
+
/** Initializes the sequencer (precomputes tables). Takes about 3s. */ init() {
|
|
488
497
|
getKzg();
|
|
489
|
-
this.publisher = (await this.publisherFactory.create(undefined)).publisher;
|
|
490
498
|
}
|
|
491
499
|
/** Starts the sequencer and moves to IDLE state. */ start() {
|
|
492
500
|
this.runningPromise = new RunningPromise(this.safeWork.bind(this), this.log, this.config.sequencerPollingIntervalMS);
|
|
@@ -496,13 +504,17 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
496
504
|
this.runningPromise.start();
|
|
497
505
|
this.log.info('Started sequencer');
|
|
498
506
|
}
|
|
507
|
+
/** Triggers an immediate run of the sequencer, bypassing the polling interval. */ trigger() {
|
|
508
|
+
return this.runningPromise?.trigger();
|
|
509
|
+
}
|
|
499
510
|
/** Stops the sequencer from building blocks and moves to STOPPED state. */ async stop() {
|
|
500
511
|
this.log.info(`Stopping sequencer`);
|
|
501
512
|
this.setState(SequencerState.STOPPING, undefined, {
|
|
502
513
|
force: true
|
|
503
514
|
});
|
|
504
|
-
this.
|
|
515
|
+
await this.publisherFactory.stopAll();
|
|
505
516
|
await this.runningPromise?.stop();
|
|
517
|
+
await this.lastCheckpointProposalJob?.awaitPendingSubmission();
|
|
506
518
|
this.setState(SequencerState.STOPPED, undefined, {
|
|
507
519
|
force: true
|
|
508
520
|
});
|
|
@@ -516,7 +528,6 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
516
528
|
error: err
|
|
517
529
|
});
|
|
518
530
|
if (err instanceof SequencerTooSlowError) {
|
|
519
|
-
// TODO(palla/mbps): Add missing states
|
|
520
531
|
// Log as warn only if we had to abort halfway through the block proposal
|
|
521
532
|
const logLvl = [
|
|
522
533
|
SequencerState.INITIALIZING_CHECKPOINT,
|
|
@@ -547,22 +558,25 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
547
558
|
* - Submit checkpoint
|
|
548
559
|
*/ async work() {
|
|
549
560
|
this.setState(SequencerState.SYNCHRONIZING, undefined);
|
|
550
|
-
const { slot, ts,
|
|
561
|
+
const { slot, ts, nowSeconds, epoch } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
562
|
+
const { slot: targetSlot, epoch: targetEpoch } = this.epochCache.getTargetEpochAndSlotInNextL1Slot();
|
|
551
563
|
// Check if we are synced and it's our slot, grab a publisher, check previous block invalidation, etc
|
|
552
|
-
const checkpointProposalJob = await this.prepareCheckpointProposal(epoch,
|
|
564
|
+
const checkpointProposalJob = await this.prepareCheckpointProposal(slot, targetSlot, epoch, targetEpoch, ts, nowSeconds);
|
|
553
565
|
if (!checkpointProposalJob) {
|
|
554
566
|
return;
|
|
555
567
|
}
|
|
568
|
+
// Track the job so we can await its pending L1 submission during shutdown
|
|
569
|
+
this.lastCheckpointProposalJob = checkpointProposalJob;
|
|
556
570
|
// Execute the checkpoint proposal job
|
|
557
571
|
const checkpoint = await checkpointProposalJob.execute();
|
|
558
572
|
// Update last checkpoint proposed (currently unused)
|
|
559
573
|
if (checkpoint) {
|
|
560
574
|
this.lastCheckpointProposed = checkpoint;
|
|
561
575
|
}
|
|
562
|
-
// Log fee strategy comparison if on fisherman
|
|
563
|
-
if (this.config.fishermanMode && (this.lastEpochForStrategyComparison === undefined ||
|
|
564
|
-
this.logStrategyComparison(
|
|
565
|
-
this.lastEpochForStrategyComparison =
|
|
576
|
+
// Log fee strategy comparison if on fisherman (uses target epoch since we mirror the proposer's perspective)
|
|
577
|
+
if (this.config.fishermanMode && (this.lastEpochForStrategyComparison === undefined || targetEpoch > this.lastEpochForStrategyComparison)) {
|
|
578
|
+
this.logStrategyComparison(targetEpoch, checkpointProposalJob.getPublisher());
|
|
579
|
+
this.lastEpochForStrategyComparison = targetEpoch;
|
|
566
580
|
}
|
|
567
581
|
return checkpoint;
|
|
568
582
|
}
|
|
@@ -570,17 +584,17 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
570
584
|
* Prepares the checkpoint proposal by performing all necessary checks and setup.
|
|
571
585
|
* This is the initial step in the main loop.
|
|
572
586
|
* @returns CheckpointProposalJob if successful, undefined if we are not yet synced or are not the proposer.
|
|
573
|
-
*/ async prepareCheckpointProposal(epoch,
|
|
574
|
-
// Check we have not already processed this slot (cheapest check)
|
|
587
|
+
*/ async prepareCheckpointProposal(slot, targetSlot, epoch, targetEpoch, ts, nowSeconds) {
|
|
588
|
+
// Check we have not already processed this target slot (cheapest check)
|
|
575
589
|
// We only check this if enforce timetable is set, since we want to keep processing the same slot if we are not
|
|
576
590
|
// running against actual time (eg when we use sandbox-style automining)
|
|
577
|
-
if (this.lastSlotForCheckpointProposalJob && this.lastSlotForCheckpointProposalJob >=
|
|
578
|
-
this.log.trace(`
|
|
591
|
+
if (this.lastSlotForCheckpointProposalJob && this.lastSlotForCheckpointProposalJob >= targetSlot && this.config.enforceTimeTable) {
|
|
592
|
+
this.log.trace(`Target slot ${targetSlot} has already been processed`);
|
|
579
593
|
return undefined;
|
|
580
594
|
}
|
|
581
|
-
// But if we have already proposed for this slot,
|
|
582
|
-
if (this.lastCheckpointProposed && this.lastCheckpointProposed.header.slotNumber >=
|
|
583
|
-
this.log.trace(`Slot ${
|
|
595
|
+
// But if we have already proposed for this slot, then we definitely have to skip it, automining or not
|
|
596
|
+
if (this.lastCheckpointProposed && this.lastCheckpointProposed.header.slotNumber >= targetSlot) {
|
|
597
|
+
this.log.trace(`Slot ${targetSlot} has already been published as checkpoint ${this.lastCheckpointProposed.number}`);
|
|
584
598
|
return undefined;
|
|
585
599
|
}
|
|
586
600
|
// Check all components are synced to latest as seen by the archiver (queries all subsystems)
|
|
@@ -591,16 +605,18 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
591
605
|
if (!syncedTo) {
|
|
592
606
|
await this.tryVoteWhenSyncFails({
|
|
593
607
|
slot,
|
|
608
|
+
targetSlot,
|
|
594
609
|
ts
|
|
595
610
|
});
|
|
596
611
|
return undefined;
|
|
597
612
|
}
|
|
598
|
-
// If escape hatch is open for
|
|
613
|
+
// If escape hatch is open for the target epoch, do not start checkpoint proposal work and do not attempt invalidations.
|
|
599
614
|
// Still perform governance/slashing voting (as proposer) once per slot.
|
|
600
|
-
|
|
615
|
+
// When pipelining, we check the target epoch (slot+1's epoch) since that's the epoch we're building for.
|
|
616
|
+
const isEscapeHatchOpen = await this.epochCache.isEscapeHatchOpen(targetEpoch);
|
|
601
617
|
if (isEscapeHatchOpen) {
|
|
602
618
|
this.setState(SequencerState.PROPOSER_CHECK, slot);
|
|
603
|
-
const [canPropose, proposer] = await this.checkCanPropose(
|
|
619
|
+
const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
|
|
604
620
|
if (canPropose) {
|
|
605
621
|
await this.tryVoteWhenEscapeHatchOpen({
|
|
606
622
|
slot,
|
|
@@ -618,29 +634,36 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
618
634
|
// Next checkpoint follows from the last synced one
|
|
619
635
|
const checkpointNumber = CheckpointNumber(syncedTo.checkpointNumber + 1);
|
|
620
636
|
const logCtx = {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
syncedToL2Slot: getSlotAtTimestamp(syncedTo.l1Timestamp, this.l1Constants),
|
|
637
|
+
nowSeconds,
|
|
638
|
+
syncedToL2Slot: syncedTo.syncedL2Slot,
|
|
624
639
|
slot,
|
|
640
|
+
targetSlot,
|
|
625
641
|
slotTs: ts,
|
|
626
642
|
checkpointNumber,
|
|
627
643
|
isPendingChainValid: pick(syncedTo.pendingChainValidationStatus, 'valid', 'reason', 'invalidIndex')
|
|
628
644
|
};
|
|
629
|
-
// Check that we are a proposer for the
|
|
645
|
+
// Check that we are a proposer for the target slot.
|
|
630
646
|
this.setState(SequencerState.PROPOSER_CHECK, slot);
|
|
631
|
-
const [canPropose, proposer] = await this.checkCanPropose(
|
|
647
|
+
const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
|
|
632
648
|
// If we are not a proposer check if we should invalidate an invalid checkpoint, and bail
|
|
633
649
|
if (!canPropose) {
|
|
634
650
|
await this.considerInvalidatingCheckpoint(syncedTo, slot);
|
|
635
651
|
return undefined;
|
|
636
652
|
}
|
|
637
|
-
//
|
|
638
|
-
|
|
639
|
-
|
|
653
|
+
// Guard: don't exceed 1-deep pipeline. Without a proposed checkpoint, we can only build
|
|
654
|
+
// confirmed + 1. With a proposed checkpoint, we can build confirmed + 2.
|
|
655
|
+
const confirmedCkpt = syncedTo.checkpointedCheckpointNumber;
|
|
656
|
+
if (checkpointNumber > confirmedCkpt + 2) {
|
|
657
|
+
this.log.verbose(`Skipping slot ${targetSlot}: checkpoint ${checkpointNumber} exceeds max pipeline depth (confirmed=${confirmedCkpt})`);
|
|
658
|
+
return undefined;
|
|
659
|
+
}
|
|
660
|
+
// Check that the target slot is not taken by a block already (should never happen, since only us can propose for this slot)
|
|
661
|
+
if (syncedTo.blockData && syncedTo.blockData.header.getSlot() >= targetSlot) {
|
|
662
|
+
this.log.warn(`Cannot propose block at target slot ${targetSlot} since that slot was taken by block ${syncedTo.blockNumber}`, {
|
|
640
663
|
...logCtx,
|
|
641
|
-
block: syncedTo.
|
|
664
|
+
block: syncedTo.blockData.header.toInspect()
|
|
642
665
|
});
|
|
643
|
-
this.metrics.
|
|
666
|
+
this.metrics.recordCheckpointPrecheckFailed('slot_already_taken');
|
|
644
667
|
return undefined;
|
|
645
668
|
}
|
|
646
669
|
// We now need to get ourselves a publisher.
|
|
@@ -650,37 +673,58 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
650
673
|
const proposerForPublisher = this.config.fishermanMode ? undefined : proposer;
|
|
651
674
|
const { attestorAddress, publisher } = await this.publisherFactory.create(proposerForPublisher);
|
|
652
675
|
this.log.verbose(`Created publisher at address ${publisher.getSenderAddress()} for attestor ${attestorAddress}`);
|
|
653
|
-
this.publisher = publisher;
|
|
654
676
|
// In fisherman mode, set the actual proposer's address for simulations
|
|
655
677
|
if (this.config.fishermanMode && proposer) {
|
|
656
678
|
publisher.setProposerAddressForSimulation(proposer);
|
|
657
679
|
this.log.debug(`Set proposer address ${proposer} for simulation in fisherman mode`);
|
|
658
680
|
}
|
|
659
681
|
// Prepare invalidation request if the pending chain is invalid (returns undefined if no need)
|
|
660
|
-
|
|
661
|
-
//
|
|
662
|
-
//
|
|
663
|
-
|
|
682
|
+
let invalidateCheckpoint = await publisher.simulateInvalidateCheckpoint(syncedTo.pendingChainValidationStatus);
|
|
683
|
+
// Determine the correct archive and L1 state overrides for the canProposeAt check.
|
|
684
|
+
// The L1 contract reads archives[proposedCheckpointNumber] and compares it with the provided archive.
|
|
685
|
+
// When invalidating or pipelining, the local archive may differ from L1's, so we adjust accordingly.
|
|
686
|
+
let archiveForCheck = syncedTo.archive;
|
|
687
|
+
const l1SimulationOverridesBuilder = new SimulationOverridesBuilder();
|
|
688
|
+
if (this.epochCache.isProposerPipeliningEnabled() && syncedTo.hasProposedCheckpoint) {
|
|
689
|
+
// Parent checkpoint hasn't landed on L1 yet. Override both the proposed checkpoint number
|
|
690
|
+
// and the archive at that checkpoint so L1 simulation sees the correct chain tip.
|
|
691
|
+
const parentCheckpointNumber = CheckpointNumber(checkpointNumber - 1);
|
|
692
|
+
l1SimulationOverridesBuilder.forPendingCheckpoint(parentCheckpointNumber).withPendingArchive(syncedTo.archive);
|
|
693
|
+
this.metrics.recordPipelineDepth(syncedTo.checkpointNumber - syncedTo.checkpointedCheckpointNumber);
|
|
694
|
+
this.log.verbose(`Building on top of proposed checkpoint (pending=${syncedTo.proposedCheckpointData?.checkpointNumber})`);
|
|
695
|
+
// Clear the invalidation - the proposed checkpoint should handle it.
|
|
696
|
+
invalidateCheckpoint = undefined;
|
|
697
|
+
} else if (invalidateCheckpoint) {
|
|
698
|
+
// After invalidation, L1 will roll back to checkpoint N-1. The archive at N-1 already
|
|
699
|
+
// exists on L1, so we just pass the matching archive (the lastArchive of the invalid checkpoint).
|
|
700
|
+
archiveForCheck = invalidateCheckpoint.lastArchive;
|
|
701
|
+
l1SimulationOverridesBuilder.forPendingCheckpoint(invalidateCheckpoint.forcePendingCheckpointNumber);
|
|
702
|
+
this.metrics.recordPipelineDepth(0);
|
|
703
|
+
} else {
|
|
704
|
+
this.metrics.recordPipelineDepth(0);
|
|
705
|
+
}
|
|
706
|
+
const simulationOverridesPlan = l1SimulationOverridesBuilder.build();
|
|
707
|
+
const canProposeCheck = await publisher.canProposeAt(archiveForCheck, proposer ?? EthAddress.ZERO, simulationOverridesPlan);
|
|
664
708
|
if (canProposeCheck === undefined) {
|
|
665
709
|
this.log.warn(`Cannot propose checkpoint ${checkpointNumber} at slot ${slot} due to failed rollup contract check`, logCtx);
|
|
666
710
|
this.emit('proposer-rollup-check-failed', {
|
|
667
711
|
reason: 'Rollup contract check failed',
|
|
668
712
|
slot
|
|
669
713
|
});
|
|
670
|
-
this.metrics.
|
|
714
|
+
this.metrics.recordCheckpointPrecheckFailed('rollup_contract_check_failed');
|
|
671
715
|
return undefined;
|
|
672
716
|
}
|
|
673
|
-
if (canProposeCheck.slot !==
|
|
674
|
-
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 ${
|
|
717
|
+
if (canProposeCheck.slot !== targetSlot) {
|
|
718
|
+
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}.`, {
|
|
675
719
|
...logCtx,
|
|
676
720
|
rollup: canProposeCheck,
|
|
677
|
-
expectedSlot:
|
|
721
|
+
expectedSlot: targetSlot
|
|
678
722
|
});
|
|
679
723
|
this.emit('proposer-rollup-check-failed', {
|
|
680
724
|
reason: 'Slot mismatch',
|
|
681
725
|
slot
|
|
682
726
|
});
|
|
683
|
-
this.metrics.
|
|
727
|
+
this.metrics.recordCheckpointPrecheckFailed('slot_mismatch');
|
|
684
728
|
return undefined;
|
|
685
729
|
}
|
|
686
730
|
if (canProposeCheck.checkpointNumber !== checkpointNumber) {
|
|
@@ -693,19 +737,26 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
693
737
|
reason: 'Block mismatch',
|
|
694
738
|
slot
|
|
695
739
|
});
|
|
696
|
-
this.metrics.
|
|
740
|
+
this.metrics.recordCheckpointPrecheckFailed('block_number_mismatch');
|
|
697
741
|
return undefined;
|
|
698
742
|
}
|
|
699
|
-
this.lastSlotForCheckpointProposalJob =
|
|
700
|
-
this.
|
|
743
|
+
this.lastSlotForCheckpointProposalJob = targetSlot;
|
|
744
|
+
await this.p2pClient.prepareForSlot(targetSlot);
|
|
745
|
+
this.log.info(`Preparing checkpoint proposal ${checkpointNumber} for target slot ${targetSlot} during wall-clock slot ${slot}`, {
|
|
701
746
|
...logCtx,
|
|
702
|
-
proposer
|
|
747
|
+
proposer,
|
|
748
|
+
pipeliningEnabled: this.epochCache.isProposerPipeliningEnabled()
|
|
703
749
|
});
|
|
704
750
|
// Create and return the checkpoint proposal job
|
|
705
|
-
return this.createCheckpointProposalJob(
|
|
751
|
+
return this.createCheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedTo.blockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, syncedTo.proposedCheckpointData);
|
|
752
|
+
}
|
|
753
|
+
createCheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedToBlockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, proposedCheckpointData) {
|
|
754
|
+
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.signatureContext, 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);
|
|
706
755
|
}
|
|
707
|
-
|
|
708
|
-
|
|
756
|
+
/**
|
|
757
|
+
* Returns the current sequencer state.
|
|
758
|
+
*/ getState() {
|
|
759
|
+
return this.state;
|
|
709
760
|
}
|
|
710
761
|
/**
|
|
711
762
|
* Internal helper for setting the sequencer state and checks if we have enough time left in the slot to transition to the new state.
|
|
@@ -726,37 +777,52 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
726
777
|
secondsIntoSlot = this.getSecondsIntoSlot(slotNumber);
|
|
727
778
|
this.timetable.assertTimeLeft(proposedState, secondsIntoSlot);
|
|
728
779
|
}
|
|
780
|
+
const oldState = this.state;
|
|
781
|
+
const oldStateSlotNumber = this.stateSlotNumber;
|
|
782
|
+
const stateChanged = proposedState !== oldState;
|
|
783
|
+
const transitionAtMs = performance.now();
|
|
784
|
+
const stateDurationMs = transitionAtMs - this.stateEnteredAtMs;
|
|
729
785
|
const boringStates = [
|
|
730
786
|
SequencerState.IDLE,
|
|
731
787
|
SequencerState.SYNCHRONIZING
|
|
732
788
|
];
|
|
733
|
-
const logLevel = boringStates.includes(proposedState) && boringStates.includes(
|
|
734
|
-
this.
|
|
789
|
+
const logLevel = boringStates.includes(proposedState) && boringStates.includes(oldState) ? 'trace' : 'debug';
|
|
790
|
+
this.stateLog[logLevel](`Transitioning from ${oldState} to ${proposedState}`, {
|
|
791
|
+
oldState,
|
|
792
|
+
newState: proposedState,
|
|
735
793
|
slotNumber,
|
|
736
|
-
|
|
794
|
+
stateSlotNumber: oldStateSlotNumber,
|
|
795
|
+
secondsIntoSlot,
|
|
796
|
+
...stateChanged && {
|
|
797
|
+
stateDurationMs: Math.ceil(stateDurationMs)
|
|
798
|
+
}
|
|
737
799
|
});
|
|
738
800
|
this.emit('state-changed', {
|
|
739
|
-
oldState
|
|
801
|
+
oldState,
|
|
740
802
|
newState: proposedState,
|
|
741
803
|
secondsIntoSlot,
|
|
742
804
|
slot: slotNumber
|
|
743
805
|
});
|
|
806
|
+
if (stateChanged) {
|
|
807
|
+
this.metrics.recordStateDuration(stateDurationMs, oldState);
|
|
808
|
+
this.stateEnteredAtMs = transitionAtMs;
|
|
809
|
+
this.stateSlotNumber = slotNumber;
|
|
810
|
+
}
|
|
744
811
|
this.state = proposedState;
|
|
745
812
|
}
|
|
746
813
|
/**
|
|
747
814
|
* Returns whether all dependencies have caught up.
|
|
748
815
|
* We don't check against the previous block submitted since it may have been reorg'd out.
|
|
749
816
|
*/ async checkSync(args) {
|
|
750
|
-
// Check that the archiver
|
|
751
|
-
//
|
|
752
|
-
//
|
|
753
|
-
const
|
|
754
|
-
const { slot
|
|
755
|
-
if (
|
|
817
|
+
// Check that the archiver has fully synced the L2 slot before the one we want to propose in.
|
|
818
|
+
// The archiver reports sync progress via L1 block timestamps and synced checkpoint slots.
|
|
819
|
+
// See getSyncedL2SlotNumber for how missed L1 blocks are handled.
|
|
820
|
+
const syncedL2Slot = await this.l2BlockSource.getSyncedL2SlotNumber();
|
|
821
|
+
const { slot } = args;
|
|
822
|
+
if (syncedL2Slot === undefined || syncedL2Slot + 1 < slot) {
|
|
756
823
|
this.log.debug(`Cannot propose block at next L2 slot ${slot} due to pending sync from L1`, {
|
|
757
824
|
slot,
|
|
758
|
-
|
|
759
|
-
l1Timestamp
|
|
825
|
+
syncedL2Slot
|
|
760
826
|
});
|
|
761
827
|
return undefined;
|
|
762
828
|
}
|
|
@@ -765,22 +831,30 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
765
831
|
number: syncSummary.latestBlockNumber,
|
|
766
832
|
hash: syncSummary.latestBlockHash
|
|
767
833
|
})),
|
|
768
|
-
this.l2BlockSource.getL2Tips().then((t)=>
|
|
834
|
+
this.l2BlockSource.getL2Tips().then((t)=>({
|
|
835
|
+
proposed: t.proposed,
|
|
836
|
+
checkpointed: t.checkpointed,
|
|
837
|
+
proposedCheckpoint: t.proposedCheckpoint
|
|
838
|
+
})),
|
|
769
839
|
this.p2pClient.getStatus().then((p2p)=>p2p.syncedToL2Block),
|
|
770
|
-
this.l1ToL2MessageSource.getL2Tips().then((t)=>
|
|
771
|
-
|
|
840
|
+
this.l1ToL2MessageSource.getL2Tips().then((t)=>({
|
|
841
|
+
proposed: t.proposed,
|
|
842
|
+
checkpointed: t.checkpointed
|
|
843
|
+
})),
|
|
844
|
+
this.l2BlockSource.getPendingChainValidationStatus(),
|
|
845
|
+
this.l2BlockSource.getLastProposedCheckpoint()
|
|
772
846
|
]);
|
|
773
|
-
const [worldState,
|
|
847
|
+
const [worldState, l2Tips, p2p, l1ToL2MessageSourceTips, pendingChainValidationStatus, proposedCheckpointData] = syncedBlocks;
|
|
774
848
|
// 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,
|
|
775
849
|
// as the world state can compute the new genesis block hash, but other components use the hardcoded constant.
|
|
776
850
|
// TODO(palla/mbps): Fix the above. All components should be able to handle dynamic genesis block hashes.
|
|
777
|
-
const result =
|
|
851
|
+
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;
|
|
778
852
|
if (!result) {
|
|
779
853
|
this.log.debug(`Sequencer sync check failed`, {
|
|
780
854
|
worldState,
|
|
781
|
-
l2BlockSource,
|
|
855
|
+
l2BlockSource: l2Tips.proposed,
|
|
782
856
|
p2p,
|
|
783
|
-
|
|
857
|
+
l1ToL2MessageSourceTips
|
|
784
858
|
});
|
|
785
859
|
return undefined;
|
|
786
860
|
}
|
|
@@ -790,43 +864,52 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
790
864
|
const archive = new Fr((await this.worldState.getCommitted().getTreeInfo(MerkleTreeId.ARCHIVE)).root);
|
|
791
865
|
return {
|
|
792
866
|
checkpointNumber: CheckpointNumber.ZERO,
|
|
867
|
+
checkpointedCheckpointNumber: CheckpointNumber.ZERO,
|
|
793
868
|
blockNumber: BlockNumber.ZERO,
|
|
794
869
|
archive,
|
|
795
|
-
|
|
870
|
+
hasProposedCheckpoint: false,
|
|
871
|
+
syncedL2Slot,
|
|
796
872
|
pendingChainValidationStatus
|
|
797
873
|
};
|
|
798
874
|
}
|
|
799
|
-
const
|
|
800
|
-
if (!
|
|
875
|
+
const blockData = await this.l2BlockSource.getBlockData(blockNumber);
|
|
876
|
+
if (!blockData) {
|
|
801
877
|
// this shouldn't really happen because a moment ago we checked that all components were in sync
|
|
802
|
-
this.log.error(`Failed to get L2 block ${blockNumber} from the archiver with all components in sync`);
|
|
878
|
+
this.log.error(`Failed to get L2 block data ${blockNumber} from the archiver with all components in sync`);
|
|
803
879
|
return undefined;
|
|
804
880
|
}
|
|
881
|
+
const hasProposedCheckpoint = l2Tips.proposedCheckpoint.checkpoint.number > l2Tips.checkpointed.checkpoint.number;
|
|
805
882
|
return {
|
|
806
|
-
|
|
807
|
-
blockNumber:
|
|
808
|
-
checkpointNumber:
|
|
809
|
-
|
|
810
|
-
|
|
883
|
+
blockData,
|
|
884
|
+
blockNumber: blockData.header.getBlockNumber(),
|
|
885
|
+
checkpointNumber: blockData.checkpointNumber,
|
|
886
|
+
checkpointedCheckpointNumber: l2Tips.checkpointed.checkpoint.number,
|
|
887
|
+
archive: blockData.archive.root,
|
|
888
|
+
hasProposedCheckpoint,
|
|
889
|
+
proposedCheckpointData,
|
|
890
|
+
syncedL2Slot,
|
|
811
891
|
pendingChainValidationStatus
|
|
812
892
|
};
|
|
813
893
|
}
|
|
814
894
|
/**
|
|
815
895
|
* Checks if we are the proposer for the next slot.
|
|
816
896
|
* @returns True if we can propose, and the proposer address (undefined if anyone can propose)
|
|
817
|
-
*/ async checkCanPropose(
|
|
897
|
+
*/ async checkCanPropose(targetSlot) {
|
|
818
898
|
let proposer;
|
|
819
899
|
try {
|
|
820
|
-
proposer = await this.epochCache.getProposerAttesterAddressInSlot(
|
|
900
|
+
proposer = await this.epochCache.getProposerAttesterAddressInSlot(targetSlot);
|
|
821
901
|
} catch (e) {
|
|
822
902
|
if (e instanceof NoCommitteeError) {
|
|
823
|
-
this.
|
|
903
|
+
if (this.lastSlotForNoCommitteeWarning !== targetSlot) {
|
|
904
|
+
this.lastSlotForNoCommitteeWarning = targetSlot;
|
|
905
|
+
this.log.warn(`Cannot propose at target slot ${targetSlot} since the committee does not exist on L1`);
|
|
906
|
+
}
|
|
824
907
|
return [
|
|
825
908
|
false,
|
|
826
909
|
undefined
|
|
827
910
|
];
|
|
828
911
|
}
|
|
829
|
-
this.log.error(`Error getting proposer for slot ${
|
|
912
|
+
this.log.error(`Error getting proposer for target slot ${targetSlot}`, e);
|
|
830
913
|
return [
|
|
831
914
|
false,
|
|
832
915
|
undefined
|
|
@@ -849,7 +932,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
849
932
|
const validatorAddresses = this.validatorClient.getValidatorAddresses();
|
|
850
933
|
const weAreProposer = validatorAddresses.some((addr)=>addr.equals(proposer));
|
|
851
934
|
if (!weAreProposer) {
|
|
852
|
-
this.log.debug(`Cannot propose at slot ${
|
|
935
|
+
this.log.debug(`Cannot propose at target slot ${targetSlot} since we are not a proposer`, {
|
|
936
|
+
targetSlot,
|
|
853
937
|
validatorAddresses,
|
|
854
938
|
proposer
|
|
855
939
|
});
|
|
@@ -858,6 +942,10 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
858
942
|
proposer
|
|
859
943
|
];
|
|
860
944
|
}
|
|
945
|
+
this.log.info(`We are the proposer for pipeline slot ${targetSlot}`, {
|
|
946
|
+
targetSlot,
|
|
947
|
+
proposer
|
|
948
|
+
});
|
|
861
949
|
return [
|
|
862
950
|
true,
|
|
863
951
|
proposer
|
|
@@ -867,7 +955,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
867
955
|
* Tries to vote on slashing actions and governance when the sync check fails but we're past the max time for initializing a proposal.
|
|
868
956
|
* This allows the sequencer to participate in governance/slashing votes even when it cannot build blocks.
|
|
869
957
|
*/ async tryVoteWhenSyncFails(args) {
|
|
870
|
-
const { slot } = args;
|
|
958
|
+
const { slot, targetSlot } = args;
|
|
871
959
|
// Prevent duplicate attempts in the same slot
|
|
872
960
|
if (this.lastSlotForFallbackVote === slot) {
|
|
873
961
|
this.log.trace(`Already attempted to vote in slot ${slot} (skipping)`);
|
|
@@ -890,7 +978,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
890
978
|
maxAllowedTime
|
|
891
979
|
});
|
|
892
980
|
// Check if we're a proposer or proposal is open
|
|
893
|
-
const [canPropose, proposer] = await this.checkCanPropose(
|
|
981
|
+
const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
|
|
894
982
|
if (!canPropose) {
|
|
895
983
|
this.log.trace(`Cannot vote in slot ${slot} since we are not a proposer`, {
|
|
896
984
|
slot,
|
|
@@ -906,8 +994,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
906
994
|
attestorAddress,
|
|
907
995
|
slot
|
|
908
996
|
});
|
|
909
|
-
// Enqueue governance and slashing votes
|
|
910
|
-
const voter = new CheckpointVoter(
|
|
997
|
+
// Enqueue governance and slashing votes (voter uses the target slot for L1 submission)
|
|
998
|
+
const voter = new CheckpointVoter(targetSlot, publisher, attestorAddress, this.validatorClient, this.slasherClient, this.l1Constants, this.config, this.metrics, this.log);
|
|
911
999
|
const votesPromises = voter.enqueueVotes();
|
|
912
1000
|
const votes = await Promise.all(votesPromises);
|
|
913
1001
|
if (votes.every((p)=>!p)) {
|
|
@@ -954,7 +1042,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
954
1042
|
* have the proposer try to invalidate, but if they fail, the sequencers in the committee are expected to try,
|
|
955
1043
|
* and if they fail, any sequencer will try as well.
|
|
956
1044
|
*/ async considerInvalidatingCheckpoint(syncedTo, currentSlot) {
|
|
957
|
-
const { pendingChainValidationStatus,
|
|
1045
|
+
const { pendingChainValidationStatus, syncedL2Slot } = syncedTo;
|
|
958
1046
|
if (pendingChainValidationStatus.valid) {
|
|
959
1047
|
return;
|
|
960
1048
|
}
|
|
@@ -965,7 +1053,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
965
1053
|
const { secondsBeforeInvalidatingBlockAsCommitteeMember, secondsBeforeInvalidatingBlockAsNonCommitteeMember } = this.config;
|
|
966
1054
|
const logData = {
|
|
967
1055
|
invalidL1Timestamp: invalidCheckpointTimestamp,
|
|
968
|
-
|
|
1056
|
+
syncedL2Slot,
|
|
969
1057
|
invalidCheckpoint: pendingChainValidationStatus.checkpoint,
|
|
970
1058
|
secondsBeforeInvalidatingBlockAsCommitteeMember,
|
|
971
1059
|
secondsBeforeInvalidatingBlockAsNonCommitteeMember,
|
|
@@ -1056,6 +1144,9 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
1056
1144
|
getValidatorAddresses() {
|
|
1057
1145
|
return this.validatorClient?.getValidatorAddresses();
|
|
1058
1146
|
}
|
|
1147
|
+
/** Updates the publisher factory's node keystore adapter after a keystore reload. */ updatePublisherNodeKeyStore(adapter) {
|
|
1148
|
+
this.publisherFactory.updateNodeKeyStore(adapter);
|
|
1149
|
+
}
|
|
1059
1150
|
getConfig() {
|
|
1060
1151
|
return this.config;
|
|
1061
1152
|
}
|