mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
247 lines
6.7 KiB
Go
247 lines
6.7 KiB
Go
// Copyright (C) 2022, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
package subnetcmd
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/luxfi/cli/v2/pkg/constants"
|
|
"github.com/luxfi/cli/v2/pkg/models"
|
|
"github.com/luxfi/cli/v2/pkg/prompts"
|
|
"github.com/luxfi/cli/v2/pkg/subnet"
|
|
"github.com/luxfi/cli/v2/pkg/txutils"
|
|
"github.com/luxfi/cli/v2/pkg/ux"
|
|
"github.com/luxfi/node/v2/v2/genesis"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/node/v2/v2/vms/secp256k1fx"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// lux subnet deploy
|
|
func newRemoveValidatorCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "removeValidator [subnetName]",
|
|
Short: "Remove a permissioned validator from your subnet",
|
|
Long: `The subnet removeValidator command stops a whitelisted, subnet network validator from
|
|
validating your deployed Subnet.
|
|
|
|
To remove the validator from the Subnet's allow list, provide the validator's unique NodeID. You can bypass
|
|
these prompts by providing the values with flags.`,
|
|
SilenceUsage: true,
|
|
RunE: removeValidator,
|
|
Args: cobra.ExactArgs(1),
|
|
}
|
|
cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [testnet deploy only]")
|
|
cmd.Flags().StringVar(&nodeIDStr, "nodeID", "", "set the NodeID of the validator to remove")
|
|
cmd.Flags().BoolVar(&deployLocal, "local", false, "remove from the locally deployed Subnet")
|
|
cmd.Flags().BoolVar(&deployTestnet, "testnet", false, "remove from `testnet` deployment (alias for `testnet`)")
|
|
cmd.Flags().BoolVar(&deployMainnet, "mainnet", false, "remove from `mainnet` deployment")
|
|
cmd.Flags().StringSliceVar(&subnetAuthKeys, "subnet-auth-keys", nil, "control keys that will be used to authenticate the removeValidator tx")
|
|
cmd.Flags().StringVar(&outputTxPath, "output-tx-path", "", "file path of the removeValidator tx")
|
|
cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on testnet)")
|
|
cmd.Flags().StringSliceVar(&ledgerAddresses, "ledger-addrs", []string{}, "use the given ledger addresses")
|
|
return cmd
|
|
}
|
|
|
|
func removeValidator(_ *cobra.Command, args []string) error {
|
|
var (
|
|
nodeID ids.NodeID
|
|
err error
|
|
)
|
|
|
|
var network models.Network
|
|
switch {
|
|
case deployTestnet:
|
|
network = models.Testnet
|
|
case deployMainnet:
|
|
network = models.Mainnet
|
|
case deployLocal:
|
|
network = models.Local
|
|
}
|
|
|
|
if network == models.Undefined {
|
|
networkStr, err := app.Prompt.CaptureList(
|
|
"Choose a network to remove a validator from",
|
|
[]string{models.Local.String(), models.Testnet.String(), models.Mainnet.String()},
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
network = models.NetworkFromString(networkStr)
|
|
}
|
|
|
|
if outputTxPath != "" {
|
|
if _, err := os.Stat(outputTxPath); err == nil {
|
|
return fmt.Errorf("outputTxPath %q already exists", outputTxPath)
|
|
}
|
|
}
|
|
|
|
if len(ledgerAddresses) > 0 {
|
|
useLedger = true
|
|
}
|
|
|
|
if useLedger && keyName != "" {
|
|
return ErrMutuallyExlusiveKeyLedger
|
|
}
|
|
|
|
chains, err := validateSubnetNameAndGetChains(args)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
subnetName := chains[0]
|
|
|
|
switch network {
|
|
case models.Local:
|
|
return removeFromLocal(subnetName)
|
|
case models.Testnet:
|
|
if !useLedger && keyName == "" {
|
|
useLedger, keyName, err = prompts.GetTestnetKeyOrLedger(app.Prompt, "pay transaction fees", app.GetKeyDir())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
case models.Mainnet:
|
|
useLedger = true
|
|
if keyName != "" {
|
|
return ErrStoredKeyOnMainnet
|
|
}
|
|
default:
|
|
return errors.New("unsupported network")
|
|
}
|
|
|
|
// used in E2E to simulate public network execution paths on a local network
|
|
if os.Getenv(constants.SimulatePublicNetwork) != "" {
|
|
network = models.Local
|
|
}
|
|
|
|
sc, err := app.LoadSidecar(subnetName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
subnetID := sc.Networks[network.String()].SubnetID
|
|
if subnetID == ids.Empty {
|
|
return errNoSubnetID
|
|
}
|
|
|
|
controlKeys, threshold, err := txutils.GetOwners(network, subnetID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// get keys for add validator tx signing
|
|
if subnetAuthKeys != nil {
|
|
if err := prompts.CheckSubnetAuthKeys(subnetAuthKeys, controlKeys, threshold); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
subnetAuthKeys, err = prompts.GetSubnetAuthKeys(app.Prompt, controlKeys, threshold)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
ux.Logger.PrintToUser("Your subnet auth keys for remove validator tx creation: %s", subnetAuthKeys)
|
|
|
|
if nodeIDStr == "" {
|
|
nodeID, err = promptNodeID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
nodeID, err = ids.NodeIDFromString(nodeIDStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// check that this guy actually is a validator on the subnet
|
|
isValidator, err := subnet.IsSubnetValidator(subnetID, nodeID, network)
|
|
if err != nil {
|
|
// just warn the user, don't fail
|
|
ux.Logger.PrintToUser("failed to check if node is a validator on the subnet: %s", err)
|
|
} else if !isValidator {
|
|
// this is actually an error
|
|
return fmt.Errorf("node %s is not a validator on subnet %s", nodeID, subnetID)
|
|
}
|
|
|
|
ux.Logger.PrintToUser("NodeID: %s", nodeID.String())
|
|
ux.Logger.PrintToUser("Network: %s", network.String())
|
|
ux.Logger.PrintToUser("Inputs complete, issuing transaction to remove the specified validator...")
|
|
|
|
// get keychain accesor
|
|
kc, err := GetKeychain(useLedger, ledgerAddresses, keyName, network)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
deployer := subnet.NewPublicDeployer(app, useLedger, kc, network)
|
|
isFullySigned, tx, remainingSubnetAuthKeys, err := deployer.RemoveValidator(controlKeys, subnetAuthKeys, subnetID, nodeID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !isFullySigned {
|
|
if err := SaveNotFullySignedTx(
|
|
"Remove Validator",
|
|
tx,
|
|
subnetName,
|
|
subnetAuthKeys,
|
|
remainingSubnetAuthKeys,
|
|
outputTxPath,
|
|
false,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func removeFromLocal(subnetName string) error {
|
|
sc, err := app.LoadSidecar(subnetName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
subnetID := sc.Networks[models.Local.String()].SubnetID
|
|
if subnetID == ids.Empty {
|
|
return errNoSubnetID
|
|
}
|
|
|
|
// Get NodeIDs of all validators on the subnet
|
|
validators, err := subnet.GetSubnetValidators(subnetID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// construct list of validators to choose from
|
|
validatorList := make([]string, len(validators))
|
|
for i, v := range validators {
|
|
validatorList[i] = v.NodeID.String()
|
|
}
|
|
|
|
if nodeIDStr == "" {
|
|
nodeIDStr, err = app.Prompt.CaptureList("Choose a validator to remove", validatorList)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Convert NodeID string to NodeID type
|
|
nodeID, err := ids.NodeIDFromString(nodeIDStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
testKey := genesis.EWOQKey
|
|
keyChain := secp256k1fx.NewKeychain(testKey)
|
|
_, err = subnet.IssueRemoveSubnetValidatorTx(keyChain, subnetID, nodeID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ux.Logger.PrintToUser("Validator removed")
|
|
|
|
return nil
|
|
}
|