@aztec/sequencer-client 0.0.1-commit.0c875d939 → 0.0.1-commit.0ec55a70b
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 +27 -6
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +55 -30
- 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 +68 -48
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +421 -137
- 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 +46 -10
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +700 -220
- 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 +20 -8
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +92 -21
- package/dest/sequencer/sequencer.d.ts +53 -19
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +188 -102
- package/dest/sequencer/timetable.d.ts +17 -3
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +51 -43
- 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 +7 -9
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.js +39 -30
- package/dest/test/utils.d.ts +1 -1
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +7 -6
- package/package.json +27 -28
- package/src/client/sequencer-client.ts +81 -30
- package/src/config.ts +68 -35
- 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 +474 -197
- package/src/sequencer/README.md +82 -13
- package/src/sequencer/chain_state_overrides.ts +87 -0
- package/src/sequencer/checkpoint_proposal_job.ts +866 -241
- 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 +108 -25
- package/src/sequencer/sequencer.ts +262 -116
- package/src/sequencer/timetable.ts +64 -52
- package/src/sequencer/types.ts +1 -1
- package/src/test/index.ts +2 -4
- package/src/test/mock_checkpoint_builder.ts +51 -48
- package/src/test/utils.ts +28 -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,46 +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;
|
|
453
458
|
/** The last slot for which we logged "no committee" warning, to avoid spam */ lastSlotForNoCommitteeWarning;
|
|
454
459
|
/** The last slot for which we triggered a checkpoint proposal job, to prevent duplicate attempts. */ lastSlotForCheckpointProposalJob;
|
|
455
460
|
/** Last successful checkpoint proposed */ lastCheckpointProposed;
|
|
456
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;
|
|
457
463
|
/** 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
464
|
/** Config for the sequencer */ config;
|
|
465
|
+
signatureContext;
|
|
466
466
|
constructor(publisherFactory, validatorClient, globalsBuilder, p2pClient, worldState, slasherClient, l2BlockSource, l1ToL2MessageSource, checkpointsBuilder, l1Constants, dateProvider, epochCache, rollupContract, config, telemetry = getTelemetryClient(), log = createLogger('sequencer')){
|
|
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.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');
|
|
468
469
|
// Add [FISHERMAN] prefix to logger if in fisherman mode
|
|
469
470
|
if (config.fishermanMode) {
|
|
470
471
|
this.log = log.createChild('[FISHERMAN]');
|
|
471
472
|
}
|
|
473
|
+
this.signatureContext = {
|
|
474
|
+
chainId: config.l1ChainId,
|
|
475
|
+
rollupAddress: config.l1Contracts.rollupAddress
|
|
476
|
+
};
|
|
472
477
|
this.metrics = new SequencerMetrics(telemetry, this.rollupContract, 'Sequencer');
|
|
478
|
+
this.checkpointProposalJobMetrics = new CheckpointProposalJobMetrics(telemetry);
|
|
473
479
|
this.updateConfig(config);
|
|
474
480
|
}
|
|
475
481
|
/** Updates sequencer config by the defined values and updates the timetable */ updateConfig(config) {
|
|
476
482
|
const filteredConfig = pickFromSchema(config, SequencerConfigSchema);
|
|
477
|
-
this.log.info(`Updated sequencer config`, omit(filteredConfig, '
|
|
483
|
+
this.log.info(`Updated sequencer config`, omit(filteredConfig, 'txPublicSetupAllowListExtend'));
|
|
478
484
|
this.config = merge(this.config, filteredConfig);
|
|
485
|
+
const p2pPropagationTime = this.config.attestationPropagationTime;
|
|
479
486
|
this.timetable = new SequencerTimetable({
|
|
480
487
|
ethereumSlotDuration: this.l1Constants.ethereumSlotDuration,
|
|
481
488
|
aztecSlotDuration: this.aztecSlotDuration,
|
|
482
489
|
l1PublishingTime: this.l1PublishingTime,
|
|
483
|
-
p2pPropagationTime
|
|
490
|
+
p2pPropagationTime,
|
|
484
491
|
blockDurationMs: this.config.blockDurationMs,
|
|
485
|
-
enforce: this.config.enforceTimeTable
|
|
492
|
+
enforce: this.config.enforceTimeTable,
|
|
493
|
+
pipelining: this.epochCache.isProposerPipeliningEnabled()
|
|
486
494
|
}, this.metrics, this.log);
|
|
487
495
|
}
|
|
488
|
-
/** Initializes the sequencer (precomputes tables
|
|
496
|
+
/** Initializes the sequencer (precomputes tables). Takes about 3s. */ init() {
|
|
489
497
|
getKzg();
|
|
490
|
-
this.publisher = (await this.publisherFactory.create(undefined)).publisher;
|
|
491
498
|
}
|
|
492
499
|
/** Starts the sequencer and moves to IDLE state. */ start() {
|
|
493
500
|
this.runningPromise = new RunningPromise(this.safeWork.bind(this), this.log, this.config.sequencerPollingIntervalMS);
|
|
@@ -497,13 +504,17 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
497
504
|
this.runningPromise.start();
|
|
498
505
|
this.log.info('Started sequencer');
|
|
499
506
|
}
|
|
507
|
+
/** Triggers an immediate run of the sequencer, bypassing the polling interval. */ trigger() {
|
|
508
|
+
return this.runningPromise?.trigger();
|
|
509
|
+
}
|
|
500
510
|
/** Stops the sequencer from building blocks and moves to STOPPED state. */ async stop() {
|
|
501
511
|
this.log.info(`Stopping sequencer`);
|
|
502
512
|
this.setState(SequencerState.STOPPING, undefined, {
|
|
503
513
|
force: true
|
|
504
514
|
});
|
|
505
|
-
this.
|
|
515
|
+
await this.publisherFactory.stopAll();
|
|
506
516
|
await this.runningPromise?.stop();
|
|
517
|
+
await this.lastCheckpointProposalJob?.awaitPendingSubmission();
|
|
507
518
|
this.setState(SequencerState.STOPPED, undefined, {
|
|
508
519
|
force: true
|
|
509
520
|
});
|
|
@@ -517,7 +528,6 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
517
528
|
error: err
|
|
518
529
|
});
|
|
519
530
|
if (err instanceof SequencerTooSlowError) {
|
|
520
|
-
// TODO(palla/mbps): Add missing states
|
|
521
531
|
// Log as warn only if we had to abort halfway through the block proposal
|
|
522
532
|
const logLvl = [
|
|
523
533
|
SequencerState.INITIALIZING_CHECKPOINT,
|
|
@@ -548,22 +558,25 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
548
558
|
* - Submit checkpoint
|
|
549
559
|
*/ async work() {
|
|
550
560
|
this.setState(SequencerState.SYNCHRONIZING, undefined);
|
|
551
|
-
const { slot, ts,
|
|
561
|
+
const { slot, ts, nowSeconds, epoch } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
562
|
+
const { slot: targetSlot, epoch: targetEpoch } = this.epochCache.getTargetEpochAndSlotInNextL1Slot();
|
|
552
563
|
// 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,
|
|
564
|
+
const checkpointProposalJob = await this.prepareCheckpointProposal(slot, targetSlot, epoch, targetEpoch, ts, nowSeconds);
|
|
554
565
|
if (!checkpointProposalJob) {
|
|
555
566
|
return;
|
|
556
567
|
}
|
|
568
|
+
// Track the job so we can await its pending L1 submission during shutdown
|
|
569
|
+
this.lastCheckpointProposalJob = checkpointProposalJob;
|
|
557
570
|
// Execute the checkpoint proposal job
|
|
558
571
|
const checkpoint = await checkpointProposalJob.execute();
|
|
559
572
|
// Update last checkpoint proposed (currently unused)
|
|
560
573
|
if (checkpoint) {
|
|
561
574
|
this.lastCheckpointProposed = checkpoint;
|
|
562
575
|
}
|
|
563
|
-
// Log fee strategy comparison if on fisherman
|
|
564
|
-
if (this.config.fishermanMode && (this.lastEpochForStrategyComparison === undefined ||
|
|
565
|
-
this.logStrategyComparison(
|
|
566
|
-
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;
|
|
567
580
|
}
|
|
568
581
|
return checkpoint;
|
|
569
582
|
}
|
|
@@ -571,17 +584,17 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
571
584
|
* Prepares the checkpoint proposal by performing all necessary checks and setup.
|
|
572
585
|
* This is the initial step in the main loop.
|
|
573
586
|
* @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)
|
|
587
|
+
*/ async prepareCheckpointProposal(slot, targetSlot, epoch, targetEpoch, ts, nowSeconds) {
|
|
588
|
+
// Check we have not already processed this target slot (cheapest check)
|
|
576
589
|
// We only check this if enforce timetable is set, since we want to keep processing the same slot if we are not
|
|
577
590
|
// running against actual time (eg when we use sandbox-style automining)
|
|
578
|
-
if (this.lastSlotForCheckpointProposalJob && this.lastSlotForCheckpointProposalJob >=
|
|
579
|
-
this.log.trace(`
|
|
591
|
+
if (this.lastSlotForCheckpointProposalJob && this.lastSlotForCheckpointProposalJob >= targetSlot && this.config.enforceTimeTable) {
|
|
592
|
+
this.log.trace(`Target slot ${targetSlot} has already been processed`);
|
|
580
593
|
return undefined;
|
|
581
594
|
}
|
|
582
|
-
// But if we have already proposed for this slot,
|
|
583
|
-
if (this.lastCheckpointProposed && this.lastCheckpointProposed.header.slotNumber >=
|
|
584
|
-
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}`);
|
|
585
598
|
return undefined;
|
|
586
599
|
}
|
|
587
600
|
// Check all components are synced to latest as seen by the archiver (queries all subsystems)
|
|
@@ -592,16 +605,18 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
592
605
|
if (!syncedTo) {
|
|
593
606
|
await this.tryVoteWhenSyncFails({
|
|
594
607
|
slot,
|
|
608
|
+
targetSlot,
|
|
595
609
|
ts
|
|
596
610
|
});
|
|
597
611
|
return undefined;
|
|
598
612
|
}
|
|
599
|
-
// 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.
|
|
600
614
|
// Still perform governance/slashing voting (as proposer) once per slot.
|
|
601
|
-
|
|
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);
|
|
602
617
|
if (isEscapeHatchOpen) {
|
|
603
618
|
this.setState(SequencerState.PROPOSER_CHECK, slot);
|
|
604
|
-
const [canPropose, proposer] = await this.checkCanPropose(
|
|
619
|
+
const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
|
|
605
620
|
if (canPropose) {
|
|
606
621
|
await this.tryVoteWhenEscapeHatchOpen({
|
|
607
622
|
slot,
|
|
@@ -619,29 +634,36 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
619
634
|
// Next checkpoint follows from the last synced one
|
|
620
635
|
const checkpointNumber = CheckpointNumber(syncedTo.checkpointNumber + 1);
|
|
621
636
|
const logCtx = {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
syncedToL2Slot: getSlotAtTimestamp(syncedTo.l1Timestamp, this.l1Constants),
|
|
637
|
+
nowSeconds,
|
|
638
|
+
syncedToL2Slot: syncedTo.syncedL2Slot,
|
|
625
639
|
slot,
|
|
640
|
+
targetSlot,
|
|
626
641
|
slotTs: ts,
|
|
627
642
|
checkpointNumber,
|
|
628
643
|
isPendingChainValid: pick(syncedTo.pendingChainValidationStatus, 'valid', 'reason', 'invalidIndex')
|
|
629
644
|
};
|
|
630
|
-
// Check that we are a proposer for the
|
|
645
|
+
// Check that we are a proposer for the target slot.
|
|
631
646
|
this.setState(SequencerState.PROPOSER_CHECK, slot);
|
|
632
|
-
const [canPropose, proposer] = await this.checkCanPropose(
|
|
647
|
+
const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
|
|
633
648
|
// If we are not a proposer check if we should invalidate an invalid checkpoint, and bail
|
|
634
649
|
if (!canPropose) {
|
|
635
650
|
await this.considerInvalidatingCheckpoint(syncedTo, slot);
|
|
636
651
|
return undefined;
|
|
637
652
|
}
|
|
638
|
-
//
|
|
639
|
-
|
|
640
|
-
|
|
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}`, {
|
|
641
663
|
...logCtx,
|
|
642
|
-
block: syncedTo.
|
|
664
|
+
block: syncedTo.blockData.header.toInspect()
|
|
643
665
|
});
|
|
644
|
-
this.metrics.
|
|
666
|
+
this.metrics.recordCheckpointPrecheckFailed('slot_already_taken');
|
|
645
667
|
return undefined;
|
|
646
668
|
}
|
|
647
669
|
// We now need to get ourselves a publisher.
|
|
@@ -651,37 +673,58 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
651
673
|
const proposerForPublisher = this.config.fishermanMode ? undefined : proposer;
|
|
652
674
|
const { attestorAddress, publisher } = await this.publisherFactory.create(proposerForPublisher);
|
|
653
675
|
this.log.verbose(`Created publisher at address ${publisher.getSenderAddress()} for attestor ${attestorAddress}`);
|
|
654
|
-
this.publisher = publisher;
|
|
655
676
|
// In fisherman mode, set the actual proposer's address for simulations
|
|
656
677
|
if (this.config.fishermanMode && proposer) {
|
|
657
678
|
publisher.setProposerAddressForSimulation(proposer);
|
|
658
679
|
this.log.debug(`Set proposer address ${proposer} for simulation in fisherman mode`);
|
|
659
680
|
}
|
|
660
681
|
// Prepare invalidation request if the pending chain is invalid (returns undefined if no need)
|
|
661
|
-
|
|
662
|
-
//
|
|
663
|
-
//
|
|
664
|
-
|
|
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);
|
|
665
708
|
if (canProposeCheck === undefined) {
|
|
666
709
|
this.log.warn(`Cannot propose checkpoint ${checkpointNumber} at slot ${slot} due to failed rollup contract check`, logCtx);
|
|
667
710
|
this.emit('proposer-rollup-check-failed', {
|
|
668
711
|
reason: 'Rollup contract check failed',
|
|
669
712
|
slot
|
|
670
713
|
});
|
|
671
|
-
this.metrics.
|
|
714
|
+
this.metrics.recordCheckpointPrecheckFailed('rollup_contract_check_failed');
|
|
672
715
|
return undefined;
|
|
673
716
|
}
|
|
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 ${
|
|
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}.`, {
|
|
676
719
|
...logCtx,
|
|
677
720
|
rollup: canProposeCheck,
|
|
678
|
-
expectedSlot:
|
|
721
|
+
expectedSlot: targetSlot
|
|
679
722
|
});
|
|
680
723
|
this.emit('proposer-rollup-check-failed', {
|
|
681
724
|
reason: 'Slot mismatch',
|
|
682
725
|
slot
|
|
683
726
|
});
|
|
684
|
-
this.metrics.
|
|
727
|
+
this.metrics.recordCheckpointPrecheckFailed('slot_mismatch');
|
|
685
728
|
return undefined;
|
|
686
729
|
}
|
|
687
730
|
if (canProposeCheck.checkpointNumber !== checkpointNumber) {
|
|
@@ -694,20 +737,26 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
694
737
|
reason: 'Block mismatch',
|
|
695
738
|
slot
|
|
696
739
|
});
|
|
697
|
-
this.metrics.
|
|
740
|
+
this.metrics.recordCheckpointPrecheckFailed('block_number_mismatch');
|
|
698
741
|
return undefined;
|
|
699
742
|
}
|
|
700
|
-
this.lastSlotForCheckpointProposalJob =
|
|
701
|
-
await this.p2pClient.prepareForSlot(
|
|
702
|
-
this.log.info(`Preparing checkpoint proposal ${checkpointNumber}
|
|
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}`, {
|
|
703
746
|
...logCtx,
|
|
704
|
-
proposer
|
|
747
|
+
proposer,
|
|
748
|
+
pipeliningEnabled: this.epochCache.isProposerPipeliningEnabled()
|
|
705
749
|
});
|
|
706
750
|
// Create and return the checkpoint proposal job
|
|
707
|
-
return this.createCheckpointProposalJob(
|
|
751
|
+
return this.createCheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedTo.blockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, syncedTo.proposedCheckpointData);
|
|
708
752
|
}
|
|
709
|
-
createCheckpointProposalJob(
|
|
710
|
-
return new CheckpointProposalJob(
|
|
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);
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Returns the current sequencer state.
|
|
758
|
+
*/ getState() {
|
|
759
|
+
return this.state;
|
|
711
760
|
}
|
|
712
761
|
/**
|
|
713
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.
|
|
@@ -728,37 +777,52 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
728
777
|
secondsIntoSlot = this.getSecondsIntoSlot(slotNumber);
|
|
729
778
|
this.timetable.assertTimeLeft(proposedState, secondsIntoSlot);
|
|
730
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;
|
|
731
785
|
const boringStates = [
|
|
732
786
|
SequencerState.IDLE,
|
|
733
787
|
SequencerState.SYNCHRONIZING
|
|
734
788
|
];
|
|
735
|
-
const logLevel = boringStates.includes(proposedState) && boringStates.includes(
|
|
736
|
-
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,
|
|
737
793
|
slotNumber,
|
|
738
|
-
|
|
794
|
+
stateSlotNumber: oldStateSlotNumber,
|
|
795
|
+
secondsIntoSlot,
|
|
796
|
+
...stateChanged && {
|
|
797
|
+
stateDurationMs: Math.ceil(stateDurationMs)
|
|
798
|
+
}
|
|
739
799
|
});
|
|
740
800
|
this.emit('state-changed', {
|
|
741
|
-
oldState
|
|
801
|
+
oldState,
|
|
742
802
|
newState: proposedState,
|
|
743
803
|
secondsIntoSlot,
|
|
744
804
|
slot: slotNumber
|
|
745
805
|
});
|
|
806
|
+
if (stateChanged) {
|
|
807
|
+
this.metrics.recordStateDuration(stateDurationMs, oldState);
|
|
808
|
+
this.stateEnteredAtMs = transitionAtMs;
|
|
809
|
+
this.stateSlotNumber = slotNumber;
|
|
810
|
+
}
|
|
746
811
|
this.state = proposedState;
|
|
747
812
|
}
|
|
748
813
|
/**
|
|
749
814
|
* Returns whether all dependencies have caught up.
|
|
750
815
|
* We don't check against the previous block submitted since it may have been reorg'd out.
|
|
751
816
|
*/ async checkSync(args) {
|
|
752
|
-
// Check that the archiver
|
|
753
|
-
//
|
|
754
|
-
//
|
|
755
|
-
const
|
|
756
|
-
const { slot
|
|
757
|
-
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) {
|
|
758
823
|
this.log.debug(`Cannot propose block at next L2 slot ${slot} due to pending sync from L1`, {
|
|
759
824
|
slot,
|
|
760
|
-
|
|
761
|
-
l1Timestamp
|
|
825
|
+
syncedL2Slot
|
|
762
826
|
});
|
|
763
827
|
return undefined;
|
|
764
828
|
}
|
|
@@ -767,22 +831,30 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
767
831
|
number: syncSummary.latestBlockNumber,
|
|
768
832
|
hash: syncSummary.latestBlockHash
|
|
769
833
|
})),
|
|
770
|
-
this.l2BlockSource.getL2Tips().then((t)=>
|
|
834
|
+
this.l2BlockSource.getL2Tips().then((t)=>({
|
|
835
|
+
proposed: t.proposed,
|
|
836
|
+
checkpointed: t.checkpointed,
|
|
837
|
+
proposedCheckpoint: t.proposedCheckpoint
|
|
838
|
+
})),
|
|
771
839
|
this.p2pClient.getStatus().then((p2p)=>p2p.syncedToL2Block),
|
|
772
|
-
this.l1ToL2MessageSource.getL2Tips().then((t)=>
|
|
773
|
-
|
|
840
|
+
this.l1ToL2MessageSource.getL2Tips().then((t)=>({
|
|
841
|
+
proposed: t.proposed,
|
|
842
|
+
checkpointed: t.checkpointed
|
|
843
|
+
})),
|
|
844
|
+
this.l2BlockSource.getPendingChainValidationStatus(),
|
|
845
|
+
this.l2BlockSource.getLastProposedCheckpoint()
|
|
774
846
|
]);
|
|
775
|
-
const [worldState,
|
|
847
|
+
const [worldState, l2Tips, p2p, l1ToL2MessageSourceTips, pendingChainValidationStatus, proposedCheckpointData] = syncedBlocks;
|
|
776
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,
|
|
777
849
|
// as the world state can compute the new genesis block hash, but other components use the hardcoded constant.
|
|
778
850
|
// TODO(palla/mbps): Fix the above. All components should be able to handle dynamic genesis block hashes.
|
|
779
|
-
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;
|
|
780
852
|
if (!result) {
|
|
781
853
|
this.log.debug(`Sequencer sync check failed`, {
|
|
782
854
|
worldState,
|
|
783
|
-
l2BlockSource,
|
|
855
|
+
l2BlockSource: l2Tips.proposed,
|
|
784
856
|
p2p,
|
|
785
|
-
|
|
857
|
+
l1ToL2MessageSourceTips
|
|
786
858
|
});
|
|
787
859
|
return undefined;
|
|
788
860
|
}
|
|
@@ -792,46 +864,52 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
792
864
|
const archive = new Fr((await this.worldState.getCommitted().getTreeInfo(MerkleTreeId.ARCHIVE)).root);
|
|
793
865
|
return {
|
|
794
866
|
checkpointNumber: CheckpointNumber.ZERO,
|
|
867
|
+
checkpointedCheckpointNumber: CheckpointNumber.ZERO,
|
|
795
868
|
blockNumber: BlockNumber.ZERO,
|
|
796
869
|
archive,
|
|
797
|
-
|
|
870
|
+
hasProposedCheckpoint: false,
|
|
871
|
+
syncedL2Slot,
|
|
798
872
|
pendingChainValidationStatus
|
|
799
873
|
};
|
|
800
874
|
}
|
|
801
|
-
const
|
|
802
|
-
if (!
|
|
875
|
+
const blockData = await this.l2BlockSource.getBlockData(blockNumber);
|
|
876
|
+
if (!blockData) {
|
|
803
877
|
// this shouldn't really happen because a moment ago we checked that all components were in sync
|
|
804
|
-
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`);
|
|
805
879
|
return undefined;
|
|
806
880
|
}
|
|
881
|
+
const hasProposedCheckpoint = l2Tips.proposedCheckpoint.checkpoint.number > l2Tips.checkpointed.checkpoint.number;
|
|
807
882
|
return {
|
|
808
|
-
|
|
809
|
-
blockNumber:
|
|
810
|
-
checkpointNumber:
|
|
811
|
-
|
|
812
|
-
|
|
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,
|
|
813
891
|
pendingChainValidationStatus
|
|
814
892
|
};
|
|
815
893
|
}
|
|
816
894
|
/**
|
|
817
895
|
* Checks if we are the proposer for the next slot.
|
|
818
896
|
* @returns True if we can propose, and the proposer address (undefined if anyone can propose)
|
|
819
|
-
*/ async checkCanPropose(
|
|
897
|
+
*/ async checkCanPropose(targetSlot) {
|
|
820
898
|
let proposer;
|
|
821
899
|
try {
|
|
822
|
-
proposer = await this.epochCache.getProposerAttesterAddressInSlot(
|
|
900
|
+
proposer = await this.epochCache.getProposerAttesterAddressInSlot(targetSlot);
|
|
823
901
|
} catch (e) {
|
|
824
902
|
if (e instanceof NoCommitteeError) {
|
|
825
|
-
if (this.lastSlotForNoCommitteeWarning !==
|
|
826
|
-
this.lastSlotForNoCommitteeWarning =
|
|
827
|
-
this.log.warn(`Cannot propose at
|
|
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`);
|
|
828
906
|
}
|
|
829
907
|
return [
|
|
830
908
|
false,
|
|
831
909
|
undefined
|
|
832
910
|
];
|
|
833
911
|
}
|
|
834
|
-
this.log.error(`Error getting proposer for slot ${
|
|
912
|
+
this.log.error(`Error getting proposer for target slot ${targetSlot}`, e);
|
|
835
913
|
return [
|
|
836
914
|
false,
|
|
837
915
|
undefined
|
|
@@ -854,7 +932,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
854
932
|
const validatorAddresses = this.validatorClient.getValidatorAddresses();
|
|
855
933
|
const weAreProposer = validatorAddresses.some((addr)=>addr.equals(proposer));
|
|
856
934
|
if (!weAreProposer) {
|
|
857
|
-
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,
|
|
858
937
|
validatorAddresses,
|
|
859
938
|
proposer
|
|
860
939
|
});
|
|
@@ -863,6 +942,10 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
863
942
|
proposer
|
|
864
943
|
];
|
|
865
944
|
}
|
|
945
|
+
this.log.info(`We are the proposer for pipeline slot ${targetSlot}`, {
|
|
946
|
+
targetSlot,
|
|
947
|
+
proposer
|
|
948
|
+
});
|
|
866
949
|
return [
|
|
867
950
|
true,
|
|
868
951
|
proposer
|
|
@@ -872,7 +955,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
872
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.
|
|
873
956
|
* This allows the sequencer to participate in governance/slashing votes even when it cannot build blocks.
|
|
874
957
|
*/ async tryVoteWhenSyncFails(args) {
|
|
875
|
-
const { slot } = args;
|
|
958
|
+
const { slot, targetSlot } = args;
|
|
876
959
|
// Prevent duplicate attempts in the same slot
|
|
877
960
|
if (this.lastSlotForFallbackVote === slot) {
|
|
878
961
|
this.log.trace(`Already attempted to vote in slot ${slot} (skipping)`);
|
|
@@ -895,7 +978,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
895
978
|
maxAllowedTime
|
|
896
979
|
});
|
|
897
980
|
// Check if we're a proposer or proposal is open
|
|
898
|
-
const [canPropose, proposer] = await this.checkCanPropose(
|
|
981
|
+
const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
|
|
899
982
|
if (!canPropose) {
|
|
900
983
|
this.log.trace(`Cannot vote in slot ${slot} since we are not a proposer`, {
|
|
901
984
|
slot,
|
|
@@ -911,8 +994,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
911
994
|
attestorAddress,
|
|
912
995
|
slot
|
|
913
996
|
});
|
|
914
|
-
// Enqueue governance and slashing votes
|
|
915
|
-
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);
|
|
916
999
|
const votesPromises = voter.enqueueVotes();
|
|
917
1000
|
const votes = await Promise.all(votesPromises);
|
|
918
1001
|
if (votes.every((p)=>!p)) {
|
|
@@ -959,7 +1042,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
959
1042
|
* have the proposer try to invalidate, but if they fail, the sequencers in the committee are expected to try,
|
|
960
1043
|
* and if they fail, any sequencer will try as well.
|
|
961
1044
|
*/ async considerInvalidatingCheckpoint(syncedTo, currentSlot) {
|
|
962
|
-
const { pendingChainValidationStatus,
|
|
1045
|
+
const { pendingChainValidationStatus, syncedL2Slot } = syncedTo;
|
|
963
1046
|
if (pendingChainValidationStatus.valid) {
|
|
964
1047
|
return;
|
|
965
1048
|
}
|
|
@@ -970,7 +1053,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
970
1053
|
const { secondsBeforeInvalidatingBlockAsCommitteeMember, secondsBeforeInvalidatingBlockAsNonCommitteeMember } = this.config;
|
|
971
1054
|
const logData = {
|
|
972
1055
|
invalidL1Timestamp: invalidCheckpointTimestamp,
|
|
973
|
-
|
|
1056
|
+
syncedL2Slot,
|
|
974
1057
|
invalidCheckpoint: pendingChainValidationStatus.checkpoint,
|
|
975
1058
|
secondsBeforeInvalidatingBlockAsCommitteeMember,
|
|
976
1059
|
secondsBeforeInvalidatingBlockAsNonCommitteeMember,
|
|
@@ -1061,6 +1144,9 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
1061
1144
|
getValidatorAddresses() {
|
|
1062
1145
|
return this.validatorClient?.getValidatorAddresses();
|
|
1063
1146
|
}
|
|
1147
|
+
/** Updates the publisher factory's node keystore adapter after a keystore reload. */ updatePublisherNodeKeyStore(adapter) {
|
|
1148
|
+
this.publisherFactory.updateNodeKeyStore(adapter);
|
|
1149
|
+
}
|
|
1064
1150
|
getConfig() {
|
|
1065
1151
|
return this.config;
|
|
1066
1152
|
}
|