@aztec/sequencer-client 0.0.1-commit.e0f15ab9b → 0.0.1-commit.e304674f1
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 +1 -1
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +0 -4
- package/dest/global_variable_builder/global_builder.d.ts +3 -3
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +7 -4
- package/dest/publisher/sequencer-publisher-factory.d.ts +1 -3
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +0 -1
- package/dest/publisher/sequencer-publisher.d.ts +52 -31
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +106 -87
- package/dest/sequencer/checkpoint_proposal_job.d.ts +31 -10
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +179 -108
- 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/sequencer.d.ts +14 -4
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +67 -18
- package/dest/sequencer/timetable.d.ts +4 -1
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +15 -5
- package/package.json +27 -27
- package/src/client/sequencer-client.ts +0 -7
- package/src/global_variable_builder/global_builder.ts +15 -3
- package/src/publisher/sequencer-publisher-factory.ts +0 -3
- package/src/publisher/sequencer-publisher.ts +174 -124
- package/src/sequencer/README.md +81 -12
- package/src/sequencer/checkpoint_proposal_job.ts +215 -117
- package/src/sequencer/checkpoint_voter.ts +1 -12
- package/src/sequencer/sequencer.ts +97 -20
- package/src/sequencer/timetable.ts +19 -8
|
@@ -436,9 +436,11 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
|
|
|
436
436
|
return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass);
|
|
437
437
|
}
|
|
438
438
|
var _dec, _dec1, _dec2, _dec3, _dec4, _dec5, _dec6, _dec7, _initProto;
|
|
439
|
-
import {
|
|
439
|
+
import { RollupContract } from '@aztec/ethereum/contracts';
|
|
440
|
+
import { BlockNumber, CheckpointNumber, IndexWithinCheckpoint } from '@aztec/foundation/branded-types';
|
|
440
441
|
import { randomInt } from '@aztec/foundation/crypto/random';
|
|
441
442
|
import { flipSignature, generateRecoverableSignature, generateUnrecoverableSignature } from '@aztec/foundation/crypto/secp256k1-signer';
|
|
443
|
+
import { Signature } from '@aztec/foundation/eth-signature';
|
|
442
444
|
import { filter } from '@aztec/foundation/iterator';
|
|
443
445
|
import { createLogger } from '@aztec/foundation/log';
|
|
444
446
|
import { sleep, sleepUntil } from '@aztec/foundation/sleep';
|
|
@@ -446,7 +448,7 @@ import { Timer } from '@aztec/foundation/timer';
|
|
|
446
448
|
import { isErrorClass, unfreeze } from '@aztec/foundation/types';
|
|
447
449
|
import { CommitteeAttestationsAndSigners, MaliciousCommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
|
|
448
450
|
import { validateCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
449
|
-
import { getSlotStartBuildTimestamp, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
451
|
+
import { computeQuorum, getSlotStartBuildTimestamp, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
450
452
|
import { Gas } from '@aztec/stdlib/gas';
|
|
451
453
|
import { InsufficientValidTxsError } from '@aztec/stdlib/interfaces/server';
|
|
452
454
|
import { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
|
|
@@ -473,7 +475,6 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
473
475
|
*/ export class CheckpointProposalJob {
|
|
474
476
|
slotNow;
|
|
475
477
|
targetSlot;
|
|
476
|
-
epochNow;
|
|
477
478
|
targetEpoch;
|
|
478
479
|
checkpointNumber;
|
|
479
480
|
syncedToBlockNumber;
|
|
@@ -499,6 +500,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
499
500
|
eventEmitter;
|
|
500
501
|
setStateFn;
|
|
501
502
|
tracer;
|
|
503
|
+
proposedCheckpointData;
|
|
502
504
|
static{
|
|
503
505
|
({ e: [_initProto] } = _apply_decs_2203_r(this, [
|
|
504
506
|
[
|
|
@@ -544,11 +546,12 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
544
546
|
], []));
|
|
545
547
|
}
|
|
546
548
|
log;
|
|
547
|
-
|
|
548
|
-
|
|
549
|
+
/** Tracks the fire-and-forget L1 submission promise so it can be awaited during shutdown. */ pendingL1Submission;
|
|
550
|
+
/** Fee header override computed during proposeCheckpoint, reused in enqueueCheckpointForSubmission. */ computedForceProposedFeeHeader;
|
|
551
|
+
constructor(slotNow, targetSlot, targetEpoch, checkpointNumber, syncedToBlockNumber, // TODO(palla/mbps): Can we remove the proposer in favor of attestorAddress? Need to check fisherman-node flows.
|
|
552
|
+
proposer, publisher, attestorAddress, invalidateCheckpoint, validatorClient, globalsBuilder, p2pClient, worldState, l1ToL2MessageSource, l2BlockSource, checkpointsBuilder, blockSink, l1Constants, config, timetable, slasherClient, epochCache, dateProvider, metrics, eventEmitter, setStateFn, tracer, bindings, proposedCheckpointData){
|
|
549
553
|
this.slotNow = slotNow;
|
|
550
554
|
this.targetSlot = targetSlot;
|
|
551
|
-
this.epochNow = epochNow;
|
|
552
555
|
this.targetEpoch = targetEpoch;
|
|
553
556
|
this.checkpointNumber = checkpointNumber;
|
|
554
557
|
this.syncedToBlockNumber = syncedToBlockNumber;
|
|
@@ -574,28 +577,31 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
574
577
|
this.eventEmitter = eventEmitter;
|
|
575
578
|
this.setStateFn = setStateFn;
|
|
576
579
|
this.tracer = tracer;
|
|
580
|
+
this.proposedCheckpointData = proposedCheckpointData;
|
|
577
581
|
_initProto(this);
|
|
578
582
|
this.log = createLogger('sequencer:checkpoint-proposal', {
|
|
579
583
|
...bindings,
|
|
580
584
|
instanceId: `slot-${this.slotNow}`
|
|
581
585
|
});
|
|
582
586
|
}
|
|
583
|
-
/**
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
/** The wall-clock epoch. */ get epoch() {
|
|
587
|
-
return this.epochNow;
|
|
587
|
+
/** Awaits the pending L1 submission if one is in progress. Call during shutdown. */ async awaitPendingSubmission() {
|
|
588
|
+
this.log.info('Awaiting pending L1 payload submission');
|
|
589
|
+
await this.pendingL1Submission;
|
|
588
590
|
}
|
|
589
591
|
/**
|
|
590
592
|
* Executes the checkpoint proposal job.
|
|
591
|
-
*
|
|
593
|
+
* Builds blocks, collects attestations, enqueues requests, and schedules L1 submission as a
|
|
594
|
+
* background task so the work loop can return to IDLE immediately.
|
|
595
|
+
* Returns the built checkpoint if successful, undefined otherwise.
|
|
592
596
|
*/ async execute() {
|
|
593
597
|
// Enqueue governance and slashing votes (returns promises that will be awaited later)
|
|
594
598
|
// In fisherman mode, we simulate slashing but don't actually publish to L1
|
|
595
599
|
// These are constant for the whole slot, so we only enqueue them once
|
|
596
600
|
const votesPromises = new CheckpointVoter(this.targetSlot, this.publisher, this.attestorAddress, this.validatorClient, this.slasherClient, this.l1Constants, this.config, this.metrics, this.log).enqueueVotes();
|
|
597
|
-
// Build and propose the checkpoint.
|
|
598
|
-
|
|
601
|
+
// Build and propose the checkpoint. Builds blocks, broadcasts, collects attestations, and signs.
|
|
602
|
+
// Does NOT enqueue to L1 yet — that happens after the pipeline sleep.
|
|
603
|
+
const proposalResult = await this.proposeCheckpoint();
|
|
604
|
+
const checkpoint = proposalResult?.checkpoint;
|
|
599
605
|
// Wait until the voting promises have resolved, so all requests are enqueued (not sent)
|
|
600
606
|
await Promise.all(votesPromises);
|
|
601
607
|
if (checkpoint) {
|
|
@@ -606,42 +612,71 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
606
612
|
await this.handleCheckpointEndAsFisherman(checkpoint);
|
|
607
613
|
return;
|
|
608
614
|
}
|
|
609
|
-
//
|
|
610
|
-
if (
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
});
|
|
617
|
-
await sleepUntil(new Date(Number(submissionSlotTimestamp) * 1000), this.dateProvider.nowAsDate());
|
|
618
|
-
// After waking, verify the parent checkpoint wasn't pruned during the sleep.
|
|
619
|
-
// We check L1's pending tip directly instead of canProposeAt, which also validates the proposer
|
|
620
|
-
// identity and would fail because the timestamp resolves to a different slot's proposer.
|
|
621
|
-
const l1Tips = await this.publisher.rollupContract.getTips();
|
|
622
|
-
if (l1Tips.pending < this.checkpointNumber - 1) {
|
|
623
|
-
this.log.warn(`Parent checkpoint was pruned during pipelining sleep (L1 pending=${l1Tips.pending}, expected>=${this.checkpointNumber - 1}), skipping L1 submission for checkpoint ${this.checkpointNumber}`);
|
|
624
|
-
return undefined;
|
|
615
|
+
// Enqueue the checkpoint for L1 submission
|
|
616
|
+
if (proposalResult) {
|
|
617
|
+
try {
|
|
618
|
+
await this.enqueueCheckpointForSubmission(proposalResult);
|
|
619
|
+
} catch (err) {
|
|
620
|
+
this.log.error(`Failed to enqueue checkpoint for L1 submission at slot ${this.targetSlot}`, err);
|
|
621
|
+
// Continue to sendRequestsAt so votes are still sent
|
|
625
622
|
}
|
|
626
623
|
}
|
|
627
|
-
//
|
|
628
|
-
const
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
624
|
+
// Compute the earliest time to submit: pipeline slot start when pipelining, now otherwise.
|
|
625
|
+
const submitAfter = this.epochCache.isProposerPipeliningEnabled() ? new Date(Number(getTimestampForSlot(this.targetSlot, this.l1Constants)) * 1000) : new Date(this.dateProvider.now());
|
|
626
|
+
// Schedule L1 submission in the background so the work loop returns immediately.
|
|
627
|
+
// The publisher will sleep until submitAfter, then send the bundled requests.
|
|
628
|
+
// The promise is stored so it can be awaited during shutdown.
|
|
629
|
+
this.pendingL1Submission = this.publisher.sendRequestsAt(submitAfter).then(async (l1Response)=>{
|
|
630
|
+
const proposedAction = l1Response?.successfulActions.find((a)=>a === 'propose');
|
|
631
|
+
if (proposedAction) {
|
|
632
|
+
this.eventEmitter.emit('checkpoint-published', {
|
|
633
|
+
checkpoint: this.checkpointNumber,
|
|
634
|
+
slot: this.targetSlot
|
|
635
|
+
});
|
|
636
|
+
const coinbase = checkpoint?.header.coinbase;
|
|
637
|
+
await this.metrics.incFilledSlot(this.publisher.getSenderAddress().toString(), coinbase);
|
|
638
|
+
} else if (checkpoint) {
|
|
639
|
+
this.eventEmitter.emit('checkpoint-publish-failed', {
|
|
640
|
+
...l1Response,
|
|
641
|
+
slot: this.targetSlot
|
|
642
|
+
});
|
|
643
|
+
if (this.epochCache.isProposerPipeliningEnabled()) {
|
|
644
|
+
this.metrics.recordPipelineDiscard();
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}).catch((err)=>{
|
|
648
|
+
this.log.error(`Background L1 submission failed for slot ${this.targetSlot}`, err);
|
|
649
|
+
if (checkpoint) {
|
|
650
|
+
this.eventEmitter.emit('checkpoint-publish-failed', {
|
|
651
|
+
slot: this.targetSlot
|
|
652
|
+
});
|
|
653
|
+
if (this.epochCache.isProposerPipeliningEnabled()) {
|
|
654
|
+
this.metrics.recordPipelineDiscard();
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
// Return the built checkpoint immediately — the work loop is now unblocked
|
|
659
|
+
return checkpoint;
|
|
660
|
+
}
|
|
661
|
+
/** Enqueues the checkpoint for L1 submission. Called after pipeline sleep in execute(). */ async enqueueCheckpointForSubmission(result) {
|
|
662
|
+
const { checkpoint, attestations, attestationsSignature } = result;
|
|
663
|
+
this.setStateFn(SequencerState.PUBLISHING_CHECKPOINT, this.targetSlot);
|
|
664
|
+
const aztecSlotDuration = this.l1Constants.slotDuration;
|
|
665
|
+
const submissionSlotStart = Number(getTimestampForSlot(this.targetSlot, this.l1Constants));
|
|
666
|
+
const txTimeoutAt = new Date((submissionSlotStart + aztecSlotDuration) * 1000);
|
|
667
|
+
// If we have been configured to potentially skip publishing checkpoint then roll the dice here
|
|
668
|
+
if (this.config.skipPublishingCheckpointsPercent !== undefined && this.config.skipPublishingCheckpointsPercent > 0) {
|
|
669
|
+
const roll = Math.max(0, randomInt(100));
|
|
670
|
+
if (roll < this.config.skipPublishingCheckpointsPercent) {
|
|
671
|
+
this.log.warn(`Skipping publishing proposal for checkpoint ${checkpoint.number}. Configured percentage: ${this.config.skipPublishingCheckpointsPercent}, generated value: ${roll}`);
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
644
674
|
}
|
|
675
|
+
await this.publisher.enqueueProposeCheckpoint(checkpoint, attestations, attestationsSignature, {
|
|
676
|
+
txTimeoutAt,
|
|
677
|
+
forcePendingCheckpointNumber: this.invalidateCheckpoint?.forcePendingCheckpointNumber,
|
|
678
|
+
forceProposedFeeHeader: this.computedForceProposedFeeHeader
|
|
679
|
+
});
|
|
645
680
|
}
|
|
646
681
|
async proposeCheckpoint() {
|
|
647
682
|
try {
|
|
@@ -657,7 +692,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
657
692
|
// Start the checkpoint
|
|
658
693
|
this.setStateFn(SequencerState.INITIALIZING_CHECKPOINT, this.targetSlot);
|
|
659
694
|
this.log.info(`Starting checkpoint proposal`, {
|
|
660
|
-
buildSlot: this.
|
|
695
|
+
buildSlot: this.slotNow,
|
|
661
696
|
submissionSlot: this.targetSlot,
|
|
662
697
|
pipelining: this.epochCache.isProposerPipeliningEnabled(),
|
|
663
698
|
proposer: this.proposer?.toString(),
|
|
@@ -668,8 +703,19 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
668
703
|
if (this.invalidateCheckpoint && !this.config.skipInvalidateBlockAsProposer) {
|
|
669
704
|
this.publisher.enqueueInvalidateCheckpoint(this.invalidateCheckpoint);
|
|
670
705
|
}
|
|
671
|
-
// Create checkpoint builder for the slot
|
|
672
|
-
|
|
706
|
+
// Create checkpoint builder for the slot.
|
|
707
|
+
// When pipelining, force the proposed checkpoint number and fee header to our parent so the
|
|
708
|
+
// fee computation sees the same chain tip that L1 will see once the previous pipelined checkpoint lands.
|
|
709
|
+
const isPipelining = this.epochCache.isProposerPipeliningEnabled();
|
|
710
|
+
const parentCheckpointNumber = isPipelining ? CheckpointNumber(this.checkpointNumber - 1) : undefined;
|
|
711
|
+
// Compute the parent's fee header override when pipelining
|
|
712
|
+
if (isPipelining && this.proposedCheckpointData) {
|
|
713
|
+
this.computedForceProposedFeeHeader = await this.computeForceProposedFeeHeader(parentCheckpointNumber);
|
|
714
|
+
}
|
|
715
|
+
const checkpointGlobalVariables = await this.globalsBuilder.buildCheckpointGlobalVariables(coinbase, feeRecipient, this.targetSlot, {
|
|
716
|
+
forcePendingCheckpointNumber: parentCheckpointNumber,
|
|
717
|
+
forceProposedFeeHeader: this.computedForceProposedFeeHeader
|
|
718
|
+
});
|
|
673
719
|
// Collect L1 to L2 messages for the checkpoint and compute their hash
|
|
674
720
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(this.checkpointNumber);
|
|
675
721
|
const inHash = computeInHashFromL1ToL2Messages(l1ToL2Messages);
|
|
@@ -741,7 +787,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
741
787
|
maxTxsPerCheckpoint: this.config.maxTxsPerCheckpoint
|
|
742
788
|
});
|
|
743
789
|
} catch (err) {
|
|
744
|
-
this.log.error(`Built an invalid checkpoint at slot ${this.
|
|
790
|
+
this.log.error(`Built an invalid checkpoint at slot ${this.slotNow} (skipping proposal)`, err, {
|
|
745
791
|
checkpoint: checkpoint.header.toInspect()
|
|
746
792
|
});
|
|
747
793
|
return undefined;
|
|
@@ -756,16 +802,14 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
756
802
|
blocksBuilt: blocksInCheckpoint.length
|
|
757
803
|
});
|
|
758
804
|
this.metrics.recordCheckpointSuccess();
|
|
759
|
-
return
|
|
805
|
+
return {
|
|
806
|
+
checkpoint,
|
|
807
|
+
attestations: CommitteeAttestationsAndSigners.empty(),
|
|
808
|
+
attestationsSignature: Signature.empty()
|
|
809
|
+
};
|
|
760
810
|
}
|
|
761
|
-
// Include the block pending broadcast in the checkpoint proposal if any
|
|
762
|
-
const lastBlock = blockPendingBroadcast && {
|
|
763
|
-
blockHeader: blockPendingBroadcast.block.header,
|
|
764
|
-
indexWithinCheckpoint: blockPendingBroadcast.block.indexWithinCheckpoint,
|
|
765
|
-
txs: blockPendingBroadcast.txs
|
|
766
|
-
};
|
|
767
811
|
// Create the checkpoint proposal and broadcast it
|
|
768
|
-
const proposal = await this.validatorClient.createCheckpointProposal(checkpoint.header, checkpoint.archive.root, feeAssetPriceModifier,
|
|
812
|
+
const proposal = await this.validatorClient.createCheckpointProposal(checkpoint.header, checkpoint.archive.root, feeAssetPriceModifier, blockPendingBroadcast, this.proposer, checkpointProposalOptions);
|
|
769
813
|
const blockProposedAt = this.dateProvider.now();
|
|
770
814
|
await this.p2pClient.broadcastCheckpointProposal(proposal);
|
|
771
815
|
this.setStateFn(SequencerState.COLLECTING_ATTESTATIONS, this.targetSlot);
|
|
@@ -785,24 +829,12 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
785
829
|
}
|
|
786
830
|
throw err;
|
|
787
831
|
}
|
|
788
|
-
//
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
if (this.config.skipPublishingCheckpointsPercent !== undefined && this.config.skipPublishingCheckpointsPercent > 0) {
|
|
795
|
-
const result = Math.max(0, randomInt(100));
|
|
796
|
-
if (result < this.config.skipPublishingCheckpointsPercent) {
|
|
797
|
-
this.log.warn(`Skipping publishing proposal for checkpoint ${checkpoint.number}. Configured percentage: ${this.config.skipPublishingCheckpointsPercent}, generated value: ${result}`);
|
|
798
|
-
return checkpoint;
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
await this.publisher.enqueueProposeCheckpoint(checkpoint, attestations, attestationsSignature, {
|
|
802
|
-
txTimeoutAt,
|
|
803
|
-
forcePendingCheckpointNumber: this.invalidateCheckpoint?.forcePendingCheckpointNumber
|
|
804
|
-
});
|
|
805
|
-
return checkpoint;
|
|
832
|
+
// Return the result for the caller to enqueue after the pipeline sleep
|
|
833
|
+
return {
|
|
834
|
+
checkpoint,
|
|
835
|
+
attestations,
|
|
836
|
+
attestationsSignature
|
|
837
|
+
};
|
|
806
838
|
} catch (e) {
|
|
807
839
|
env.error = e;
|
|
808
840
|
env.hasError = true;
|
|
@@ -815,7 +847,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
815
847
|
// swallow this error. It's already been logged by a function deeper in the stack
|
|
816
848
|
return undefined;
|
|
817
849
|
}
|
|
818
|
-
this.log.error(`Error building checkpoint at slot ${this.
|
|
850
|
+
this.log.error(`Error building checkpoint at slot ${this.targetSlot}`, err);
|
|
819
851
|
return undefined;
|
|
820
852
|
}
|
|
821
853
|
}
|
|
@@ -852,17 +884,19 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
852
884
|
indexWithinCheckpoint,
|
|
853
885
|
txHashesAlreadyIncluded
|
|
854
886
|
});
|
|
855
|
-
//
|
|
856
|
-
if (
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
887
|
+
// If we failed to build the block due to insufficient txs, we try again if there is still time left in the slot
|
|
888
|
+
if ('failure' in buildResult) {
|
|
889
|
+
// If this was the last subslot, or we're running with a single block per slot, we're done
|
|
890
|
+
if (timingInfo.isLastBlock || timingInfo.deadline === undefined) {
|
|
891
|
+
break;
|
|
892
|
+
}
|
|
893
|
+
// Otherwise, if there is still time for more blocks, we wait until the next subslot and try again
|
|
860
894
|
await this.waitUntilNextSubslot(timingInfo.deadline);
|
|
861
895
|
continue;
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
896
|
+
}
|
|
897
|
+
// If there was an error building the block, we just exit the loop and give up the rest of the slot.
|
|
898
|
+
// We don't want to risk building more blocks if something went wrong.
|
|
899
|
+
if ('error' in buildResult) {
|
|
866
900
|
if (!(buildResult.error instanceof SequencerInterruptedError)) {
|
|
867
901
|
this.log.warn(`Halting block building for slot ${this.targetSlot}`, {
|
|
868
902
|
slot: this.targetSlot,
|
|
@@ -875,30 +909,23 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
875
909
|
const { block, usedTxs } = buildResult;
|
|
876
910
|
blocksInCheckpoint.push(block);
|
|
877
911
|
usedTxs.forEach((tx)=>txHashesAlreadyIncluded.add(tx.txHash.toString()));
|
|
878
|
-
//
|
|
879
|
-
|
|
912
|
+
// Sign the block proposal. This will throw if HA signing fails.
|
|
913
|
+
const proposal = await this.createBlockProposal(block, inHash, usedTxs, blockProposalOptions);
|
|
914
|
+
// Sync the proposed block to the archiver to make it available, only after we've managed to sign the proposal,
|
|
915
|
+
// so we avoid polluting our archive with a block that would fail.
|
|
916
|
+
// We wait for the sync to succeed, as this helps catch consistency errors, even if it means we lose some time for block-building.
|
|
917
|
+
// If this throws, we abort the entire checkpoint.
|
|
918
|
+
await this.syncProposedBlockToArchiver(block);
|
|
919
|
+
// If this is the last block, do not broadcast it, since it will be included in the checkpoint proposal.
|
|
880
920
|
if (timingInfo.isLastBlock) {
|
|
881
|
-
await this.syncProposedBlockToArchiver(block);
|
|
882
921
|
this.log.verbose(`Completed final block ${blockNumber} for slot ${this.targetSlot}`, {
|
|
883
922
|
slot: this.targetSlot,
|
|
884
923
|
blockNumber,
|
|
885
924
|
blocksBuilt
|
|
886
925
|
});
|
|
887
|
-
blockPendingBroadcast =
|
|
888
|
-
block,
|
|
889
|
-
txs: usedTxs
|
|
890
|
-
};
|
|
926
|
+
blockPendingBroadcast = proposal;
|
|
891
927
|
break;
|
|
892
928
|
}
|
|
893
|
-
// Broadcast the block proposal (unless we're in fisherman mode) unless the block is the last one,
|
|
894
|
-
// in which case we'll broadcast it along with the checkpoint at the end of the loop.
|
|
895
|
-
// Note that we only send the block to the archiver if we manage to create the proposal, so if there's
|
|
896
|
-
// a HA error we don't pollute our archiver with a block that won't make it to the chain.
|
|
897
|
-
const proposal = await this.createBlockProposal(block, inHash, usedTxs, blockProposalOptions);
|
|
898
|
-
// Sync the proposed block to the archiver to make it available, only after we've managed to sign the proposal.
|
|
899
|
-
// We wait for the sync to succeed, as this helps catch consistency errors, even if it means we lose some time for block-building.
|
|
900
|
-
// If this throws, we abort the entire checkpoint.
|
|
901
|
-
await this.syncProposedBlockToArchiver(block);
|
|
902
929
|
// Once we have a signed proposal and the archiver agreed with our proposed block, then we broadcast it.
|
|
903
930
|
proposal && await this.p2pClient.broadcastProposal(proposal);
|
|
904
931
|
// Wait until the next block's start time
|
|
@@ -948,7 +975,9 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
948
975
|
slot: this.targetSlot
|
|
949
976
|
});
|
|
950
977
|
this.metrics.recordBlockProposalFailed('insufficient_txs');
|
|
951
|
-
return
|
|
978
|
+
return {
|
|
979
|
+
failure: 'insufficient-txs'
|
|
980
|
+
};
|
|
952
981
|
}
|
|
953
982
|
// Create iterator to pending txs. We filter out txs already included in previous blocks in the checkpoint
|
|
954
983
|
// just in case p2p failed to sync the provisional block and didn't get to remove those txs from the mempool yet.
|
|
@@ -991,7 +1020,9 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
991
1020
|
slot: this.targetSlot
|
|
992
1021
|
});
|
|
993
1022
|
this.metrics.recordBlockProposalFailed('insufficient_valid_txs');
|
|
994
|
-
return
|
|
1023
|
+
return {
|
|
1024
|
+
failure: 'insufficient-valid-txs'
|
|
1025
|
+
};
|
|
995
1026
|
}
|
|
996
1027
|
// Block creation succeeded, emit stats and metrics
|
|
997
1028
|
const { block, publicProcessorDuration, usedTxs, blockBuildDuration, numTxs } = buildResult;
|
|
@@ -1010,6 +1041,8 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
1010
1041
|
manaPerSec,
|
|
1011
1042
|
...blockStats
|
|
1012
1043
|
});
|
|
1044
|
+
// `slot` is the target/submission slot (may be one ahead when pipelining),
|
|
1045
|
+
// `buildSlot` is the wall-clock slot during which the block was actually built.
|
|
1013
1046
|
this.eventEmitter.emit('block-proposed', {
|
|
1014
1047
|
blockNumber: block.number,
|
|
1015
1048
|
slot: this.targetSlot,
|
|
@@ -1110,7 +1143,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
1110
1143
|
committee
|
|
1111
1144
|
});
|
|
1112
1145
|
}
|
|
1113
|
-
const numberOfRequiredAttestations =
|
|
1146
|
+
const numberOfRequiredAttestations = computeQuorum(committee.length);
|
|
1114
1147
|
if (this.config.skipCollectingAttestations) {
|
|
1115
1148
|
this.log.warn('Skipping attestation collection as per config (attesting with own keys only)');
|
|
1116
1149
|
const attestations = await this.validatorClient?.collectOwnAttestations(proposal);
|
|
@@ -1215,8 +1248,12 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
1215
1248
|
* Adds the proposed block to the archiver so it's available via P2P.
|
|
1216
1249
|
* Gossip doesn't echo messages back to the sender, so the proposer's archiver/world-state
|
|
1217
1250
|
* would never receive its own block without this explicit sync.
|
|
1251
|
+
*
|
|
1252
|
+
* In fisherman mode we skip this push: the fisherman builds blocks locally for validation
|
|
1253
|
+
* and fee analysis only, and pushing them to the archiver causes spurious reorg cascades
|
|
1254
|
+
* whenever the real proposer's block arrives from L1.
|
|
1218
1255
|
*/ async syncProposedBlockToArchiver(block) {
|
|
1219
|
-
if (this.config.skipPushProposedBlocksToArchiver
|
|
1256
|
+
if (this.config.skipPushProposedBlocksToArchiver || this.config.fishermanMode) {
|
|
1220
1257
|
this.log.warn(`Skipping push of proposed block ${block.number} to archiver`, {
|
|
1221
1258
|
blockNumber: block.number,
|
|
1222
1259
|
slot: block.header.globalVariables.slotNumber
|
|
@@ -1268,6 +1305,40 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
1268
1305
|
}
|
|
1269
1306
|
return false;
|
|
1270
1307
|
}
|
|
1308
|
+
/**
|
|
1309
|
+
* In times of congestion we need to simulate using the correct fee header override for the previous block
|
|
1310
|
+
* We calculate the correct fee header values.
|
|
1311
|
+
*
|
|
1312
|
+
* If we are in block 1, or the checkpoint we are querying does not exist, we return undefined. However
|
|
1313
|
+
* If we are pipelining - where this function is called, the grandparentCheckpointNumber should always exist
|
|
1314
|
+
* @param parentCheckpointNumber
|
|
1315
|
+
* @returns
|
|
1316
|
+
*/ async computeForceProposedFeeHeader(parentCheckpointNumber) {
|
|
1317
|
+
if (!this.proposedCheckpointData) {
|
|
1318
|
+
return undefined;
|
|
1319
|
+
}
|
|
1320
|
+
const rollup = this.publisher.rollupContract;
|
|
1321
|
+
const grandparentCheckpointNumber = CheckpointNumber(this.checkpointNumber - 2);
|
|
1322
|
+
try {
|
|
1323
|
+
const [grandparentCheckpoint, manaTarget] = await Promise.all([
|
|
1324
|
+
rollup.getCheckpoint(grandparentCheckpointNumber),
|
|
1325
|
+
rollup.getManaTarget()
|
|
1326
|
+
]);
|
|
1327
|
+
if (!grandparentCheckpoint || !grandparentCheckpoint.feeHeader) {
|
|
1328
|
+
this.log.error(`Grandparent checkpoint or its feeHeader is undefined for checkpointNumber=${grandparentCheckpointNumber.toString()}`);
|
|
1329
|
+
return undefined;
|
|
1330
|
+
} else {
|
|
1331
|
+
const parentFeeHeader = RollupContract.computeChildFeeHeader(grandparentCheckpoint.feeHeader, this.proposedCheckpointData.totalManaUsed, this.proposedCheckpointData.feeAssetPriceModifier, manaTarget);
|
|
1332
|
+
return {
|
|
1333
|
+
checkpointNumber: parentCheckpointNumber,
|
|
1334
|
+
feeHeader: parentFeeHeader
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
} catch (err) {
|
|
1338
|
+
this.log.error(`Failed to fetch grandparent checkpoint or mana target for checkpointNumber=${grandparentCheckpointNumber.toString()}: ${err}`);
|
|
1339
|
+
return undefined;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1271
1342
|
/** Waits until a specific time within the current slot */ async waitUntilTimeInSlot(targetSecondsIntoSlot) {
|
|
1272
1343
|
const slotStartTimestamp = this.getSlotStartBuildTimestamp();
|
|
1273
1344
|
const targetTimestamp = slotStartTimestamp + targetSecondsIntoSlot;
|
|
@@ -1277,7 +1348,7 @@ _dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('Checkpoint
|
|
|
1277
1348
|
await sleep(TXS_POLLING_MS);
|
|
1278
1349
|
}
|
|
1279
1350
|
getSlotStartBuildTimestamp() {
|
|
1280
|
-
return getSlotStartBuildTimestamp(this.
|
|
1351
|
+
return getSlotStartBuildTimestamp(this.slotNow, this.l1Constants);
|
|
1281
1352
|
}
|
|
1282
1353
|
getSecondsIntoSlot() {
|
|
1283
1354
|
const slotStartTimestamp = this.getSlotStartBuildTimestamp();
|
|
@@ -20,7 +20,6 @@ export declare class CheckpointVoter {
|
|
|
20
20
|
private readonly config;
|
|
21
21
|
private readonly metrics;
|
|
22
22
|
private readonly log;
|
|
23
|
-
private slotTimestamp;
|
|
24
23
|
private governanceSigner;
|
|
25
24
|
private slashingSigner;
|
|
26
25
|
constructor(slot: SlotNumber, publisher: SequencerPublisher, attestorAddress: EthAddress, validatorClient: ValidatorClient, slasherClient: SlasherClientInterface | undefined, l1Constants: SequencerRollupConstants, config: ResolvedSequencerConfig, metrics: SequencerMetrics, log: Logger);
|
|
@@ -32,4 +31,4 @@ export declare class CheckpointVoter {
|
|
|
32
31
|
private enqueueGovernanceVote;
|
|
33
32
|
private enqueueSlashingVote;
|
|
34
33
|
}
|
|
35
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
34
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hlY2twb2ludF92b3Rlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlcXVlbmNlci9jaGVja3BvaW50X3ZvdGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLFVBQVUsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ2xFLE9BQU8sS0FBSyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ2hFLE9BQU8sS0FBSyxFQUFFLE1BQU0sRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQ3BELE9BQU8sS0FBSyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDN0QsT0FBTyxLQUFLLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUMvRSxPQUFPLEtBQUssRUFBRSxlQUFlLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQU0vRCxPQUFPLEtBQUssRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHFDQUFxQyxDQUFDO0FBQzlFLE9BQU8sS0FBSyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ3JELE9BQU8sS0FBSyxFQUFFLHdCQUF3QixFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRTNEOztHQUVHO0FBQ0gscUJBQWEsZUFBZTtJQUt4QixPQUFPLENBQUMsUUFBUSxDQUFDLElBQUk7SUFDckIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxTQUFTO0lBQzFCLE9BQU8sQ0FBQyxRQUFRLENBQUMsZUFBZTtJQUNoQyxPQUFPLENBQUMsUUFBUSxDQUFDLGVBQWU7SUFDaEMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxhQUFhO0lBQzlCLE9BQU8sQ0FBQyxRQUFRLENBQUMsV0FBVztJQUM1QixPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU07SUFDdkIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPO0lBQ3hCLE9BQU8sQ0FBQyxRQUFRLENBQUMsR0FBRztJQVp0QixPQUFPLENBQUMsZ0JBQWdCLENBQXVEO0lBQy9FLE9BQU8sQ0FBQyxjQUFjLENBQXVEO0lBRTdFLFlBQ21CLElBQUksRUFBRSxVQUFVLEVBQ2hCLFNBQVMsRUFBRSxrQkFBa0IsRUFDN0IsZUFBZSxFQUFFLFVBQVUsRUFDM0IsZUFBZSxFQUFFLGVBQWUsRUFDaEMsYUFBYSxFQUFFLHNCQUFzQixHQUFHLFNBQVMsRUFDakQsV0FBVyxFQUFFLHdCQUF3QixFQUNyQyxNQUFNLEVBQUUsdUJBQXVCLEVBQy9CLE9BQU8sRUFBRSxnQkFBZ0IsRUFDekIsR0FBRyxFQUFFLE1BQU0sRUFXN0I7SUFFRDs7O09BR0c7SUFDSCxZQUFZLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxHQUFHLFNBQVMsQ0FBQyxFQUFFLE9BQU8sQ0FBQyxPQUFPLEdBQUcsU0FBUyxDQUFDLENBQUMsQ0FVM0U7WUFFYSxxQkFBcUI7WUErQnJCLG1CQUFtQjtDQTJCbEMifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkpoint_voter.d.ts","sourceRoot":"","sources":["../../src/sequencer/checkpoint_voter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"checkpoint_voter.d.ts","sourceRoot":"","sources":["../../src/sequencer/checkpoint_voter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC/E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAM/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3D;;GAEG;AACH,qBAAa,eAAe;IAKxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAZtB,OAAO,CAAC,gBAAgB,CAAuD;IAC/E,OAAO,CAAC,cAAc,CAAuD;IAE7E,YACmB,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,kBAAkB,EAC7B,eAAe,EAAE,UAAU,EAC3B,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,sBAAsB,GAAG,SAAS,EACjD,WAAW,EAAE,wBAAwB,EACrC,MAAM,EAAE,uBAAuB,EAC/B,OAAO,EAAE,gBAAgB,EACzB,GAAG,EAAE,MAAM,EAW7B;IAED;;;OAGG;IACH,YAAY,IAAI,CAAC,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,EAAE,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC,CAU3E;YAEa,qBAAqB;YA+BrB,mBAAmB;CA2BlC"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
2
1
|
import { DutyAlreadySignedError } from '@aztec/validator-ha-signer/errors';
|
|
3
2
|
import { DutyType } from '@aztec/validator-ha-signer/types';
|
|
4
3
|
/**
|
|
@@ -13,7 +12,6 @@ import { DutyType } from '@aztec/validator-ha-signer/types';
|
|
|
13
12
|
config;
|
|
14
13
|
metrics;
|
|
15
14
|
log;
|
|
16
|
-
slotTimestamp;
|
|
17
15
|
governanceSigner;
|
|
18
16
|
slashingSigner;
|
|
19
17
|
constructor(slot, publisher, attestorAddress, validatorClient, slasherClient, l1Constants, config, metrics, log){
|
|
@@ -26,7 +24,6 @@ import { DutyType } from '@aztec/validator-ha-signer/types';
|
|
|
26
24
|
this.config = config;
|
|
27
25
|
this.metrics = metrics;
|
|
28
26
|
this.log = log;
|
|
29
|
-
this.slotTimestamp = getTimestampForSlot(this.slot, this.l1Constants);
|
|
30
27
|
// Create separate signers with appropriate duty contexts for governance and slashing votes
|
|
31
28
|
// These use HA protection to ensure only one node signs per slot/duty
|
|
32
29
|
const governanceContext = {
|
|
@@ -69,7 +66,7 @@ import { DutyType } from '@aztec/validator-ha-signer/types';
|
|
|
69
66
|
governanceProposerPayload: governanceProposerPayload.toString()
|
|
70
67
|
});
|
|
71
68
|
try {
|
|
72
|
-
return await this.publisher.enqueueGovernanceCastSignal(governanceProposerPayload, this.slot, this.
|
|
69
|
+
return await this.publisher.enqueueGovernanceCastSignal(governanceProposerPayload, this.slot, this.attestorAddress, this.governanceSigner);
|
|
73
70
|
} catch (err) {
|
|
74
71
|
if (err instanceof DutyAlreadySignedError) {
|
|
75
72
|
this.log.info(`Governance vote already signed by another node`, {
|
|
@@ -93,7 +90,7 @@ import { DutyType } from '@aztec/validator-ha-signer/types';
|
|
|
93
90
|
actionCount: actions.length
|
|
94
91
|
});
|
|
95
92
|
this.metrics.recordSlashingAttempt(actions.length);
|
|
96
|
-
return await this.publisher.enqueueSlashingActions(actions, this.slot, this.
|
|
93
|
+
return await this.publisher.enqueueSlashingActions(actions, this.slot, this.attestorAddress, this.slashingSigner);
|
|
97
94
|
} catch (err) {
|
|
98
95
|
if (err instanceof DutyAlreadySignedError) {
|
|
99
96
|
this.log.info(`Slashing vote already signed by another node`, {
|
|
@@ -8,7 +8,7 @@ import type { TypedEventEmitter } from '@aztec/foundation/types';
|
|
|
8
8
|
import type { P2P } from '@aztec/p2p';
|
|
9
9
|
import type { SlasherClientInterface } from '@aztec/slasher';
|
|
10
10
|
import type { BlockData, L2BlockSink, L2BlockSource, ValidateCheckpointResult } from '@aztec/stdlib/block';
|
|
11
|
-
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
11
|
+
import type { Checkpoint, ProposedCheckpointData } from '@aztec/stdlib/checkpoint';
|
|
12
12
|
import { type ResolvedSequencerConfig, type SequencerConfig, type WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
13
13
|
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
14
14
|
import { type TelemetryClient, type Tracer } from '@aztec/telemetry-client';
|
|
@@ -60,6 +60,8 @@ export declare class Sequencer extends Sequencer_base {
|
|
|
60
60
|
private lastCheckpointProposed;
|
|
61
61
|
/** The last epoch for which we logged strategy comparison in fisherman mode. */
|
|
62
62
|
private lastEpochForStrategyComparison;
|
|
63
|
+
/** The last checkpoint proposal job, tracked so we can await its pending L1 submission during shutdown. */
|
|
64
|
+
private lastCheckpointProposalJob;
|
|
63
65
|
/** The maximum number of seconds that the sequencer can be into a slot to transition to a particular state. */
|
|
64
66
|
protected timetable: SequencerTimetable;
|
|
65
67
|
/** Config for the sequencer */
|
|
@@ -88,8 +90,13 @@ export declare class Sequencer extends Sequencer_base {
|
|
|
88
90
|
* - Submit checkpoint
|
|
89
91
|
*/
|
|
90
92
|
protected work(): Promise<Checkpoint | undefined>;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Prepares the checkpoint proposal by performing all necessary checks and setup.
|
|
95
|
+
* This is the initial step in the main loop.
|
|
96
|
+
* @returns CheckpointProposalJob if successful, undefined if we are not yet synced or are not the proposer.
|
|
97
|
+
*/
|
|
98
|
+
protected prepareCheckpointProposal(slot: SlotNumber, targetSlot: SlotNumber, epoch: EpochNumber, targetEpoch: EpochNumber, ts: bigint, nowSeconds: bigint): Promise<CheckpointProposalJob | undefined>;
|
|
99
|
+
protected createCheckpointProposalJob(slot: SlotNumber, targetSlot: SlotNumber, targetEpoch: EpochNumber, checkpointNumber: CheckpointNumber, syncedToBlockNumber: BlockNumber, proposer: EthAddress | undefined, publisher: SequencerPublisher, attestorAddress: EthAddress, invalidateCheckpoint: InvalidateCheckpointRequest | undefined, proposedCheckpointData?: ProposedCheckpointData): CheckpointProposalJob;
|
|
93
100
|
/**
|
|
94
101
|
* Returns the current sequencer state.
|
|
95
102
|
*/
|
|
@@ -194,9 +201,12 @@ export declare class Sequencer extends Sequencer_base {
|
|
|
194
201
|
type SequencerSyncCheckResult = {
|
|
195
202
|
blockData?: BlockData;
|
|
196
203
|
checkpointNumber: CheckpointNumber;
|
|
204
|
+
checkpointedCheckpointNumber: CheckpointNumber;
|
|
197
205
|
blockNumber: BlockNumber;
|
|
198
206
|
archive: Fr;
|
|
207
|
+
hasProposedCheckpoint: boolean;
|
|
208
|
+
proposedCheckpointData?: ProposedCheckpointData;
|
|
199
209
|
syncedL2Slot: SlotNumber;
|
|
200
210
|
pendingChainValidationStatus: ValidateCheckpointResult;
|
|
201
211
|
};
|
|
202
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
212
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VxdWVuY2VyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VxdWVuY2VyL3NlcXVlbmNlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNyRCxPQUFPLEVBQW9CLEtBQUssY0FBYyxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDbEYsT0FBTyxFQUFFLFdBQVcsRUFBRSxnQkFBZ0IsRUFBRSxXQUFXLEVBQUUsVUFBVSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFFekcsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ3BELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUczRCxPQUFPLEtBQUssRUFBRSxZQUFZLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUM1RCxPQUFPLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ2pFLE9BQU8sS0FBSyxFQUFFLEdBQUcsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUN0QyxPQUFPLEtBQUssRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzdELE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUsYUFBYSxFQUFFLHdCQUF3QixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDM0csT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLHNCQUFzQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFbkYsT0FBTyxFQUNMLEtBQUssdUJBQXVCLEVBQzVCLEtBQUssZUFBZSxFQUVwQixLQUFLLHNCQUFzQixFQUM1QixNQUFNLGlDQUFpQyxDQUFDO0FBQ3pDLE9BQU8sS0FBSyxFQUFFLG1CQUFtQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFHbkUsT0FBTyxFQUFjLEtBQUssZUFBZSxFQUFFLEtBQUssTUFBTSxFQUFpQyxNQUFN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
|