@aztec/sequencer-client 0.0.1-commit.e558bd1c → 0.0.1-commit.e588bc7e5
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 +15 -7
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +60 -26
- package/dest/config.d.ts +26 -7
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +47 -28
- package/dest/global_variable_builder/global_builder.d.ts +15 -11
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +29 -25
- package/dest/global_variable_builder/index.d.ts +2 -2
- package/dest/global_variable_builder/index.d.ts.map +1 -1
- 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 +59 -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 -3
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +27 -2
- package/dest/publisher/sequencer-publisher.d.ts +76 -30
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +396 -71
- package/dest/sequencer/checkpoint_proposal_job.d.ts +33 -8
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +345 -172
- 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 +2 -1
- package/dest/sequencer/events.d.ts.map +1 -1
- package/dest/sequencer/metrics.d.ts +21 -5
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +97 -15
- package/dest/sequencer/sequencer.d.ts +40 -17
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +145 -91
- package/dest/sequencer/timetable.d.ts +4 -6
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +7 -11
- 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 +76 -23
- package/src/config.ts +65 -38
- package/src/global_variable_builder/global_builder.ts +38 -27
- package/src/global_variable_builder/index.ts +1 -1
- package/src/publisher/config.ts +153 -43
- 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 +55 -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 -6
- package/src/publisher/sequencer-publisher.ts +442 -95
- package/src/sequencer/checkpoint_proposal_job.ts +454 -178
- package/src/sequencer/checkpoint_voter.ts +1 -12
- package/src/sequencer/events.ts +1 -1
- package/src/sequencer/metrics.ts +106 -18
- package/src/sequencer/sequencer.ts +208 -107
- package/src/sequencer/timetable.ts +13 -12
- 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
|
@@ -380,7 +380,7 @@ 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';
|
|
@@ -454,14 +454,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
454
454
|
/** The last slot for which we triggered a checkpoint proposal job, to prevent duplicate attempts. */ lastSlotForCheckpointProposalJob;
|
|
455
455
|
/** Last successful checkpoint proposed */ lastCheckpointProposed;
|
|
456
456
|
/** The last epoch for which we logged strategy comparison in fisherman mode. */ lastEpochForStrategyComparison;
|
|
457
|
+
/** The last checkpoint proposal job, tracked so we can await its pending L1 submission during shutdown. */ lastCheckpointProposalJob;
|
|
457
458
|
/** 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
459
|
/** Config for the sequencer */ config;
|
|
466
460
|
constructor(publisherFactory, validatorClient, globalsBuilder, p2pClient, worldState, slasherClient, l2BlockSource, l1ToL2MessageSource, checkpointsBuilder, l1Constants, dateProvider, epochCache, rollupContract, config, telemetry = getTelemetryClient(), log = createLogger('sequencer')){
|
|
467
461
|
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;
|
|
@@ -474,7 +468,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
474
468
|
}
|
|
475
469
|
/** Updates sequencer config by the defined values and updates the timetable */ updateConfig(config) {
|
|
476
470
|
const filteredConfig = pickFromSchema(config, SequencerConfigSchema);
|
|
477
|
-
this.log.info(`Updated sequencer config`, omit(filteredConfig, '
|
|
471
|
+
this.log.info(`Updated sequencer config`, omit(filteredConfig, 'txPublicSetupAllowListExtend'));
|
|
478
472
|
this.config = merge(this.config, filteredConfig);
|
|
479
473
|
this.timetable = new SequencerTimetable({
|
|
480
474
|
ethereumSlotDuration: this.l1Constants.ethereumSlotDuration,
|
|
@@ -485,9 +479,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
485
479
|
enforce: this.config.enforceTimeTable
|
|
486
480
|
}, this.metrics, this.log);
|
|
487
481
|
}
|
|
488
|
-
/** Initializes the sequencer (precomputes tables
|
|
482
|
+
/** Initializes the sequencer (precomputes tables). Takes about 3s. */ init() {
|
|
489
483
|
getKzg();
|
|
490
|
-
this.publisher = (await this.publisherFactory.create(undefined)).publisher;
|
|
491
484
|
}
|
|
492
485
|
/** Starts the sequencer and moves to IDLE state. */ start() {
|
|
493
486
|
this.runningPromise = new RunningPromise(this.safeWork.bind(this), this.log, this.config.sequencerPollingIntervalMS);
|
|
@@ -502,8 +495,9 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
502
495
|
this.setState(SequencerState.STOPPING, undefined, {
|
|
503
496
|
force: true
|
|
504
497
|
});
|
|
505
|
-
this.
|
|
498
|
+
await this.publisherFactory.stopAll();
|
|
506
499
|
await this.runningPromise?.stop();
|
|
500
|
+
await this.lastCheckpointProposalJob?.awaitPendingSubmission();
|
|
507
501
|
this.setState(SequencerState.STOPPED, undefined, {
|
|
508
502
|
force: true
|
|
509
503
|
});
|
|
@@ -517,7 +511,6 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
517
511
|
error: err
|
|
518
512
|
});
|
|
519
513
|
if (err instanceof SequencerTooSlowError) {
|
|
520
|
-
// TODO(palla/mbps): Add missing states
|
|
521
514
|
// Log as warn only if we had to abort halfway through the block proposal
|
|
522
515
|
const logLvl = [
|
|
523
516
|
SequencerState.INITIALIZING_CHECKPOINT,
|
|
@@ -548,22 +541,25 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
548
541
|
* - Submit checkpoint
|
|
549
542
|
*/ async work() {
|
|
550
543
|
this.setState(SequencerState.SYNCHRONIZING, undefined);
|
|
551
|
-
const { slot, ts,
|
|
544
|
+
const { slot, ts, nowSeconds, epoch } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
545
|
+
const { slot: targetSlot, epoch: targetEpoch } = this.epochCache.getTargetEpochAndSlotInNextL1Slot();
|
|
552
546
|
// 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,
|
|
547
|
+
const checkpointProposalJob = await this.prepareCheckpointProposal(slot, targetSlot, epoch, targetEpoch, ts, nowSeconds);
|
|
554
548
|
if (!checkpointProposalJob) {
|
|
555
549
|
return;
|
|
556
550
|
}
|
|
551
|
+
// Track the job so we can await its pending L1 submission during shutdown
|
|
552
|
+
this.lastCheckpointProposalJob = checkpointProposalJob;
|
|
557
553
|
// Execute the checkpoint proposal job
|
|
558
554
|
const checkpoint = await checkpointProposalJob.execute();
|
|
559
555
|
// Update last checkpoint proposed (currently unused)
|
|
560
556
|
if (checkpoint) {
|
|
561
557
|
this.lastCheckpointProposed = checkpoint;
|
|
562
558
|
}
|
|
563
|
-
// Log fee strategy comparison if on fisherman
|
|
564
|
-
if (this.config.fishermanMode && (this.lastEpochForStrategyComparison === undefined ||
|
|
565
|
-
this.logStrategyComparison(
|
|
566
|
-
this.lastEpochForStrategyComparison =
|
|
559
|
+
// Log fee strategy comparison if on fisherman (uses target epoch since we mirror the proposer's perspective)
|
|
560
|
+
if (this.config.fishermanMode && (this.lastEpochForStrategyComparison === undefined || targetEpoch > this.lastEpochForStrategyComparison)) {
|
|
561
|
+
this.logStrategyComparison(targetEpoch, checkpointProposalJob.getPublisher());
|
|
562
|
+
this.lastEpochForStrategyComparison = targetEpoch;
|
|
567
563
|
}
|
|
568
564
|
return checkpoint;
|
|
569
565
|
}
|
|
@@ -571,17 +567,17 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
571
567
|
* Prepares the checkpoint proposal by performing all necessary checks and setup.
|
|
572
568
|
* This is the initial step in the main loop.
|
|
573
569
|
* @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)
|
|
570
|
+
*/ async prepareCheckpointProposal(slot, targetSlot, epoch, targetEpoch, ts, nowSeconds) {
|
|
571
|
+
// Check we have not already processed this target slot (cheapest check)
|
|
576
572
|
// We only check this if enforce timetable is set, since we want to keep processing the same slot if we are not
|
|
577
573
|
// running against actual time (eg when we use sandbox-style automining)
|
|
578
|
-
if (this.lastSlotForCheckpointProposalJob && this.lastSlotForCheckpointProposalJob >=
|
|
579
|
-
this.log.trace(`
|
|
574
|
+
if (this.lastSlotForCheckpointProposalJob && this.lastSlotForCheckpointProposalJob >= targetSlot && this.config.enforceTimeTable) {
|
|
575
|
+
this.log.trace(`Target slot ${targetSlot} has already been processed`);
|
|
580
576
|
return undefined;
|
|
581
577
|
}
|
|
582
|
-
// But if we have already proposed for this slot,
|
|
583
|
-
if (this.lastCheckpointProposed && this.lastCheckpointProposed.header.slotNumber >=
|
|
584
|
-
this.log.trace(`Slot ${
|
|
578
|
+
// But if we have already proposed for this slot, then we definitely have to skip it, automining or not
|
|
579
|
+
if (this.lastCheckpointProposed && this.lastCheckpointProposed.header.slotNumber >= targetSlot) {
|
|
580
|
+
this.log.trace(`Slot ${targetSlot} has already been published as checkpoint ${this.lastCheckpointProposed.number}`);
|
|
585
581
|
return undefined;
|
|
586
582
|
}
|
|
587
583
|
// Check all components are synced to latest as seen by the archiver (queries all subsystems)
|
|
@@ -592,16 +588,18 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
592
588
|
if (!syncedTo) {
|
|
593
589
|
await this.tryVoteWhenSyncFails({
|
|
594
590
|
slot,
|
|
591
|
+
targetSlot,
|
|
595
592
|
ts
|
|
596
593
|
});
|
|
597
594
|
return undefined;
|
|
598
595
|
}
|
|
599
|
-
// If escape hatch is open for
|
|
596
|
+
// If escape hatch is open for the target epoch, do not start checkpoint proposal work and do not attempt invalidations.
|
|
600
597
|
// Still perform governance/slashing voting (as proposer) once per slot.
|
|
601
|
-
|
|
598
|
+
// When pipelining, we check the target epoch (slot+1's epoch) since that's the epoch we're building for.
|
|
599
|
+
const isEscapeHatchOpen = await this.epochCache.isEscapeHatchOpen(targetEpoch);
|
|
602
600
|
if (isEscapeHatchOpen) {
|
|
603
601
|
this.setState(SequencerState.PROPOSER_CHECK, slot);
|
|
604
|
-
const [canPropose, proposer] = await this.checkCanPropose(
|
|
602
|
+
const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
|
|
605
603
|
if (canPropose) {
|
|
606
604
|
await this.tryVoteWhenEscapeHatchOpen({
|
|
607
605
|
slot,
|
|
@@ -618,30 +616,37 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
618
616
|
}
|
|
619
617
|
// Next checkpoint follows from the last synced one
|
|
620
618
|
const checkpointNumber = CheckpointNumber(syncedTo.checkpointNumber + 1);
|
|
619
|
+
// Guard: don't exceed 1-deep pipeline. Without a proposed checkpoint, we can only build
|
|
620
|
+
// confirmed + 1. With a proposed checkpoint, we can build confirmed + 2.
|
|
621
|
+
const confirmedCkpt = syncedTo.checkpointedCheckpointNumber;
|
|
622
|
+
if (checkpointNumber > confirmedCkpt + 2) {
|
|
623
|
+
this.log.warn(`Skipping slot ${targetSlot}: checkpoint ${checkpointNumber} exceeds max pipeline depth (confirmed=${confirmedCkpt})`);
|
|
624
|
+
return undefined;
|
|
625
|
+
}
|
|
621
626
|
const logCtx = {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
syncedToL2Slot: getSlotAtTimestamp(syncedTo.l1Timestamp, this.l1Constants),
|
|
627
|
+
nowSeconds,
|
|
628
|
+
syncedToL2Slot: syncedTo.syncedL2Slot,
|
|
625
629
|
slot,
|
|
630
|
+
targetSlot,
|
|
626
631
|
slotTs: ts,
|
|
627
632
|
checkpointNumber,
|
|
628
633
|
isPendingChainValid: pick(syncedTo.pendingChainValidationStatus, 'valid', 'reason', 'invalidIndex')
|
|
629
634
|
};
|
|
630
|
-
// Check that we are a proposer for the
|
|
635
|
+
// Check that we are a proposer for the target slot.
|
|
631
636
|
this.setState(SequencerState.PROPOSER_CHECK, slot);
|
|
632
|
-
const [canPropose, proposer] = await this.checkCanPropose(
|
|
637
|
+
const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
|
|
633
638
|
// If we are not a proposer check if we should invalidate an invalid checkpoint, and bail
|
|
634
639
|
if (!canPropose) {
|
|
635
640
|
await this.considerInvalidatingCheckpoint(syncedTo, slot);
|
|
636
641
|
return undefined;
|
|
637
642
|
}
|
|
638
|
-
// Check that the slot is not taken by a block already (should never happen, since only us can propose for this slot)
|
|
639
|
-
if (syncedTo.
|
|
640
|
-
this.log.warn(`Cannot propose block at
|
|
643
|
+
// Check that the target slot is not taken by a block already (should never happen, since only us can propose for this slot)
|
|
644
|
+
if (syncedTo.blockData && syncedTo.blockData.header.getSlot() >= targetSlot) {
|
|
645
|
+
this.log.warn(`Cannot propose block at target slot ${targetSlot} since that slot was taken by block ${syncedTo.blockNumber}`, {
|
|
641
646
|
...logCtx,
|
|
642
|
-
block: syncedTo.
|
|
647
|
+
block: syncedTo.blockData.header.toInspect()
|
|
643
648
|
});
|
|
644
|
-
this.metrics.
|
|
649
|
+
this.metrics.recordCheckpointPrecheckFailed('slot_already_taken');
|
|
645
650
|
return undefined;
|
|
646
651
|
}
|
|
647
652
|
// We now need to get ourselves a publisher.
|
|
@@ -651,37 +656,61 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
651
656
|
const proposerForPublisher = this.config.fishermanMode ? undefined : proposer;
|
|
652
657
|
const { attestorAddress, publisher } = await this.publisherFactory.create(proposerForPublisher);
|
|
653
658
|
this.log.verbose(`Created publisher at address ${publisher.getSenderAddress()} for attestor ${attestorAddress}`);
|
|
654
|
-
this.publisher = publisher;
|
|
655
659
|
// In fisherman mode, set the actual proposer's address for simulations
|
|
656
660
|
if (this.config.fishermanMode && proposer) {
|
|
657
661
|
publisher.setProposerAddressForSimulation(proposer);
|
|
658
662
|
this.log.debug(`Set proposer address ${proposer} for simulation in fisherman mode`);
|
|
659
663
|
}
|
|
660
664
|
// Prepare invalidation request if the pending chain is invalid (returns undefined if no need)
|
|
661
|
-
|
|
662
|
-
//
|
|
663
|
-
//
|
|
664
|
-
|
|
665
|
+
let invalidateCheckpoint = await publisher.simulateInvalidateCheckpoint(syncedTo.pendingChainValidationStatus);
|
|
666
|
+
// Determine the correct archive and L1 state overrides for the canProposeAt check.
|
|
667
|
+
// The L1 contract reads archives[proposedCheckpointNumber] and compares it with the provided archive.
|
|
668
|
+
// When invalidating or pipelining, the local archive may differ from L1's, so we adjust accordingly.
|
|
669
|
+
let archiveForCheck = syncedTo.archive;
|
|
670
|
+
const l1Overrides = {};
|
|
671
|
+
if (this.epochCache.isProposerPipeliningEnabled() && syncedTo.hasProposedCheckpoint) {
|
|
672
|
+
// Parent checkpoint hasn't landed on L1 yet. Override both the proposed checkpoint number
|
|
673
|
+
// and the archive at that checkpoint so L1 simulation sees the correct chain tip.
|
|
674
|
+
const parentCheckpointNumber = CheckpointNumber(checkpointNumber - 1);
|
|
675
|
+
l1Overrides.forcePendingCheckpointNumber = parentCheckpointNumber;
|
|
676
|
+
l1Overrides.forceArchive = {
|
|
677
|
+
checkpointNumber: parentCheckpointNumber,
|
|
678
|
+
archive: syncedTo.archive
|
|
679
|
+
};
|
|
680
|
+
this.metrics.recordPipelineDepth(1);
|
|
681
|
+
this.log.verbose(`Building on top of proposed checkpoint (pending=${syncedTo.proposedCheckpointData?.checkpointNumber})`);
|
|
682
|
+
// Clear the invalidation - the proposed checkpoint should handle it.
|
|
683
|
+
invalidateCheckpoint = undefined;
|
|
684
|
+
} else if (invalidateCheckpoint) {
|
|
685
|
+
// After invalidation, L1 will roll back to checkpoint N-1. The archive at N-1 already
|
|
686
|
+
// exists on L1, so we just pass the matching archive (the lastArchive of the invalid checkpoint).
|
|
687
|
+
archiveForCheck = invalidateCheckpoint.lastArchive;
|
|
688
|
+
l1Overrides.forcePendingCheckpointNumber = invalidateCheckpoint.forcePendingCheckpointNumber;
|
|
689
|
+
this.metrics.recordPipelineDepth(0);
|
|
690
|
+
} else {
|
|
691
|
+
this.metrics.recordPipelineDepth(0);
|
|
692
|
+
}
|
|
693
|
+
const canProposeCheck = await publisher.canProposeAt(archiveForCheck, proposer ?? EthAddress.ZERO, l1Overrides);
|
|
665
694
|
if (canProposeCheck === undefined) {
|
|
666
695
|
this.log.warn(`Cannot propose checkpoint ${checkpointNumber} at slot ${slot} due to failed rollup contract check`, logCtx);
|
|
667
696
|
this.emit('proposer-rollup-check-failed', {
|
|
668
697
|
reason: 'Rollup contract check failed',
|
|
669
698
|
slot
|
|
670
699
|
});
|
|
671
|
-
this.metrics.
|
|
700
|
+
this.metrics.recordCheckpointPrecheckFailed('rollup_contract_check_failed');
|
|
672
701
|
return undefined;
|
|
673
702
|
}
|
|
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 ${
|
|
703
|
+
if (canProposeCheck.slot !== targetSlot) {
|
|
704
|
+
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
705
|
...logCtx,
|
|
677
706
|
rollup: canProposeCheck,
|
|
678
|
-
expectedSlot:
|
|
707
|
+
expectedSlot: targetSlot
|
|
679
708
|
});
|
|
680
709
|
this.emit('proposer-rollup-check-failed', {
|
|
681
710
|
reason: 'Slot mismatch',
|
|
682
711
|
slot
|
|
683
712
|
});
|
|
684
|
-
this.metrics.
|
|
713
|
+
this.metrics.recordCheckpointPrecheckFailed('slot_mismatch');
|
|
685
714
|
return undefined;
|
|
686
715
|
}
|
|
687
716
|
if (canProposeCheck.checkpointNumber !== checkpointNumber) {
|
|
@@ -694,19 +723,26 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
694
723
|
reason: 'Block mismatch',
|
|
695
724
|
slot
|
|
696
725
|
});
|
|
697
|
-
this.metrics.
|
|
726
|
+
this.metrics.recordCheckpointPrecheckFailed('block_number_mismatch');
|
|
698
727
|
return undefined;
|
|
699
728
|
}
|
|
700
|
-
this.lastSlotForCheckpointProposalJob =
|
|
701
|
-
this.
|
|
729
|
+
this.lastSlotForCheckpointProposalJob = targetSlot;
|
|
730
|
+
await this.p2pClient.prepareForSlot(targetSlot);
|
|
731
|
+
this.log.info(`Preparing checkpoint proposal ${checkpointNumber} for target slot ${targetSlot} during wall-clock slot ${slot}`, {
|
|
702
732
|
...logCtx,
|
|
703
|
-
proposer
|
|
733
|
+
proposer,
|
|
734
|
+
pipeliningEnabled: this.epochCache.isProposerPipeliningEnabled()
|
|
704
735
|
});
|
|
705
736
|
// Create and return the checkpoint proposal job
|
|
706
|
-
return this.createCheckpointProposalJob(
|
|
737
|
+
return this.createCheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedTo.blockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, syncedTo.proposedCheckpointData);
|
|
738
|
+
}
|
|
739
|
+
createCheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedToBlockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, proposedCheckpointData) {
|
|
740
|
+
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, this.setState.bind(this), this.tracer, this.log.getBindings(), proposedCheckpointData);
|
|
707
741
|
}
|
|
708
|
-
|
|
709
|
-
|
|
742
|
+
/**
|
|
743
|
+
* Returns the current sequencer state.
|
|
744
|
+
*/ getState() {
|
|
745
|
+
return this.state;
|
|
710
746
|
}
|
|
711
747
|
/**
|
|
712
748
|
* 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 +784,15 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
748
784
|
* Returns whether all dependencies have caught up.
|
|
749
785
|
* We don't check against the previous block submitted since it may have been reorg'd out.
|
|
750
786
|
*/ async checkSync(args) {
|
|
751
|
-
// Check that the archiver
|
|
752
|
-
//
|
|
753
|
-
//
|
|
754
|
-
const
|
|
755
|
-
const { slot
|
|
756
|
-
if (
|
|
787
|
+
// Check that the archiver has fully synced the L2 slot before the one we want to propose in.
|
|
788
|
+
// The archiver reports sync progress via L1 block timestamps and synced checkpoint slots.
|
|
789
|
+
// See getSyncedL2SlotNumber for how missed L1 blocks are handled.
|
|
790
|
+
const syncedL2Slot = await this.l2BlockSource.getSyncedL2SlotNumber();
|
|
791
|
+
const { slot } = args;
|
|
792
|
+
if (syncedL2Slot === undefined || syncedL2Slot + 1 < slot) {
|
|
757
793
|
this.log.debug(`Cannot propose block at next L2 slot ${slot} due to pending sync from L1`, {
|
|
758
794
|
slot,
|
|
759
|
-
|
|
760
|
-
l1Timestamp
|
|
795
|
+
syncedL2Slot
|
|
761
796
|
});
|
|
762
797
|
return undefined;
|
|
763
798
|
}
|
|
@@ -766,20 +801,25 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
766
801
|
number: syncSummary.latestBlockNumber,
|
|
767
802
|
hash: syncSummary.latestBlockHash
|
|
768
803
|
})),
|
|
769
|
-
this.l2BlockSource.getL2Tips().then((t)=>
|
|
804
|
+
this.l2BlockSource.getL2Tips().then((t)=>({
|
|
805
|
+
proposed: t.proposed,
|
|
806
|
+
checkpointed: t.checkpointed,
|
|
807
|
+
proposedCheckpoint: t.proposedCheckpoint
|
|
808
|
+
})),
|
|
770
809
|
this.p2pClient.getStatus().then((p2p)=>p2p.syncedToL2Block),
|
|
771
810
|
this.l1ToL2MessageSource.getL2Tips().then((t)=>t.proposed),
|
|
772
|
-
this.l2BlockSource.getPendingChainValidationStatus()
|
|
811
|
+
this.l2BlockSource.getPendingChainValidationStatus(),
|
|
812
|
+
this.l2BlockSource.getProposedCheckpointOnly()
|
|
773
813
|
]);
|
|
774
|
-
const [worldState,
|
|
814
|
+
const [worldState, l2Tips, p2p, l1ToL2MessageSource, pendingChainValidationStatus, proposedCheckpointData] = syncedBlocks;
|
|
775
815
|
// 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
816
|
// as the world state can compute the new genesis block hash, but other components use the hardcoded constant.
|
|
777
817
|
// TODO(palla/mbps): Fix the above. All components should be able to handle dynamic genesis block hashes.
|
|
778
|
-
const result =
|
|
818
|
+
const result = l2Tips.proposed.number === 0 && worldState.number === 0 && p2p.number === 0 && l1ToL2MessageSource.number === 0 || worldState.hash === l2Tips.proposed.hash && p2p.hash === l2Tips.proposed.hash && l1ToL2MessageSource.hash === l2Tips.proposed.hash;
|
|
779
819
|
if (!result) {
|
|
780
820
|
this.log.debug(`Sequencer sync check failed`, {
|
|
781
821
|
worldState,
|
|
782
|
-
l2BlockSource,
|
|
822
|
+
l2BlockSource: l2Tips.proposed,
|
|
783
823
|
p2p,
|
|
784
824
|
l1ToL2MessageSource
|
|
785
825
|
});
|
|
@@ -791,46 +831,52 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
791
831
|
const archive = new Fr((await this.worldState.getCommitted().getTreeInfo(MerkleTreeId.ARCHIVE)).root);
|
|
792
832
|
return {
|
|
793
833
|
checkpointNumber: CheckpointNumber.ZERO,
|
|
834
|
+
checkpointedCheckpointNumber: CheckpointNumber.ZERO,
|
|
794
835
|
blockNumber: BlockNumber.ZERO,
|
|
795
836
|
archive,
|
|
796
|
-
|
|
837
|
+
hasProposedCheckpoint: false,
|
|
838
|
+
syncedL2Slot,
|
|
797
839
|
pendingChainValidationStatus
|
|
798
840
|
};
|
|
799
841
|
}
|
|
800
|
-
const
|
|
801
|
-
if (!
|
|
842
|
+
const blockData = await this.l2BlockSource.getBlockData(blockNumber);
|
|
843
|
+
if (!blockData) {
|
|
802
844
|
// 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`);
|
|
845
|
+
this.log.error(`Failed to get L2 block data ${blockNumber} from the archiver with all components in sync`);
|
|
804
846
|
return undefined;
|
|
805
847
|
}
|
|
848
|
+
const hasProposedCheckpoint = l2Tips.proposedCheckpoint.checkpoint.number > l2Tips.checkpointed.checkpoint.number;
|
|
806
849
|
return {
|
|
807
|
-
|
|
808
|
-
blockNumber:
|
|
809
|
-
checkpointNumber:
|
|
810
|
-
|
|
811
|
-
|
|
850
|
+
blockData,
|
|
851
|
+
blockNumber: blockData.header.getBlockNumber(),
|
|
852
|
+
checkpointNumber: blockData.checkpointNumber,
|
|
853
|
+
checkpointedCheckpointNumber: l2Tips.checkpointed.checkpoint.number,
|
|
854
|
+
archive: blockData.archive.root,
|
|
855
|
+
hasProposedCheckpoint,
|
|
856
|
+
proposedCheckpointData,
|
|
857
|
+
syncedL2Slot,
|
|
812
858
|
pendingChainValidationStatus
|
|
813
859
|
};
|
|
814
860
|
}
|
|
815
861
|
/**
|
|
816
862
|
* Checks if we are the proposer for the next slot.
|
|
817
863
|
* @returns True if we can propose, and the proposer address (undefined if anyone can propose)
|
|
818
|
-
*/ async checkCanPropose(
|
|
864
|
+
*/ async checkCanPropose(targetSlot) {
|
|
819
865
|
let proposer;
|
|
820
866
|
try {
|
|
821
|
-
proposer = await this.epochCache.getProposerAttesterAddressInSlot(
|
|
867
|
+
proposer = await this.epochCache.getProposerAttesterAddressInSlot(targetSlot);
|
|
822
868
|
} catch (e) {
|
|
823
869
|
if (e instanceof NoCommitteeError) {
|
|
824
|
-
if (this.lastSlotForNoCommitteeWarning !==
|
|
825
|
-
this.lastSlotForNoCommitteeWarning =
|
|
826
|
-
this.log.warn(`Cannot propose at
|
|
870
|
+
if (this.lastSlotForNoCommitteeWarning !== targetSlot) {
|
|
871
|
+
this.lastSlotForNoCommitteeWarning = targetSlot;
|
|
872
|
+
this.log.warn(`Cannot propose at target slot ${targetSlot} since the committee does not exist on L1`);
|
|
827
873
|
}
|
|
828
874
|
return [
|
|
829
875
|
false,
|
|
830
876
|
undefined
|
|
831
877
|
];
|
|
832
878
|
}
|
|
833
|
-
this.log.error(`Error getting proposer for slot ${
|
|
879
|
+
this.log.error(`Error getting proposer for target slot ${targetSlot}`, e);
|
|
834
880
|
return [
|
|
835
881
|
false,
|
|
836
882
|
undefined
|
|
@@ -853,7 +899,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
853
899
|
const validatorAddresses = this.validatorClient.getValidatorAddresses();
|
|
854
900
|
const weAreProposer = validatorAddresses.some((addr)=>addr.equals(proposer));
|
|
855
901
|
if (!weAreProposer) {
|
|
856
|
-
this.log.debug(`Cannot propose at slot ${
|
|
902
|
+
this.log.debug(`Cannot propose at target slot ${targetSlot} since we are not a proposer`, {
|
|
903
|
+
targetSlot,
|
|
857
904
|
validatorAddresses,
|
|
858
905
|
proposer
|
|
859
906
|
});
|
|
@@ -862,6 +909,10 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
862
909
|
proposer
|
|
863
910
|
];
|
|
864
911
|
}
|
|
912
|
+
this.log.info(`We are the proposer for pipeline slot ${targetSlot}`, {
|
|
913
|
+
targetSlot,
|
|
914
|
+
proposer
|
|
915
|
+
});
|
|
865
916
|
return [
|
|
866
917
|
true,
|
|
867
918
|
proposer
|
|
@@ -871,7 +922,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
871
922
|
* 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
923
|
* This allows the sequencer to participate in governance/slashing votes even when it cannot build blocks.
|
|
873
924
|
*/ async tryVoteWhenSyncFails(args) {
|
|
874
|
-
const { slot } = args;
|
|
925
|
+
const { slot, targetSlot } = args;
|
|
875
926
|
// Prevent duplicate attempts in the same slot
|
|
876
927
|
if (this.lastSlotForFallbackVote === slot) {
|
|
877
928
|
this.log.trace(`Already attempted to vote in slot ${slot} (skipping)`);
|
|
@@ -894,7 +945,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
894
945
|
maxAllowedTime
|
|
895
946
|
});
|
|
896
947
|
// Check if we're a proposer or proposal is open
|
|
897
|
-
const [canPropose, proposer] = await this.checkCanPropose(
|
|
948
|
+
const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
|
|
898
949
|
if (!canPropose) {
|
|
899
950
|
this.log.trace(`Cannot vote in slot ${slot} since we are not a proposer`, {
|
|
900
951
|
slot,
|
|
@@ -910,8 +961,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
910
961
|
attestorAddress,
|
|
911
962
|
slot
|
|
912
963
|
});
|
|
913
|
-
// Enqueue governance and slashing votes
|
|
914
|
-
const voter = new CheckpointVoter(
|
|
964
|
+
// Enqueue governance and slashing votes (voter uses the target slot for L1 submission)
|
|
965
|
+
const voter = new CheckpointVoter(targetSlot, publisher, attestorAddress, this.validatorClient, this.slasherClient, this.l1Constants, this.config, this.metrics, this.log);
|
|
915
966
|
const votesPromises = voter.enqueueVotes();
|
|
916
967
|
const votes = await Promise.all(votesPromises);
|
|
917
968
|
if (votes.every((p)=>!p)) {
|
|
@@ -958,7 +1009,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
958
1009
|
* have the proposer try to invalidate, but if they fail, the sequencers in the committee are expected to try,
|
|
959
1010
|
* and if they fail, any sequencer will try as well.
|
|
960
1011
|
*/ async considerInvalidatingCheckpoint(syncedTo, currentSlot) {
|
|
961
|
-
const { pendingChainValidationStatus,
|
|
1012
|
+
const { pendingChainValidationStatus, syncedL2Slot } = syncedTo;
|
|
962
1013
|
if (pendingChainValidationStatus.valid) {
|
|
963
1014
|
return;
|
|
964
1015
|
}
|
|
@@ -969,7 +1020,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
969
1020
|
const { secondsBeforeInvalidatingBlockAsCommitteeMember, secondsBeforeInvalidatingBlockAsNonCommitteeMember } = this.config;
|
|
970
1021
|
const logData = {
|
|
971
1022
|
invalidL1Timestamp: invalidCheckpointTimestamp,
|
|
972
|
-
|
|
1023
|
+
syncedL2Slot,
|
|
973
1024
|
invalidCheckpoint: pendingChainValidationStatus.checkpoint,
|
|
974
1025
|
secondsBeforeInvalidatingBlockAsCommitteeMember,
|
|
975
1026
|
secondsBeforeInvalidatingBlockAsNonCommitteeMember,
|
|
@@ -1060,6 +1111,9 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
1060
1111
|
getValidatorAddresses() {
|
|
1061
1112
|
return this.validatorClient?.getValidatorAddresses();
|
|
1062
1113
|
}
|
|
1114
|
+
/** Updates the publisher factory's node keystore adapter after a keystore reload. */ updatePublisherNodeKeyStore(adapter) {
|
|
1115
|
+
this.publisherFactory.updateNodeKeyStore(adapter);
|
|
1116
|
+
}
|
|
1063
1117
|
getConfig() {
|
|
1064
1118
|
return this.config;
|
|
1065
1119
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
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?;
|
|
9
7
|
/**
|
|
10
8
|
* How late into the slot can we be to start working. Computed as the total time needed for assembling and publishing a block,
|
|
11
9
|
* assuming an execution time equal to `minExecutionTime`, subtracted from the slot duration. This means that, if the proposer
|
|
@@ -59,7 +57,7 @@ export declare class SequencerTimetable {
|
|
|
59
57
|
p2pPropagationTime?: number;
|
|
60
58
|
blockDurationMs?: number;
|
|
61
59
|
enforce: boolean;
|
|
62
|
-
}, metrics?: SequencerMetrics | undefined, log?:
|
|
60
|
+
}, metrics?: SequencerMetrics | undefined, log?: Logger | undefined);
|
|
63
61
|
getMaxAllowedTime(state: Extract<SequencerState, SequencerState.STOPPED | SequencerState.IDLE | SequencerState.SYNCHRONIZING>): undefined;
|
|
64
62
|
getMaxAllowedTime(state: Exclude<SequencerState, SequencerState.STOPPED | SequencerState.IDLE | SequencerState.SYNCHRONIZING>): number;
|
|
65
63
|
getMaxAllowedTime(state: SequencerState): number | undefined;
|
|
@@ -87,4 +85,4 @@ export declare class SequencerTimetable {
|
|
|
87
85
|
isLastBlock: boolean;
|
|
88
86
|
};
|
|
89
87
|
}
|
|
90
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
88
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZXRhYmxlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL3RpbWV0YWJsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQVNwRCxPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNyRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRTVDLHFCQUFhLGtCQUFrQjtJQXFFM0IsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUM7SUFDekIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUM7SUFyRXZCOzs7O09BSUc7SUFDSCxTQUFnQixrQkFBa0IsRUFBRSxNQUFNLENBQUM7SUFFM0M7Ozs7T0FJRztJQUNILFNBQWdCLG9CQUFvQixFQUFFLE1BQU0sQ0FBQztJQUU3Qzs7Ozs7T0FLRztJQUNILFNBQWdCLDBCQUEwQixFQUFFLE1BQU0sQ0FBQztJQUVuRDs7OztPQUlHO0lBQ0gsU0FBZ0IsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDO0lBRXpDOzs7T0FHRztJQUNILFNBQWdCLGdCQUFnQixFQUFFLE1BQU0sQ0FBc0I7SUFFOUQsdURBQXVEO0lBQ3ZELFNBQWdCLDRCQUE0QixFQUFFLE1BQU0sQ0FBa0M7SUFFdEYsbUdBQW1HO0lBQ25HLFNBQWdCLGtCQUFrQixFQUFFLE1BQU0sQ0FBQztJQUUzQyxtRkFBbUY7SUFDbkYsU0FBZ0Isc0JBQXNCLEVBQUUsTUFBTSxDQUE0QjtJQUUxRSx3Q0FBd0M7SUFDeEMsU0FBZ0Isb0JBQW9CLEVBQUUsTUFBTSxDQUFDO0lBRTdDLGtGQUFrRjtJQUNsRixTQUFnQixpQkFBaUIsRUFBRSxNQUFNLENBQUM7SUFFMUMsNERBQTREO0lBQzVELFNBQWdCLE9BQU8sRUFBRSxPQUFPLENBQUM7SUFFakMsb0dBQW9HO0lBQ3BHLFNBQWdCLGFBQWEsRUFBRSxNQUFNLEdBQUcsU0FBUyxDQUFDO0lBRWxELDRFQUE0RTtJQUM1RSxTQUFnQixpQkFBaUIsRUFBRSxNQUFNLENBQUM7SUFFMUMsWUFDRSxJQUFJLEVBQUU7UUFDSixvQkFBb0IsRUFBRSxNQUFNLENBQUM7UUFDN0IsaUJBQWlCLEVBQUUsTUFBTSxDQUFDO1FBQzFCLGdCQUFnQixFQUFFLE1BQU0sQ0FBQztRQUN6QixrQkFBa0IsQ0FBQyxFQUFFLE1BQU0sQ0FBQztRQUM1QixlQUFlLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDekIsT0FBTyxFQUFFLE9BQU8sQ0FBQztLQUNsQixFQUNnQixPQUFPLENBQUMsOEJBQWtCLEVBQzFCLEdBQUcsQ0FBQyxvQkFBUSxFQTJFOUI7SUFFTSxpQkFBaUIsQ0FDdEIsS0FBSyxFQUFFLE9BQU8sQ0FBQyxjQUFjLEVBQUUsY0FBYyxDQUFDLE9BQU8sR0FBRyxjQUFjLENBQUMsSUFBSSxHQUFHLGNBQWMsQ0FBQyxhQUFhLENBQUMsR0FDMUcsU0FBUyxDQUFDO0lBQ04saUJBQWlCLENBQ3RCLEtBQUssRUFBRSxPQUFPLENBQUMsY0FBYyxFQUFFLGNBQWMsQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLElBQUksR0FBRyxjQUFjLENBQUMsYUFBYSxDQUFDLEdBQzFHLE1BQU0sQ0FBQztJQUNILGlCQUFpQixDQUFDLEtBQUssRUFBRSxjQUFjLEdBQUcsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQTJCN0QsY0FBYyxDQUFDLFFBQVEsRUFBRSxjQUFjLEVBQUUsZUFBZSxFQUFFLE1BQU0sUUFpQnRFO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSSxpQkFBaUIsQ0FDdEIsZUFBZSxFQUFFLE1BQU0sR0FFckI7UUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDO1FBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQztRQUFDLFdBQVcsRUFBRSxJQUFJLENBQUE7S0FBRSxHQUMxRDtRQUFFLFFBQVEsRUFBRSxLQUFLLENBQUM7UUFBQyxRQUFRLEVBQUUsU0FBUyxDQUFDO1FBQUMsV0FBVyxFQUFFLEtBQUssQ0FBQTtLQUFFLEdBQzVEO1FBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQztRQUFDLFFBQVEsRUFBRSxNQUFNLENBQUM7UUFBQyxXQUFXLEVBQUUsT0FBTyxDQUFBO0tBQUUsQ0F3RGhFO0NBQ0YifQ==
|
|
@@ -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":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AASpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,qBAAa,kBAAkB;IAqE3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;IArEvB;;;;OAIG;IACH,SAAgB,kBAAkB,EAAE,MAAM,CAAC;IAE3C;;;;OAIG;IACH,SAAgB,oBAAoB,EAAE,MAAM,CAAC;IAE7C;;;;;OAKG;IACH,SAAgB,0BAA0B,EAAE,MAAM,CAAC;IAEnD;;;;OAIG;IACH,SAAgB,gBAAgB,EAAE,MAAM,CAAC;IAEzC;;;OAGG;IACH,SAAgB,gBAAgB,EAAE,MAAM,CAAsB;IAE9D,uDAAuD;IACvD,SAAgB,4BAA4B,EAAE,MAAM,CAAkC;IAEtF,mGAAmG;IACnG,SAAgB,kBAAkB,EAAE,MAAM,CAAC;IAE3C,mFAAmF;IACnF,SAAgB,sBAAsB,EAAE,MAAM,CAA4B;IAE1E,wCAAwC;IACxC,SAAgB,oBAAoB,EAAE,MAAM,CAAC;IAE7C,kFAAkF;IAClF,SAAgB,iBAAiB,EAAE,MAAM,CAAC;IAE1C,4DAA4D;IAC5D,SAAgB,OAAO,EAAE,OAAO,CAAC;IAEjC,oGAAoG;IACpG,SAAgB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAElD,4EAA4E;IAC5E,SAAgB,iBAAiB,EAAE,MAAM,CAAC;IAE1C,YACE,IAAI,EAAE;QACJ,oBAAoB,EAAE,MAAM,CAAC;QAC7B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,gBAAgB,EAAE,MAAM,CAAC;QACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,OAAO,EAAE,OAAO,CAAC;KAClB,EACgB,OAAO,CAAC,8BAAkB,EAC1B,GAAG,CAAC,oBAAQ,EA2E9B;IAEM,iBAAiB,CACtB,KAAK,EAAE,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,IAAI,GAAG,cAAc,CAAC,aAAa,CAAC,GAC1G,SAAS,CAAC;IACN,iBAAiB,CACtB,KAAK,EAAE,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,IAAI,GAAG,cAAc,CAAC,aAAa,CAAC,GAC1G,MAAM,CAAC;IACH,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAAC;IA2B7D,cAAc,CAAC,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,QAiBtE;IAED;;;;;;;;OAQG;IACI,iBAAiB,CACtB,eAAe,EAAE,MAAM,GAErB;QAAE,QAAQ,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,SAAS,CAAC;QAAC,WAAW,EAAE,IAAI,CAAA;KAAE,GAC1D;QAAE,QAAQ,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,SAAS,CAAC;QAAC,WAAW,EAAE,KAAK,CAAA;KAAE,GAC5D;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,CAwDhE;CACF"}
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { DEFAULT_ATTESTATION_PROPAGATION_TIME as DEFAULT_P2P_PROPAGATION_TIME } from '../config.js';
|
|
1
|
+
import { CHECKPOINT_ASSEMBLE_TIME, CHECKPOINT_INITIALIZATION_TIME, DEFAULT_P2P_PROPAGATION_TIME, MIN_EXECUTION_TIME } from '@aztec/stdlib/timetable';
|
|
3
2
|
import { SequencerTooSlowError } from './errors.js';
|
|
4
3
|
import { SequencerState } from './utils.js';
|
|
5
|
-
export const MIN_EXECUTION_TIME = 2;
|
|
6
|
-
export const CHECKPOINT_INITIALIZATION_TIME = 1;
|
|
7
|
-
export const CHECKPOINT_ASSEMBLE_TIME = 1;
|
|
8
4
|
export class SequencerTimetable {
|
|
9
5
|
metrics;
|
|
10
6
|
log;
|
|
@@ -41,7 +37,7 @@ export class SequencerTimetable {
|
|
|
41
37
|
/** Whether assertTimeLeft will throw if not enough time. */ enforce;
|
|
42
38
|
/** Duration per block when building multiple blocks per slot (undefined = single block per slot) */ blockDuration;
|
|
43
39
|
/** Maximum number of blocks that can be built in this slot configuration */ maxNumberOfBlocks;
|
|
44
|
-
constructor(opts, metrics, log
|
|
40
|
+
constructor(opts, metrics, log){
|
|
45
41
|
this.metrics = metrics;
|
|
46
42
|
this.log = log;
|
|
47
43
|
this.minExecutionTime = MIN_EXECUTION_TIME;
|
|
@@ -84,7 +80,7 @@ export class SequencerTimetable {
|
|
|
84
80
|
this.checkpointFinalizationTime;
|
|
85
81
|
const initializeDeadline = this.aztecSlotDuration - minWorkToDo;
|
|
86
82
|
this.initializeDeadline = initializeDeadline;
|
|
87
|
-
this.log
|
|
83
|
+
this.log?.info(`Sequencer timetable initialized with ${this.maxNumberOfBlocks} blocks per slot (${this.enforce ? 'enforced' : 'not enforced'})`, {
|
|
88
84
|
ethereumSlotDuration: this.ethereumSlotDuration,
|
|
89
85
|
aztecSlotDuration: this.aztecSlotDuration,
|
|
90
86
|
l1PublishingTime: this.l1PublishingTime,
|
|
@@ -141,7 +137,7 @@ export class SequencerTimetable {
|
|
|
141
137
|
throw new SequencerTooSlowError(newState, maxAllowedTime, secondsIntoSlot);
|
|
142
138
|
}
|
|
143
139
|
this.metrics?.recordStateTransitionBufferMs(Math.floor(bufferSeconds * 1000), newState);
|
|
144
|
-
this.log
|
|
140
|
+
this.log?.trace(`Enough time to transition to ${newState}`, {
|
|
145
141
|
maxAllowedTime,
|
|
146
142
|
secondsIntoSlot
|
|
147
143
|
});
|
|
@@ -175,7 +171,7 @@ export class SequencerTimetable {
|
|
|
175
171
|
const available = (maxAllowed - secondsIntoSlot) / 2; // Split remaining time: half for execution, half for re-execution
|
|
176
172
|
const canStart = available >= this.minExecutionTime;
|
|
177
173
|
const deadline = secondsIntoSlot + available;
|
|
178
|
-
this.log
|
|
174
|
+
this.log?.verbose(`${canStart ? 'Can' : 'Cannot'} start single-block checkpoint at ${secondsIntoSlot}s into slot`, {
|
|
179
175
|
secondsIntoSlot,
|
|
180
176
|
maxAllowed,
|
|
181
177
|
available,
|
|
@@ -197,7 +193,7 @@ export class SequencerTimetable {
|
|
|
197
193
|
if (timeUntilDeadline >= this.minExecutionTime) {
|
|
198
194
|
// Found an available sub-slot! Is this the last one?
|
|
199
195
|
const isLastBlock = subSlot === this.maxNumberOfBlocks;
|
|
200
|
-
this.log
|
|
196
|
+
this.log?.verbose(`Can start ${isLastBlock ? 'last block' : 'block'} in sub-slot ${subSlot} with deadline ${deadline}s`, {
|
|
201
197
|
secondsIntoSlot,
|
|
202
198
|
deadline,
|
|
203
199
|
timeUntilDeadline,
|
|
@@ -212,7 +208,7 @@ export class SequencerTimetable {
|
|
|
212
208
|
}
|
|
213
209
|
}
|
|
214
210
|
// No sub-slots available with enough time
|
|
215
|
-
this.log
|
|
211
|
+
this.log?.verbose(`No time left to start any more blocks`, {
|
|
216
212
|
secondsIntoSlot,
|
|
217
213
|
maxBlocks: this.maxNumberOfBlocks,
|
|
218
214
|
initializationOffset: this.initializationOffset
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
|
|
2
|
-
export type SequencerRollupConstants = Pick<L1RollupConstants, 'ethereumSlotDuration' | 'l1GenesisTime' | 'slotDuration'>;
|
|
3
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
2
|
+
export type SequencerRollupConstants = Pick<L1RollupConstants, 'ethereumSlotDuration' | 'l1GenesisTime' | 'slotDuration' | 'rollupManaLimit'>;
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXF1ZW5jZXIvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUVyRSxNQUFNLE1BQU0sd0JBQXdCLEdBQUcsSUFBSSxDQUN6QyxpQkFBaUIsRUFDakIsc0JBQXNCLEdBQUcsZUFBZSxHQUFHLGNBQWMsR0FBRyxpQkFBaUIsQ0FDOUUsQ0FBQyJ9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sequencer/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACzC,iBAAiB,EACjB,sBAAsB,GAAG,eAAe,GAAG,cAAc,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sequencer/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACzC,iBAAiB,EACjB,sBAAsB,GAAG,eAAe,GAAG,cAAc,GAAG,iBAAiB,CAC9E,CAAC"}
|