Skip to content

Commit 580243a

Browse files
authored
Merge pull request #846 from jshufro/jms/constraintscleanup
Clean up some version specific logic for versions well behind upgrade barriers
2 parents c66c076 + 2acd718 commit 580243a

1 file changed

Lines changed: 0 additions & 220 deletions

File tree

rocketpool-cli/service/service.go

Lines changed: 0 additions & 220 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,11 @@ package service
33
import (
44
"fmt"
55
"os"
6-
"os/exec"
76
"path/filepath"
87
"regexp"
98
"strings"
109
"time"
1110

12-
"github.com/hashicorp/go-version"
1311
"github.com/mitchellh/go-homedir"
1412
"github.com/rivo/tview"
1513
"github.com/urfave/cli"
@@ -556,45 +554,6 @@ func startService(c *cli.Context, ignoreConfigSuggestion bool) error {
556554
fmt.Printf("%sIgnoring anti-slashing safety delay.%s\n", colorYellow, colorReset)
557555
}
558556

559-
// Force a delay if using Teku and upgrading from v1.3.0 or below because of the slashing protection DB migration in v1.3.1+
560-
isLocalTeku := (cfg.ConsensusClientMode.Value.(cfgtypes.Mode) == cfgtypes.Mode_Local && cfg.ConsensusClient.Value.(cfgtypes.ConsensusClient) == cfgtypes.ConsensusClient_Teku)
561-
isExternalTeku := (cfg.ConsensusClientMode.Value.(cfgtypes.Mode) == cfgtypes.Mode_External && cfg.ExternalConsensusClient.Value.(cfgtypes.ConsensusClient) == cfgtypes.ConsensusClient_Teku)
562-
if isUpdate && !isNew && !cfg.IsNativeMode && (isLocalTeku || isExternalTeku) && !c.Bool("ignore-slash-timer") {
563-
previousVersion := "0.0.0"
564-
backupCfg, err := rp.LoadBackupConfig()
565-
if err != nil {
566-
fmt.Printf("WARNING: Couldn't determine previous Smart Node version from backup settings: %s\n", err.Error())
567-
} else if backupCfg != nil {
568-
previousVersion = backupCfg.Version
569-
}
570-
571-
oldVersion, err := version.NewVersion(strings.TrimPrefix(previousVersion, "v"))
572-
if err != nil {
573-
fmt.Printf("WARNING: Backup configuration states the previous Smart Node installation used version %s, which is not a valid version\n", previousVersion)
574-
oldVersion, _ = version.NewVersion("0.0.0")
575-
}
576-
577-
vulnerableConstraint, _ := version.NewConstraint("<= 1.3.0")
578-
if vulnerableConstraint.Check(oldVersion) {
579-
err = handleTekuSlashProtectionMigrationDelay(rp, cfg)
580-
if err != nil {
581-
return err
582-
}
583-
}
584-
}
585-
586-
// Force stop eth2 if using Nimbus prior to v1.8.0 so it ensures the container is shut down and thus lets go of the validator keys and slashing database
587-
isLocalNimbus := (cfg.ConsensusClientMode.Value.(cfgtypes.Mode) == cfgtypes.Mode_Local && cfg.ConsensusClient.Value.(cfgtypes.ConsensusClient) == cfgtypes.ConsensusClient_Nimbus)
588-
if isUpdate && !isNew && !cfg.IsNativeMode && isLocalNimbus {
589-
proceed, err := handleNimbusSplitConversion(rp, cfg)
590-
if err != nil {
591-
return fmt.Errorf("error handling Nimbus split-mode upgrade: %w", err)
592-
}
593-
if !proceed {
594-
return nil
595-
}
596-
}
597-
598557
// Write a note on doppelganger protection
599558
doppelgangerEnabled, err := cfg.IsDoppelgangerEnabled()
600559
if err != nil {
@@ -614,182 +573,6 @@ func startService(c *cli.Context, ignoreConfigSuggestion bool) error {
614573

615574
}
616575

617-
// Versions prior to v1.9.0 had Nimbus in single mode instead of split mode, so handle the conversion to ensure the user doesn't get slashed
618-
func handleNimbusSplitConversion(rp *rocketpool.Client, cfg *config.RocketPoolConfig) (bool, error) {
619-
620-
previousVersion := "0.0.0"
621-
backupCfg, err := rp.LoadBackupConfig()
622-
if err != nil {
623-
fmt.Printf("%sWARNING: Couldn't determine previous Smart Node version from backup settings: %s%s\n", colorYellow, err.Error(), colorReset)
624-
fmt.Printf("%sYou are configured to use Nimbus in local mode. Starting with v1.9.0, Nimbus is now configured to use a split-process configuration, which means the Beacon Node (the `eth2` container) no longer loads your validator keys - now the `validator` container does.\n\nDue to this, we must restart Nimbus as part of the upgrade.\n\nIf you were previously running Smart Node v1.7.5 or earlier, you **MUST** shut down the Docker containers with `rocketpool service stop` and wait **at least 15 minutes** to ensure that you've missed at least two attestations before proceeding to prevent being slashed. Please use an explorer such as https://beaconcha.in to confirm at least one of the missed attestations has been finalized before proceeding.%s\n\n", colorYellow, colorReset)
625-
fmt.Println()
626-
if !prompt.Confirm(fmt.Sprintf("Press y when you understand the above warning, have waited, and are ready to start Rocket Pool:%s", colorReset)) {
627-
fmt.Println("Cancelled.")
628-
return false, nil
629-
}
630-
return true, nil
631-
} else if backupCfg != nil {
632-
previousVersion = backupCfg.Version
633-
} else {
634-
fmt.Printf("%sWARNING: Couldn't determine previous Smart Node version from backup settings because the backup configuration didn't exist.%s\n", colorYellow, colorReset)
635-
fmt.Printf("%sYou are configured to use Nimbus in local mode. Starting with v1.9.0, Nimbus is now configured to use a split-process configuration, which means the Beacon Node (the `eth2` container) no longer loads your validator keys - now the `validator` container does.\n\nDue to this, we must restart Nimbus as part of the upgrade.\n\nIf you were previously running Smart Node v1.7.5 or earlier, you **MUST** shut down the Docker containers with `rocketpool service stop` and wait **at least 15 minutes** to ensure that you've missed at least two attestations before proceeding to prevent being slashed. Please use an explorer such as https://beaconcha.in to confirm at least one of the missed attestations has been finalized before proceeding.%s\n\n", colorYellow, colorReset)
636-
fmt.Println()
637-
if !prompt.Confirm(fmt.Sprintf("Press y when you understand the above warning, have waited, and are ready to start Rocket Pool:%s", colorReset)) {
638-
fmt.Println("Cancelled.")
639-
return false, nil
640-
}
641-
return true, nil
642-
}
643-
644-
oldVersion, err := version.NewVersion(strings.TrimPrefix(previousVersion, "v"))
645-
if err != nil {
646-
fmt.Printf("%sWARNING: Backup configuration states the previous Smart Node installation used version %s, which is not a valid version%s\n", colorYellow, previousVersion, colorReset)
647-
fmt.Printf("%sYou are configured to use Nimbus in local mode. Starting with v1.9.0, Nimbus is now configured to use a split-process configuration, which means the Beacon Node (the `eth2` container) no longer loads your validator keys - now the `validator` container does.\n\nDue to this, we must restart Nimbus as part of the upgrade.\n\nIf you were previously running Smart Node v1.7.5 or earlier, you **MUST** shut down the Docker containers with `rocketpool service stop` and wait **at least 15 minutes** to ensure that you've missed at least two attestations before proceeding to prevent being slashed. Please use an explorer such as https://beaconcha.in to confirm at least one of the missed attestations has been finalized before proceeding.%s\n\n", colorYellow, colorReset)
648-
fmt.Println()
649-
if !prompt.Confirm(fmt.Sprintf("Press y when you understand the above warning, have waited, and are ready to start Rocket Pool:%s", colorReset)) {
650-
fmt.Println("Cancelled.")
651-
return false, nil
652-
}
653-
return true, nil
654-
}
655-
656-
vulnerableConstraint, _ := version.NewConstraint("< 1.8.0")
657-
if vulnerableConstraint.Check(oldVersion) {
658-
fmt.Println()
659-
fmt.Printf("%sNOTE: You are configured to use Nimbus in local mode. Starting with v1.9.0, Nimbus is now configured to use a split-process configuration, which means the Beacon Node (the `eth2` container) no longer loads your validator keys - now the `validator` container does.\n\nDue to this, we must restart Nimbus as part of the upgrade. Your client's slashing database will be moved from the `eth2` container to the `validator` container automatically to ensure your node doesn't attest to the same duty twice and get slashed.\n\nIf you have *any concern at all* about this process, you may want to voluntarily shut down the Docker containers with `rocketpool service stop` and wait 15 minutes to ensure that you've missed at least two attestations before proceeding. If you do this, please use an explorer such as https://beaconcha.in to confirm at least one of the missed attestations has been finalized before proceeding.%s\n\n", colorYellow, colorReset)
660-
fmt.Println()
661-
if !prompt.Confirm("Do you want to continue starting the service?") {
662-
fmt.Println("Cancelled.")
663-
return false, nil
664-
}
665-
666-
// Ensure the eth2 and validator containers have stopped
667-
prefix, err := rp.GetContainerPrefix()
668-
if err != nil {
669-
return false, fmt.Errorf("error getting container prefix: %w", err)
670-
}
671-
672-
successfulStop := true
673-
eth2ContainerName := prefix + BeaconContainerSuffix
674-
fmt.Printf("Stopping %s...\n", eth2ContainerName)
675-
out, err := rp.StopContainer(eth2ContainerName)
676-
if err != nil {
677-
exitErr, isExitErr := err.(*exec.ExitError)
678-
if isExitErr && exitErr.ProcessState.ExitCode() == 1 && strings.Contains(string(exitErr.Stderr), "No such container:") {
679-
// Handle errors where the container didn't exist
680-
fmt.Printf("%sNOTE: couldn't shut down %s because it didn't exist.%s\n", colorYellow, eth2ContainerName, colorReset)
681-
successfulStop = false
682-
} else {
683-
return false, fmt.Errorf("error stopping %s: %w", eth2ContainerName, err)
684-
}
685-
} else if out != eth2ContainerName {
686-
return false, fmt.Errorf("unexpected output when trying to stop %s: [%s]", eth2ContainerName, out)
687-
}
688-
689-
validatorContainerName := prefix + ValidatorContainerSuffix
690-
fmt.Printf("Stopping %s...\n", validatorContainerName)
691-
out, err = rp.StopContainer(validatorContainerName)
692-
if err != nil {
693-
exitErr, isExitErr := err.(*exec.ExitError)
694-
if isExitErr && exitErr.ProcessState.ExitCode() == 1 && strings.Contains(string(exitErr.Stderr), "No such container:") {
695-
// Handle errors where the container didn't exist
696-
fmt.Printf("%sNOTE: couldn't shut down %s because it didn't exist.%s\n", colorYellow, validatorContainerName, colorReset)
697-
successfulStop = false
698-
} else {
699-
return false, fmt.Errorf("error stopping %s: %w", validatorContainerName, err)
700-
}
701-
} else if out != validatorContainerName {
702-
return false, fmt.Errorf("unexpected output when trying to stop %s: [%s]", validatorContainerName, out)
703-
}
704-
705-
if !successfulStop {
706-
fmt.Println()
707-
fmt.Printf("%sWARNING: Some of the Nimbus containers couldn't be shut down safely.\nThe Smart Node can't guarantee the safe transfer of the slashing database. If you have active validators, you **must ensure** you have waited 15 minutes since your last attestation and **missed at least two attestations** before continuing.\nIf you don't, you %sMAY BE SLASHED!%s\n\n", colorYellow, colorRed, colorReset)
708-
fmt.Println()
709-
if !prompt.Confirm(fmt.Sprintf("Press y when you understand the above warning, have waited, and are ready to start Rocket Pool:%s", colorReset)) {
710-
fmt.Println("Cancelled.")
711-
return false, nil
712-
}
713-
}
714-
}
715-
716-
return true, nil
717-
718-
}
719-
720-
// Versions prior to v1.3.1 didn't preserve Teku's slashing DB, so force a delay when upgrading to ensure the user doesn't get slashed by accident
721-
func handleTekuSlashProtectionMigrationDelay(rp *rocketpool.Client, cfg *config.RocketPoolConfig) error {
722-
723-
fmt.Printf("%s=== NOTICE ===\n", colorYellow)
724-
fmt.Printf("You are currently using Teku as your Consensus client.\nv1.3.1+ fixes an issue that would cause Teku's slashing protection database to be lost after an upgrade.\nIt will now be rebuilt.\n\nFor the absolute safety of your funds, your node will wait for 15 minutes before starting.\nYou will miss a few attestations during this process; this is expected.\n\nThis delay only needs to happen the first time you start the Smart Node after upgrading to v1.3.1 or higher.%s\n\nIf you are installing the Smart Node for the first time or don't have any validators yet, you can skip this with `rocketpool service start --ignore-slash-timer`. Otherwise, we strongly recommend you wait for the full delay.\n\n", colorReset)
725-
726-
// Get the container prefix
727-
prefix, err := rp.GetContainerPrefix()
728-
if err != nil {
729-
return fmt.Errorf("Error getting validator container prefix: %w", err)
730-
}
731-
732-
// Get the current validator client
733-
currentValidatorImageString, err := rp.GetDockerImage(prefix + ValidatorContainerSuffix)
734-
if err != nil {
735-
return fmt.Errorf("Error getting current validator image: %w", err)
736-
}
737-
738-
currentValidatorName, err := getDockerImageName(currentValidatorImageString)
739-
if err != nil {
740-
return fmt.Errorf("Error getting current validator image name: %w", err)
741-
}
742-
743-
// Get the time that the container responsible for validator duties exited
744-
validatorDutyContainerName, err := getContainerNameForValidatorDuties(currentValidatorName, rp)
745-
if err != nil {
746-
return fmt.Errorf("Error getting validator container name: %w", err)
747-
}
748-
validatorFinishTime, err := rp.GetDockerContainerShutdownTime(validatorDutyContainerName)
749-
if err != nil {
750-
return fmt.Errorf("Error getting validator shutdown time: %w", err)
751-
}
752-
753-
// If it hasn't exited yet, shut it down
754-
zeroTime := time.Time{}
755-
status, err := rp.GetDockerStatus(validatorDutyContainerName)
756-
if err != nil {
757-
return fmt.Errorf("Error getting container [%s] status: %w", validatorDutyContainerName, err)
758-
}
759-
if validatorFinishTime == zeroTime || status == "running" {
760-
fmt.Printf("%sValidator is currently running, stopping it...%s\n", colorYellow, colorReset)
761-
response, err := rp.StopContainer(validatorDutyContainerName)
762-
validatorFinishTime = time.Now()
763-
if err != nil {
764-
return fmt.Errorf("Error stopping container [%s]: %w", validatorDutyContainerName, err)
765-
}
766-
if response != validatorDutyContainerName {
767-
return fmt.Errorf("Unexpected response when stopping container [%s]: %s", validatorDutyContainerName, response)
768-
}
769-
}
770-
771-
// Print the warning and start the time lockout
772-
safeStartTime := validatorFinishTime.Add(15 * time.Minute)
773-
remainingTime := time.Until(safeStartTime)
774-
if remainingTime <= 0 {
775-
fmt.Printf("The validator has been offline for %s, which is long enough to prevent slashing.\n", time.Since(validatorFinishTime))
776-
fmt.Println("The new client can be safely started.")
777-
} else {
778-
// Wait for 15 minutes
779-
for remainingTime > 0 {
780-
fmt.Printf("Remaining time: %s", remainingTime)
781-
time.Sleep(1 * time.Second)
782-
remainingTime = time.Until(safeStartTime)
783-
fmt.Printf("%s\r", clearLine)
784-
}
785-
786-
fmt.Println(colorReset)
787-
fmt.Println("You may now safely start the validator without fear of being slashed.")
788-
}
789-
790-
return nil
791-
}
792-
793576
func checkForValidatorChange(rp *rocketpool.Client, cfg *config.RocketPoolConfig) error {
794577

795578
// Get the container prefix
@@ -824,9 +607,6 @@ func checkForValidatorChange(rp *rocketpool.Client, cfg *config.RocketPoolConfig
824607
fmt.Printf("Validator client [%s] was previously used - no slashing prevention delay necessary.\n", currentValidatorName)
825608
} else if currentValidatorName == "" {
826609
fmt.Println("This is the first time starting Rocket Pool - no slashing prevention delay necessary.")
827-
} else if (currentValidatorName == "nimbus-eth2" && pendingValidatorName == "nimbus-validator-client") || (pendingValidatorName == "nimbus-eth2" && currentValidatorName == "nimbus-validator-client") {
828-
// Handle the transition from Nimbus v22.11.x to Nimbus v22.12.x where they split the VC into its own container
829-
fmt.Printf("Validator client [%s] was previously used, you are changing to [%s] but the Smart Node will migrate your slashing database automatically to this new client. No slashing prevention delay is necessary.\n", currentValidatorName, pendingValidatorName)
830610
} else {
831611

832612
consensusClient, _ := cfg.GetSelectedConsensusClient()

0 commit comments

Comments
 (0)