@aztec/sequencer-client 0.0.1-commit.f504929 → 0.0.1-commit.f5d02921e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/client/sequencer-client.d.ts +4 -1
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +46 -23
- package/dest/config.d.ts +25 -5
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +21 -12
- package/dest/global_variable_builder/global_builder.d.ts +15 -9
- 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 +13 -1
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +17 -2
- package/dest/publisher/sequencer-publisher-factory.d.ts +3 -3
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +2 -2
- package/dest/publisher/sequencer-publisher.d.ts +52 -25
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +98 -42
- 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 +284 -158
- 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 +5 -1
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +11 -0
- package/dest/sequencer/sequencer.d.ts +23 -10
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +123 -68
- package/dest/sequencer/timetable.d.ts +4 -3
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +6 -7
- package/dest/sequencer/types.d.ts +2 -2
- package/dest/sequencer/types.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/package.json +27 -28
- package/src/client/sequencer-client.ts +56 -21
- package/src/config.ts +28 -14
- package/src/global_variable_builder/global_builder.ts +37 -26
- package/src/global_variable_builder/index.ts +1 -1
- package/src/publisher/config.ts +32 -0
- package/src/publisher/sequencer-publisher-factory.ts +3 -3
- package/src/publisher/sequencer-publisher.ts +144 -54
- package/src/sequencer/checkpoint_proposal_job.ts +367 -175
- package/src/sequencer/checkpoint_voter.ts +1 -12
- package/src/sequencer/events.ts +1 -1
- package/src/sequencer/metrics.ts +14 -0
- package/src/sequencer/sequencer.ts +178 -79
- package/src/sequencer/timetable.ts +7 -7
- package/src/sequencer/types.ts +1 -1
- package/src/test/mock_checkpoint_builder.ts +51 -48
|
@@ -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,6 +454,7 @@ _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
459
|
/** Config for the sequencer */ config;
|
|
459
460
|
constructor(publisherFactory, validatorClient, globalsBuilder, p2pClient, worldState, slasherClient, l2BlockSource, l1ToL2MessageSource, checkpointsBuilder, l1Constants, dateProvider, epochCache, rollupContract, config, telemetry = getTelemetryClient(), log = createLogger('sequencer')){
|
|
@@ -494,8 +495,9 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
494
495
|
this.setState(SequencerState.STOPPING, undefined, {
|
|
495
496
|
force: true
|
|
496
497
|
});
|
|
497
|
-
this.publisherFactory.
|
|
498
|
+
await this.publisherFactory.stopAll();
|
|
498
499
|
await this.runningPromise?.stop();
|
|
500
|
+
await this.lastCheckpointProposalJob?.awaitPendingSubmission();
|
|
499
501
|
this.setState(SequencerState.STOPPED, undefined, {
|
|
500
502
|
force: true
|
|
501
503
|
});
|
|
@@ -539,22 +541,25 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
539
541
|
* - Submit checkpoint
|
|
540
542
|
*/ async work() {
|
|
541
543
|
this.setState(SequencerState.SYNCHRONIZING, undefined);
|
|
542
|
-
const { slot, ts,
|
|
544
|
+
const { slot, ts, nowSeconds, epoch } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
545
|
+
const { slot: targetSlot, epoch: targetEpoch } = this.epochCache.getTargetEpochAndSlotInNextL1Slot();
|
|
543
546
|
// Check if we are synced and it's our slot, grab a publisher, check previous block invalidation, etc
|
|
544
|
-
const checkpointProposalJob = await this.prepareCheckpointProposal(epoch,
|
|
547
|
+
const checkpointProposalJob = await this.prepareCheckpointProposal(slot, targetSlot, epoch, targetEpoch, ts, nowSeconds);
|
|
545
548
|
if (!checkpointProposalJob) {
|
|
546
549
|
return;
|
|
547
550
|
}
|
|
551
|
+
// Track the job so we can await its pending L1 submission during shutdown
|
|
552
|
+
this.lastCheckpointProposalJob = checkpointProposalJob;
|
|
548
553
|
// Execute the checkpoint proposal job
|
|
549
554
|
const checkpoint = await checkpointProposalJob.execute();
|
|
550
555
|
// Update last checkpoint proposed (currently unused)
|
|
551
556
|
if (checkpoint) {
|
|
552
557
|
this.lastCheckpointProposed = checkpoint;
|
|
553
558
|
}
|
|
554
|
-
// Log fee strategy comparison if on fisherman
|
|
555
|
-
if (this.config.fishermanMode && (this.lastEpochForStrategyComparison === undefined ||
|
|
556
|
-
this.logStrategyComparison(
|
|
557
|
-
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;
|
|
558
563
|
}
|
|
559
564
|
return checkpoint;
|
|
560
565
|
}
|
|
@@ -562,17 +567,17 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
562
567
|
* Prepares the checkpoint proposal by performing all necessary checks and setup.
|
|
563
568
|
* This is the initial step in the main loop.
|
|
564
569
|
* @returns CheckpointProposalJob if successful, undefined if we are not yet synced or are not the proposer.
|
|
565
|
-
*/ async prepareCheckpointProposal(epoch,
|
|
566
|
-
// 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)
|
|
567
572
|
// We only check this if enforce timetable is set, since we want to keep processing the same slot if we are not
|
|
568
573
|
// running against actual time (eg when we use sandbox-style automining)
|
|
569
|
-
if (this.lastSlotForCheckpointProposalJob && this.lastSlotForCheckpointProposalJob >=
|
|
570
|
-
this.log.trace(`
|
|
574
|
+
if (this.lastSlotForCheckpointProposalJob && this.lastSlotForCheckpointProposalJob >= targetSlot && this.config.enforceTimeTable) {
|
|
575
|
+
this.log.trace(`Target slot ${targetSlot} has already been processed`);
|
|
571
576
|
return undefined;
|
|
572
577
|
}
|
|
573
|
-
// But if we have already proposed for this slot,
|
|
574
|
-
if (this.lastCheckpointProposed && this.lastCheckpointProposed.header.slotNumber >=
|
|
575
|
-
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}`);
|
|
576
581
|
return undefined;
|
|
577
582
|
}
|
|
578
583
|
// Check all components are synced to latest as seen by the archiver (queries all subsystems)
|
|
@@ -583,16 +588,18 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
583
588
|
if (!syncedTo) {
|
|
584
589
|
await this.tryVoteWhenSyncFails({
|
|
585
590
|
slot,
|
|
591
|
+
targetSlot,
|
|
586
592
|
ts
|
|
587
593
|
});
|
|
588
594
|
return undefined;
|
|
589
595
|
}
|
|
590
|
-
// 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.
|
|
591
597
|
// Still perform governance/slashing voting (as proposer) once per slot.
|
|
592
|
-
|
|
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);
|
|
593
600
|
if (isEscapeHatchOpen) {
|
|
594
601
|
this.setState(SequencerState.PROPOSER_CHECK, slot);
|
|
595
|
-
const [canPropose, proposer] = await this.checkCanPropose(
|
|
602
|
+
const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
|
|
596
603
|
if (canPropose) {
|
|
597
604
|
await this.tryVoteWhenEscapeHatchOpen({
|
|
598
605
|
slot,
|
|
@@ -609,26 +616,33 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
609
616
|
}
|
|
610
617
|
// Next checkpoint follows from the last synced one
|
|
611
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
|
+
}
|
|
612
626
|
const logCtx = {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
syncedToL2Slot: getSlotAtTimestamp(syncedTo.l1Timestamp, this.l1Constants),
|
|
627
|
+
nowSeconds,
|
|
628
|
+
syncedToL2Slot: syncedTo.syncedL2Slot,
|
|
616
629
|
slot,
|
|
630
|
+
targetSlot,
|
|
617
631
|
slotTs: ts,
|
|
618
632
|
checkpointNumber,
|
|
619
633
|
isPendingChainValid: pick(syncedTo.pendingChainValidationStatus, 'valid', 'reason', 'invalidIndex')
|
|
620
634
|
};
|
|
621
|
-
// Check that we are a proposer for the
|
|
635
|
+
// Check that we are a proposer for the target slot.
|
|
622
636
|
this.setState(SequencerState.PROPOSER_CHECK, slot);
|
|
623
|
-
const [canPropose, proposer] = await this.checkCanPropose(
|
|
637
|
+
const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
|
|
624
638
|
// If we are not a proposer check if we should invalidate an invalid checkpoint, and bail
|
|
625
639
|
if (!canPropose) {
|
|
626
640
|
await this.considerInvalidatingCheckpoint(syncedTo, slot);
|
|
627
641
|
return undefined;
|
|
628
642
|
}
|
|
629
|
-
// Check that the slot is not taken by a block already (should never happen, since only us can propose for this slot)
|
|
630
|
-
if (syncedTo.blockData && syncedTo.blockData.header.getSlot() >=
|
|
631
|
-
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}`, {
|
|
632
646
|
...logCtx,
|
|
633
647
|
block: syncedTo.blockData.header.toInspect()
|
|
634
648
|
});
|
|
@@ -648,10 +662,35 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
648
662
|
this.log.debug(`Set proposer address ${proposer} for simulation in fisherman mode`);
|
|
649
663
|
}
|
|
650
664
|
// Prepare invalidation request if the pending chain is invalid (returns undefined if no need)
|
|
651
|
-
|
|
652
|
-
//
|
|
653
|
-
//
|
|
654
|
-
|
|
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);
|
|
655
694
|
if (canProposeCheck === undefined) {
|
|
656
695
|
this.log.warn(`Cannot propose checkpoint ${checkpointNumber} at slot ${slot} due to failed rollup contract check`, logCtx);
|
|
657
696
|
this.emit('proposer-rollup-check-failed', {
|
|
@@ -661,11 +700,11 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
661
700
|
this.metrics.recordCheckpointPrecheckFailed('rollup_contract_check_failed');
|
|
662
701
|
return undefined;
|
|
663
702
|
}
|
|
664
|
-
if (canProposeCheck.slot !==
|
|
665
|
-
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}.`, {
|
|
666
705
|
...logCtx,
|
|
667
706
|
rollup: canProposeCheck,
|
|
668
|
-
expectedSlot:
|
|
707
|
+
expectedSlot: targetSlot
|
|
669
708
|
});
|
|
670
709
|
this.emit('proposer-rollup-check-failed', {
|
|
671
710
|
reason: 'Slot mismatch',
|
|
@@ -687,17 +726,18 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
687
726
|
this.metrics.recordCheckpointPrecheckFailed('block_number_mismatch');
|
|
688
727
|
return undefined;
|
|
689
728
|
}
|
|
690
|
-
this.lastSlotForCheckpointProposalJob =
|
|
691
|
-
await this.p2pClient.prepareForSlot(
|
|
692
|
-
this.log.info(`Preparing checkpoint proposal ${checkpointNumber}
|
|
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}`, {
|
|
693
732
|
...logCtx,
|
|
694
|
-
proposer
|
|
733
|
+
proposer,
|
|
734
|
+
pipeliningEnabled: this.epochCache.isProposerPipeliningEnabled()
|
|
695
735
|
});
|
|
696
736
|
// Create and return the checkpoint proposal job
|
|
697
|
-
return this.createCheckpointProposalJob(
|
|
737
|
+
return this.createCheckpointProposalJob(slot, targetSlot, targetEpoch, checkpointNumber, syncedTo.blockNumber, proposer, publisher, attestorAddress, invalidateCheckpoint, syncedTo.proposedCheckpointData);
|
|
698
738
|
}
|
|
699
|
-
createCheckpointProposalJob(
|
|
700
|
-
return new CheckpointProposalJob(
|
|
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);
|
|
701
741
|
}
|
|
702
742
|
/**
|
|
703
743
|
* Returns the current sequencer state.
|
|
@@ -744,16 +784,15 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
744
784
|
* Returns whether all dependencies have caught up.
|
|
745
785
|
* We don't check against the previous block submitted since it may have been reorg'd out.
|
|
746
786
|
*/ async checkSync(args) {
|
|
747
|
-
// Check that the archiver
|
|
748
|
-
//
|
|
749
|
-
//
|
|
750
|
-
const
|
|
751
|
-
const { slot
|
|
752
|
-
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) {
|
|
753
793
|
this.log.debug(`Cannot propose block at next L2 slot ${slot} due to pending sync from L1`, {
|
|
754
794
|
slot,
|
|
755
|
-
|
|
756
|
-
l1Timestamp
|
|
795
|
+
syncedL2Slot
|
|
757
796
|
});
|
|
758
797
|
return undefined;
|
|
759
798
|
}
|
|
@@ -762,20 +801,25 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
762
801
|
number: syncSummary.latestBlockNumber,
|
|
763
802
|
hash: syncSummary.latestBlockHash
|
|
764
803
|
})),
|
|
765
|
-
this.l2BlockSource.getL2Tips().then((t)=>
|
|
804
|
+
this.l2BlockSource.getL2Tips().then((t)=>({
|
|
805
|
+
proposed: t.proposed,
|
|
806
|
+
checkpointed: t.checkpointed,
|
|
807
|
+
proposedCheckpoint: t.proposedCheckpoint
|
|
808
|
+
})),
|
|
766
809
|
this.p2pClient.getStatus().then((p2p)=>p2p.syncedToL2Block),
|
|
767
810
|
this.l1ToL2MessageSource.getL2Tips().then((t)=>t.proposed),
|
|
768
|
-
this.l2BlockSource.getPendingChainValidationStatus()
|
|
811
|
+
this.l2BlockSource.getPendingChainValidationStatus(),
|
|
812
|
+
this.l2BlockSource.getProposedCheckpointOnly()
|
|
769
813
|
]);
|
|
770
|
-
const [worldState,
|
|
814
|
+
const [worldState, l2Tips, p2p, l1ToL2MessageSource, pendingChainValidationStatus, proposedCheckpointData] = syncedBlocks;
|
|
771
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,
|
|
772
816
|
// as the world state can compute the new genesis block hash, but other components use the hardcoded constant.
|
|
773
817
|
// TODO(palla/mbps): Fix the above. All components should be able to handle dynamic genesis block hashes.
|
|
774
|
-
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;
|
|
775
819
|
if (!result) {
|
|
776
820
|
this.log.debug(`Sequencer sync check failed`, {
|
|
777
821
|
worldState,
|
|
778
|
-
l2BlockSource,
|
|
822
|
+
l2BlockSource: l2Tips.proposed,
|
|
779
823
|
p2p,
|
|
780
824
|
l1ToL2MessageSource
|
|
781
825
|
});
|
|
@@ -787,9 +831,11 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
787
831
|
const archive = new Fr((await this.worldState.getCommitted().getTreeInfo(MerkleTreeId.ARCHIVE)).root);
|
|
788
832
|
return {
|
|
789
833
|
checkpointNumber: CheckpointNumber.ZERO,
|
|
834
|
+
checkpointedCheckpointNumber: CheckpointNumber.ZERO,
|
|
790
835
|
blockNumber: BlockNumber.ZERO,
|
|
791
836
|
archive,
|
|
792
|
-
|
|
837
|
+
hasProposedCheckpoint: false,
|
|
838
|
+
syncedL2Slot,
|
|
793
839
|
pendingChainValidationStatus
|
|
794
840
|
};
|
|
795
841
|
}
|
|
@@ -799,34 +845,38 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
799
845
|
this.log.error(`Failed to get L2 block data ${blockNumber} from the archiver with all components in sync`);
|
|
800
846
|
return undefined;
|
|
801
847
|
}
|
|
848
|
+
const hasProposedCheckpoint = l2Tips.proposedCheckpoint.checkpoint.number > l2Tips.checkpointed.checkpoint.number;
|
|
802
849
|
return {
|
|
803
850
|
blockData,
|
|
804
851
|
blockNumber: blockData.header.getBlockNumber(),
|
|
805
852
|
checkpointNumber: blockData.checkpointNumber,
|
|
853
|
+
checkpointedCheckpointNumber: l2Tips.checkpointed.checkpoint.number,
|
|
806
854
|
archive: blockData.archive.root,
|
|
807
|
-
|
|
855
|
+
hasProposedCheckpoint,
|
|
856
|
+
proposedCheckpointData,
|
|
857
|
+
syncedL2Slot,
|
|
808
858
|
pendingChainValidationStatus
|
|
809
859
|
};
|
|
810
860
|
}
|
|
811
861
|
/**
|
|
812
862
|
* Checks if we are the proposer for the next slot.
|
|
813
863
|
* @returns True if we can propose, and the proposer address (undefined if anyone can propose)
|
|
814
|
-
*/ async checkCanPropose(
|
|
864
|
+
*/ async checkCanPropose(targetSlot) {
|
|
815
865
|
let proposer;
|
|
816
866
|
try {
|
|
817
|
-
proposer = await this.epochCache.getProposerAttesterAddressInSlot(
|
|
867
|
+
proposer = await this.epochCache.getProposerAttesterAddressInSlot(targetSlot);
|
|
818
868
|
} catch (e) {
|
|
819
869
|
if (e instanceof NoCommitteeError) {
|
|
820
|
-
if (this.lastSlotForNoCommitteeWarning !==
|
|
821
|
-
this.lastSlotForNoCommitteeWarning =
|
|
822
|
-
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`);
|
|
823
873
|
}
|
|
824
874
|
return [
|
|
825
875
|
false,
|
|
826
876
|
undefined
|
|
827
877
|
];
|
|
828
878
|
}
|
|
829
|
-
this.log.error(`Error getting proposer for slot ${
|
|
879
|
+
this.log.error(`Error getting proposer for target slot ${targetSlot}`, e);
|
|
830
880
|
return [
|
|
831
881
|
false,
|
|
832
882
|
undefined
|
|
@@ -849,7 +899,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
849
899
|
const validatorAddresses = this.validatorClient.getValidatorAddresses();
|
|
850
900
|
const weAreProposer = validatorAddresses.some((addr)=>addr.equals(proposer));
|
|
851
901
|
if (!weAreProposer) {
|
|
852
|
-
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,
|
|
853
904
|
validatorAddresses,
|
|
854
905
|
proposer
|
|
855
906
|
});
|
|
@@ -858,6 +909,10 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
858
909
|
proposer
|
|
859
910
|
];
|
|
860
911
|
}
|
|
912
|
+
this.log.info(`We are the proposer for pipeline slot ${targetSlot}`, {
|
|
913
|
+
targetSlot,
|
|
914
|
+
proposer
|
|
915
|
+
});
|
|
861
916
|
return [
|
|
862
917
|
true,
|
|
863
918
|
proposer
|
|
@@ -867,7 +922,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
867
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.
|
|
868
923
|
* This allows the sequencer to participate in governance/slashing votes even when it cannot build blocks.
|
|
869
924
|
*/ async tryVoteWhenSyncFails(args) {
|
|
870
|
-
const { slot } = args;
|
|
925
|
+
const { slot, targetSlot } = args;
|
|
871
926
|
// Prevent duplicate attempts in the same slot
|
|
872
927
|
if (this.lastSlotForFallbackVote === slot) {
|
|
873
928
|
this.log.trace(`Already attempted to vote in slot ${slot} (skipping)`);
|
|
@@ -890,7 +945,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
890
945
|
maxAllowedTime
|
|
891
946
|
});
|
|
892
947
|
// Check if we're a proposer or proposal is open
|
|
893
|
-
const [canPropose, proposer] = await this.checkCanPropose(
|
|
948
|
+
const [canPropose, proposer] = await this.checkCanPropose(targetSlot);
|
|
894
949
|
if (!canPropose) {
|
|
895
950
|
this.log.trace(`Cannot vote in slot ${slot} since we are not a proposer`, {
|
|
896
951
|
slot,
|
|
@@ -906,8 +961,8 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
906
961
|
attestorAddress,
|
|
907
962
|
slot
|
|
908
963
|
});
|
|
909
|
-
// Enqueue governance and slashing votes
|
|
910
|
-
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);
|
|
911
966
|
const votesPromises = voter.enqueueVotes();
|
|
912
967
|
const votes = await Promise.all(votesPromises);
|
|
913
968
|
if (votes.every((p)=>!p)) {
|
|
@@ -954,7 +1009,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
954
1009
|
* have the proposer try to invalidate, but if they fail, the sequencers in the committee are expected to try,
|
|
955
1010
|
* and if they fail, any sequencer will try as well.
|
|
956
1011
|
*/ async considerInvalidatingCheckpoint(syncedTo, currentSlot) {
|
|
957
|
-
const { pendingChainValidationStatus,
|
|
1012
|
+
const { pendingChainValidationStatus, syncedL2Slot } = syncedTo;
|
|
958
1013
|
if (pendingChainValidationStatus.valid) {
|
|
959
1014
|
return;
|
|
960
1015
|
}
|
|
@@ -965,7 +1020,7 @@ _dec = trackSpan('Sequencer.work'), _dec1 = trackSpan('Sequencer.prepareCheckpoi
|
|
|
965
1020
|
const { secondsBeforeInvalidatingBlockAsCommitteeMember, secondsBeforeInvalidatingBlockAsNonCommitteeMember } = this.config;
|
|
966
1021
|
const logData = {
|
|
967
1022
|
invalidL1Timestamp: invalidCheckpointTimestamp,
|
|
968
|
-
|
|
1023
|
+
syncedL2Slot,
|
|
969
1024
|
invalidCheckpoint: pendingChainValidationStatus.checkpoint,
|
|
970
1025
|
secondsBeforeInvalidatingBlockAsCommitteeMember,
|
|
971
1026
|
secondsBeforeInvalidatingBlockAsNonCommitteeMember,
|
|
@@ -1,8 +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
4
|
export declare class SequencerTimetable {
|
|
4
5
|
private readonly metrics?;
|
|
5
|
-
private readonly log
|
|
6
|
+
private readonly log?;
|
|
6
7
|
/**
|
|
7
8
|
* How late into the slot can we be to start working. Computed as the total time needed for assembling and publishing a block,
|
|
8
9
|
* assuming an execution time equal to `minExecutionTime`, subtracted from the slot duration. This means that, if the proposer
|
|
@@ -56,7 +57,7 @@ export declare class SequencerTimetable {
|
|
|
56
57
|
p2pPropagationTime?: number;
|
|
57
58
|
blockDurationMs?: number;
|
|
58
59
|
enforce: boolean;
|
|
59
|
-
}, metrics?: SequencerMetrics | undefined, log?:
|
|
60
|
+
}, metrics?: SequencerMetrics | undefined, log?: Logger | undefined);
|
|
60
61
|
getMaxAllowedTime(state: Extract<SequencerState, SequencerState.STOPPED | SequencerState.IDLE | SequencerState.SYNCHRONIZING>): undefined;
|
|
61
62
|
getMaxAllowedTime(state: Exclude<SequencerState, SequencerState.STOPPED | SequencerState.IDLE | SequencerState.SYNCHRONIZING>): number;
|
|
62
63
|
getMaxAllowedTime(state: SequencerState): number | undefined;
|
|
@@ -84,4 +85,4 @@ export declare class SequencerTimetable {
|
|
|
84
85
|
isLastBlock: boolean;
|
|
85
86
|
};
|
|
86
87
|
}
|
|
87
|
-
//# 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,4 +1,3 @@
|
|
|
1
|
-
import { createLogger } from '@aztec/aztec.js/log';
|
|
2
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';
|
|
@@ -38,7 +37,7 @@ export class SequencerTimetable {
|
|
|
38
37
|
/** Whether assertTimeLeft will throw if not enough time. */ enforce;
|
|
39
38
|
/** Duration per block when building multiple blocks per slot (undefined = single block per slot) */ blockDuration;
|
|
40
39
|
/** Maximum number of blocks that can be built in this slot configuration */ maxNumberOfBlocks;
|
|
41
|
-
constructor(opts, metrics, log
|
|
40
|
+
constructor(opts, metrics, log){
|
|
42
41
|
this.metrics = metrics;
|
|
43
42
|
this.log = log;
|
|
44
43
|
this.minExecutionTime = MIN_EXECUTION_TIME;
|
|
@@ -81,7 +80,7 @@ export class SequencerTimetable {
|
|
|
81
80
|
this.checkpointFinalizationTime;
|
|
82
81
|
const initializeDeadline = this.aztecSlotDuration - minWorkToDo;
|
|
83
82
|
this.initializeDeadline = initializeDeadline;
|
|
84
|
-
this.log
|
|
83
|
+
this.log?.info(`Sequencer timetable initialized with ${this.maxNumberOfBlocks} blocks per slot (${this.enforce ? 'enforced' : 'not enforced'})`, {
|
|
85
84
|
ethereumSlotDuration: this.ethereumSlotDuration,
|
|
86
85
|
aztecSlotDuration: this.aztecSlotDuration,
|
|
87
86
|
l1PublishingTime: this.l1PublishingTime,
|
|
@@ -138,7 +137,7 @@ export class SequencerTimetable {
|
|
|
138
137
|
throw new SequencerTooSlowError(newState, maxAllowedTime, secondsIntoSlot);
|
|
139
138
|
}
|
|
140
139
|
this.metrics?.recordStateTransitionBufferMs(Math.floor(bufferSeconds * 1000), newState);
|
|
141
|
-
this.log
|
|
140
|
+
this.log?.trace(`Enough time to transition to ${newState}`, {
|
|
142
141
|
maxAllowedTime,
|
|
143
142
|
secondsIntoSlot
|
|
144
143
|
});
|
|
@@ -172,7 +171,7 @@ export class SequencerTimetable {
|
|
|
172
171
|
const available = (maxAllowed - secondsIntoSlot) / 2; // Split remaining time: half for execution, half for re-execution
|
|
173
172
|
const canStart = available >= this.minExecutionTime;
|
|
174
173
|
const deadline = secondsIntoSlot + available;
|
|
175
|
-
this.log
|
|
174
|
+
this.log?.verbose(`${canStart ? 'Can' : 'Cannot'} start single-block checkpoint at ${secondsIntoSlot}s into slot`, {
|
|
176
175
|
secondsIntoSlot,
|
|
177
176
|
maxAllowed,
|
|
178
177
|
available,
|
|
@@ -194,7 +193,7 @@ export class SequencerTimetable {
|
|
|
194
193
|
if (timeUntilDeadline >= this.minExecutionTime) {
|
|
195
194
|
// Found an available sub-slot! Is this the last one?
|
|
196
195
|
const isLastBlock = subSlot === this.maxNumberOfBlocks;
|
|
197
|
-
this.log
|
|
196
|
+
this.log?.verbose(`Can start ${isLastBlock ? 'last block' : 'block'} in sub-slot ${subSlot} with deadline ${deadline}s`, {
|
|
198
197
|
secondsIntoSlot,
|
|
199
198
|
deadline,
|
|
200
199
|
timeUntilDeadline,
|
|
@@ -209,7 +208,7 @@ export class SequencerTimetable {
|
|
|
209
208
|
}
|
|
210
209
|
}
|
|
211
210
|
// No sub-slots available with enough time
|
|
212
|
-
this.log
|
|
211
|
+
this.log?.verbose(`No time left to start any more blocks`, {
|
|
213
212
|
secondsIntoSlot,
|
|
214
213
|
maxBlocks: this.maxNumberOfBlocks,
|
|
215
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"}
|
|
@@ -2,7 +2,7 @@ import { type BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-ty
|
|
|
2
2
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
3
3
|
import { L2Block } from '@aztec/stdlib/block';
|
|
4
4
|
import { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
5
|
-
import type { FullNodeBlockBuilderConfig, ICheckpointBlockBuilder, ICheckpointsBuilder, MerkleTreeWriteOperations
|
|
5
|
+
import type { BlockBuilderOptions, FullNodeBlockBuilderConfig, ICheckpointBlockBuilder, ICheckpointsBuilder, MerkleTreeWriteOperations } from '@aztec/stdlib/interfaces/server';
|
|
6
6
|
import type { CheckpointGlobalVariables, Tx } from '@aztec/stdlib/tx';
|
|
7
7
|
import type { BuildBlockInCheckpointResult } from '@aztec/validator-client';
|
|
8
8
|
/**
|
|
@@ -22,7 +22,7 @@ export declare class MockCheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
22
22
|
buildBlockCalls: Array<{
|
|
23
23
|
blockNumber: BlockNumber;
|
|
24
24
|
timestamp: bigint;
|
|
25
|
-
opts:
|
|
25
|
+
opts: BlockBuilderOptions;
|
|
26
26
|
}>;
|
|
27
27
|
/** Track all consumed transaction hashes across buildBlock calls */
|
|
28
28
|
consumedTxHashes: Set<string>;
|
|
@@ -39,14 +39,12 @@ export declare class MockCheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
39
39
|
*/
|
|
40
40
|
setBlockProvider(provider: () => L2Block): this;
|
|
41
41
|
getConstantData(): CheckpointGlobalVariables;
|
|
42
|
-
buildBlock(pendingTxs: Iterable<Tx> | AsyncIterable<Tx>, blockNumber: BlockNumber, timestamp: bigint, opts:
|
|
42
|
+
buildBlock(pendingTxs: Iterable<Tx> | AsyncIterable<Tx>, blockNumber: BlockNumber, timestamp: bigint, opts: BlockBuilderOptions): Promise<BuildBlockInCheckpointResult>;
|
|
43
43
|
completeCheckpoint(): Promise<Checkpoint>;
|
|
44
44
|
getCheckpoint(): Promise<Checkpoint>;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
*/
|
|
49
|
-
private createCheckpointHeader;
|
|
45
|
+
private buildCheckpoint;
|
|
46
|
+
/** Resets per-checkpoint state (built blocks, consumed txs) while preserving config (blockProvider, seeded blocks). */
|
|
47
|
+
resetCheckpointState(): void;
|
|
50
48
|
/** Reset for reuse in another test */
|
|
51
49
|
reset(): void;
|
|
52
50
|
}
|
|
@@ -94,4 +92,4 @@ export declare class MockCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
94
92
|
/** Reset for reuse in another test */
|
|
95
93
|
reset(): void;
|
|
96
94
|
}
|
|
97
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
95
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9ja19jaGVja3BvaW50X2J1aWxkZXIuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0L21vY2tfY2hlY2twb2ludF9idWlsZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLFdBQVcsRUFBRSxnQkFBZ0IsRUFBeUIsTUFBTSxpQ0FBaUMsQ0FBQztBQUM1RyxPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFcEQsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUN0RCxPQUFPLEtBQUssRUFDVixtQkFBbUIsRUFDbkIsMEJBQTBCLEVBQzFCLHVCQUF1QixFQUN2QixtQkFBbUIsRUFDbkIseUJBQXlCLEVBQzFCLE1BQU0saUNBQWlDLENBQUM7QUFHekMsT0FBTyxLQUFLLEVBQUUseUJBQXlCLEVBQUUsRUFBRSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDdEUsT0FBTyxLQUFLLEVBQUUsNEJBQTRCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUU1RTs7O0dBR0c7QUFDSCxxQkFBYSxxQkFBc0IsWUFBVyx1QkFBdUI7SUF3QmpFLE9BQU8sQ0FBQyxRQUFRLENBQUMsU0FBUztJQUMxQixPQUFPLENBQUMsUUFBUSxDQUFDLGdCQUFnQjtJQXhCbkMsT0FBTyxDQUFDLE1BQU0sQ0FBaUI7SUFDL0IsT0FBTyxDQUFDLFdBQVcsQ0FBaUI7SUFDcEMsT0FBTyxDQUFDLGVBQWUsQ0FBYztJQUNyQyxPQUFPLENBQUMsVUFBVSxDQUFLO0lBRXZCLHFGQUFxRjtJQUNyRixPQUFPLENBQUMsYUFBYSxDQUEwQztJQUUvRCxpQ0FBaUM7SUFDMUIsZUFBZSxFQUFFLEtBQUssQ0FBQztRQUM1QixXQUFXLEVBQUUsV0FBVyxDQUFDO1FBQ3pCLFNBQVMsRUFBRSxNQUFNLENBQUM7UUFDbEIsSUFBSSxFQUFFLG1CQUFtQixDQUFDO0tBQzNCLENBQUMsQ0FBTTtJQUNSLG9FQUFvRTtJQUM3RCxnQkFBZ0IsRUFBRSxHQUFHLENBQUMsTUFBTSxDQUFDLENBQWE7SUFDMUMsd0JBQXdCLFVBQVM7SUFDakMsbUJBQW1CLFVBQVM7SUFFbkMsNERBQTREO0lBQ3JELFlBQVksRUFBRSxLQUFLLEdBQUcsU0FBUyxDQUFhO0lBRW5ELFlBQ21CLFNBQVMsRUFBRSx5QkFBeUIsRUFDcEMsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQ2pEO0lBRUosNEVBQTRFO0lBQzVFLFVBQVUsQ0FBQyxNQUFNLEVBQUUsT0FBTyxFQUFFLEVBQUUsZUFBZSxDQUFDLEVBQUUsRUFBRSxFQUFFLEVBQUUsR0FBRyxJQUFJLENBTTVEO0lBRUQ7OztPQUdHO0lBQ0gsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FJOUM7SUFFRCxlQUFlLElBQUkseUJBQXlCLENBRTNDO0lBRUssVUFBVSxDQUNkLFVBQVUsRUFBRSxRQUFRLENBQUMsRUFBRSxDQUFDLEdBQUcsYUFBYSxDQUFDLEVBQUUsQ0FBQyxFQUM1QyxXQUFXLEVBQUUsV0FBVyxFQUN4QixTQUFTLEVBQUUsTUFBTSxFQUNqQixJQUFJLEVBQUUsbUJBQW1CLEdBQ3hCLE9BQU8sQ0FBQyw0QkFBNEIsQ0FBQyxDQTZDdkM7SUFFRCxrQkFBa0IsSUFBSSxPQUFPLENBQUMsVUFBVSxDQUFDLENBSXhDO0lBRUQsYUFBYSxJQUFJLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FPbkM7WUFHYSxlQUFlO0lBaUM3Qix1SEFBdUg7SUFDdkgsb0JBQW9CLElBQUksSUFBSSxDQU0zQjtJQUVELHNDQUFzQztJQUN0QyxLQUFLLElBQUksSUFBSSxDQU9aO0NBQ0Y7QUFFRDs7OztHQUlHO0FBQ0gscUJBQWEsc0JBQXVCLFlBQVcsbUJBQW1CO0lBQ2hFLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBb0M7SUFFN0QsaUNBQWlDO0lBQzFCLG9CQUFvQixFQUFFLEtBQUssQ0FBQztRQUNqQyxnQkFBZ0IsRUFBRSxnQkFBZ0IsQ0FBQztRQUNuQyxTQUFTLEVBQUUseUJBQXlCLENBQUM7UUFDckMsY0FBYyxFQUFFLEVBQUUsRUFBRSxDQUFDO1FBQ3JCLDJCQUEyQixFQUFFLEVBQUUsRUFBRSxDQUFDO1FBQ2xDLHFCQUFxQixFQUFFLE1BQU0sQ0FBQztLQUMvQixDQUFDLENBQU07SUFDRCxtQkFBbUIsRUFBRSxLQUFLLENBQUM7UUFDaEMsZ0JBQWdCLEVBQUUsZ0JBQWdCLENBQUM7UUFDbkMsU0FBUyxFQUFFLHlCQUF5QixDQUFDO1FBQ3JDLGNBQWMsRUFBRSxFQUFFLEVBQUUsQ0FBQztRQUNyQiwyQkFBMkIsRUFBRSxFQUFFLEVBQUUsQ0FBQztRQUNsQyxjQUFjLEVBQUUsT0FBTyxFQUFFLENBQUM7UUFDMUIscUJBQXFCLEVBQUUsTUFBTSxDQUFDO0tBQy9CLENBQUMsQ0FBTTtJQUNELGlCQUFpQixFQUFFLEtBQUssQ0FBQyxPQUFPLENBQUMsMEJBQTBCLENBQUMsQ0FBQyxDQUFNO0lBRTFFOzs7T0FHRztJQUNILG9CQUFvQixDQUFDLE9BQU8sRUFBRSxxQkFBcUIsR0FBRyxJQUFJLENBR3pEO0lBRUQ7OztPQUdHO0lBQ0gsdUJBQXVCLENBQ3JCLFNBQVMsRUFBRSx5QkFBeUIsRUFDcEMsZ0JBQWdCLEVBQUUsZ0JBQWdCLEdBQ2pDLHFCQUFxQixDQUd2QjtJQUVELDBEQUEwRDtJQUMxRCxvQkFBb0IsSUFBSSxxQkFBcUIsR0FBRyxTQUFTLENBRXhEO0lBRUQsU0FBUyxJQUFJLDBCQUEwQixDQVF0QztJQUVELFlBQVksQ0FBQyxNQUFNLEVBQUUsT0FBTyxDQUFDLDBCQUEwQixDQUFDLEdBQUcsSUFBSSxDQUU5RDtJQUVELGVBQWUsQ0FDYixnQkFBZ0IsRUFBRSxnQkFBZ0IsRUFDbEMsU0FBUyxFQUFFLHlCQUF5QixFQUNwQyxxQkFBcUIsRUFBRSxNQUFNLEVBQzdCLGNBQWMsRUFBRSxFQUFFLEVBQUUsRUFDcEIsMkJBQTJCLEVBQUUsRUFBRSxFQUFFLEVBQ2pDLEtBQUssRUFBRSx5QkFBeUIsR0FDL0IsT0FBTyxDQUFDLHVCQUF1QixDQUFDLENBaUJsQztJQUVELGNBQWMsQ0FDWixnQkFBZ0IsRUFBRSxnQkFBZ0IsRUFDbEMsU0FBUyxFQUFFLHlCQUF5QixFQUNwQyxxQkFBcUIsRUFBRSxNQUFNLEVBQzdCLGNBQWMsRUFBRSxFQUFFLEVBQUUsRUFDcEIsMkJBQTJCLEVBQUUsRUFBRSxFQUFFLEVBQ2pDLEtBQUssRUFBRSx5QkFBeUIsRUFDaEMsY0FBYyxHQUFFLE9BQU8sRUFBTyxHQUM3QixPQUFPLENBQUMsdUJBQXVCLENBQUMsQ0FnQmxDO0lBRUQsT0FBTyxDQUFDLFlBQVksRUFBRSxXQUFXLEdBQUcsT0FBTyxDQUFDLHlCQUF5QixDQUFDLENBRXJFO0lBRUQsc0NBQXNDO0lBQ3RDLEtBQUssSUFBSSxJQUFJLENBS1o7Q0FDRiJ9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock_checkpoint_builder.d.ts","sourceRoot":"","sources":["../../src/test/mock_checkpoint_builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"mock_checkpoint_builder.d.ts","sourceRoot":"","sources":["../../src/test/mock_checkpoint_builder.ts"],"names":[],"mappings":"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"}
|