From 63cb85b83f69357d9c72f21a44668c3d05a1e9d7 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 11 Sep 2024 10:09:24 -0400 Subject: [PATCH 01/43] prompt addresses --- cmd/blockchaincmd/create.go | 88 ++++++++++++++++++++++++++++++ cmd/blockchaincmd/prompt_owners.go | 33 ++++++++--- pkg/constants/constants.go | 3 + pkg/models/sidecar.go | 6 +- 4 files changed, 119 insertions(+), 11 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 842c356c6..79a7ac97f 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -49,6 +49,8 @@ type CreateFlags struct { useExternalGasToken bool proofOfStake bool proofOfAuthority bool + tokenMinterAddress []string + validatorManagerController []string } var ( @@ -108,6 +110,8 @@ configuration, pass the -f flag.`, cmd.Flags().BoolVar(&createFlags.useExternalGasToken, "external-gas-token", false, "use a gas token from another blockchain") cmd.Flags().BoolVar(&createFlags.proofOfAuthority, "proof-of-authority", false, "use proof of authority for validator management") cmd.Flags().BoolVar(&createFlags.proofOfStake, "proof-of-stake", false, "use proof of stake for validator management") + cmd.Flags().StringSliceVar(&createFlags.tokenMinterAddress, "token-minter-address", nil, "addresses that may make mint new native tokens") + cmd.Flags().StringSliceVar(&createFlags.validatorManagerController, "validator-manager-controller", nil, "addresses that will control Validator Manager contract") return cmd } @@ -358,6 +362,33 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { if err = promptValidatorManagementType(app, sc); err != nil { return err } + if sc.ValidatorManagement == models.ProofOfAuthority { + if createFlags.tokenMinterAddress == nil { + createFlags.tokenMinterAddress, err = getTokenMinterAddr() + if err != nil { + return err + } + } + } + if len(createFlags.tokenMinterAddress) > 0 { + ux.Logger.GreenCheckmarkToUser("Addresses added as new native token minter", createFlags.tokenMinterAddress) + } else { + ux.Logger.GreenCheckmarkToUser("No additional addresses added as new native token minter") + } + sc.NewNativeTokenMinter = createFlags.tokenMinterAddress + if createFlags.validatorManagerController == nil { + var cancelled bool + createFlags.validatorManagerController, cancelled, err = getValidatorContractManagerAddr() + if err != nil { + return err + } + if cancelled { + return fmt.Errorf("user cancelled operation") + } + } + sc.ValidatorManagerController = createFlags.validatorManagerController + //TODO: add description of what Validator Manager Contract controller does + ux.Logger.GreenCheckmarkToUser("Validator Manager Contract controller %s", createFlags.validatorManagerController) if err = app.WriteGenesisFile(blockchainName, genesisBytes); err != nil { return err } @@ -375,6 +406,63 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return nil } +func getValidatorContractManagerAddr() ([]string, bool, error) { + controllerAddrPrompt := "Enter Validator Manager Contract controller address" + for { + // ask in a loop so that if some condition is not met we can keep asking + controlAddr, cancelled, err := getAddrLoop(controllerAddrPrompt, constants.ValidatorManagerController, models.UndefinedNetwork) + if err != nil { + return nil, false, err + } + if cancelled { + return nil, cancelled, nil + } + if len(controlAddr) != 0 { + return controlAddr, false, nil + } + ux.Logger.RedXToUser("An address to control Validator Manage Contract is required before proceeding") + } +} + +// Configure which addresses may make mint new native tokens +func getTokenMinterAddr() ([]string, error) { + addTokenMinterAddrPrompt := "Currently only Validator Manager Contract can mint new native tokens" + ux.Logger.PrintToUser(addTokenMinterAddrPrompt) + yes, err := app.Prompt.CaptureNoYes("Add additional addresses that can mint new native tokens?") + if err != nil { + return nil, err + } + if !yes { + return nil, nil + } + addr, cancelled, err := enterCustomAddr() + if err != nil { + return nil, err + } + if cancelled { + return nil, nil + } + return addr, nil +} + +func enterCustomAddr() ([]string, bool, error) { + addrPrompt := "Enter addresses that can mint new native tokens" + for { + addr, cancelled, err := getAddrLoop(addrPrompt, constants.TokenMinter, models.UndefinedNetwork) + if err != nil { + return nil, false, err + } + if cancelled { + return nil, cancelled, nil + } + //if len(addr) != 0 { + // return addr, false, nil + //} + //ux.Logger.PrintToUser("This tool does not allow to proceed without any control key set") + return addr, false, nil + } +} + func addSubnetEVMGenesisPrefundedAddress(genesisBytes []byte, address string, balance string) ([]byte, error) { var genesisMap map[string]interface{} if err := json.Unmarshal(genesisBytes, &genesisMap); err != nil { diff --git a/cmd/blockchaincmd/prompt_owners.go b/cmd/blockchaincmd/prompt_owners.go index 5233890af..ff97f7a59 100644 --- a/cmd/blockchaincmd/prompt_owners.go +++ b/cmd/blockchaincmd/prompt_owners.go @@ -217,7 +217,7 @@ func enterCustomKeys(network models.Network) ([]string, bool, error) { controlKeysPrompt := "Enter control keys" for { // ask in a loop so that if some condition is not met we can keep asking - controlKeys, cancelled, err := controlKeysLoop(controlKeysPrompt, network) + controlKeys, cancelled, err := getAddrLoop(controlKeysPrompt, constants.ControlKey, network) if err != nil { return nil, false, err } @@ -231,27 +231,42 @@ func enterCustomKeys(network models.Network) ([]string, bool, error) { } } -// controlKeysLoop asks as many controlkeys the user requires, until Done or Cancel is selected -func controlKeysLoop(controlKeysPrompt string, network models.Network) ([]string, bool, error) { - label := "Control key" - info := "Control keys are P-Chain addresses which have admin rights on the subnet.\n" + - "Only private keys which control such addresses are allowed to make changes on the subnet" +// getAddrLoop asks as many addresses the user requires, until Done or Cancel is selected +func getAddrLoop(prompt, label string, network models.Network) ([]string, bool, error) { + info := "" + goal := "" + switch label { + case constants.ControlKey: + info = "Control keys are P-Chain addresses which have admin rights on the subnet.\n" + + "Only private keys which control such addresses are allowed to make changes on the subnet" + goal = "be set as a subnet control key" + case constants.TokenMinter: + goal = "enable as new native token minter" + case constants.ValidatorManagerController: + goal = "enable as controller of ValidatorManager contract" + default: + } customPrompt := "Enter P-Chain address (Example: P-...)" + addressFormat := prompts.PChainFormat + if label != constants.ControlKey { + customPrompt = "Enter address" + addressFormat = prompts.EVMFormat + } return prompts.CaptureListDecision( // we need this to be able to mock test app.Prompt, // the main prompt for entering address keys - controlKeysPrompt, + prompt, // the Capture function to use func(_ string) (string, error) { return prompts.PromptAddress( app.Prompt, - "be set as a subnet control key", + goal, app.GetKeyDir(), app.GetKey, "", network, - prompts.PChainFormat, + addressFormat, customPrompt, ) }, diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index df1b76fe2..dd127aca2 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -27,6 +27,9 @@ const ( SidecarFileName = "sidecar.json" GenesisFileName = "genesis.json" AliasesFileName = "aliases.json" + ControlKey = "Control key" + TokenMinter = "Native token minter" + ValidatorManagerController = "Validator Manager Controller" SidecarSuffix = SuffixSeparator + SidecarFileName GenesisSuffix = SuffixSeparator + GenesisFileName NodeFileName = "node.json" diff --git a/pkg/models/sidecar.go b/pkg/models/sidecar.go index 63bfc3e49..ff09ed6e6 100644 --- a/pkg/models/sidecar.go +++ b/pkg/models/sidecar.go @@ -38,8 +38,10 @@ type Sidecar struct { TeleporterVersion string RunRelayer bool // SubnetEVM based VM's only - SubnetEVMMainnetChainID uint - ValidatorManagement ValidatorManagementType + SubnetEVMMainnetChainID uint + ValidatorManagement ValidatorManagementType + ValidatorManagerController []string + NewNativeTokenMinter []string } func (sc Sidecar) GetVMID() (string, error) { From 22c18e89ecdd78b94f0b35399e7604c22d59fd94 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 11 Sep 2024 10:26:33 -0400 Subject: [PATCH 02/43] fix lint --- cmd/blockchaincmd/create.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 79a7ac97f..50b4628a9 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -387,7 +387,7 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { } } sc.ValidatorManagerController = createFlags.validatorManagerController - //TODO: add description of what Validator Manager Contract controller does + // TODO: add description of what Validator Manager Contract controller does ux.Logger.GreenCheckmarkToUser("Validator Manager Contract controller %s", createFlags.validatorManagerController) if err = app.WriteGenesisFile(blockchainName, genesisBytes); err != nil { return err @@ -435,7 +435,7 @@ func getTokenMinterAddr() ([]string, error) { if !yes { return nil, nil } - addr, cancelled, err := enterCustomAddr() + addr, cancelled, err := getAddr() if err != nil { return nil, err } @@ -445,7 +445,7 @@ func getTokenMinterAddr() ([]string, error) { return addr, nil } -func enterCustomAddr() ([]string, bool, error) { +func getAddr() ([]string, bool, error) { addrPrompt := "Enter addresses that can mint new native tokens" for { addr, cancelled, err := getAddrLoop(addrPrompt, constants.TokenMinter, models.UndefinedNetwork) @@ -455,10 +455,6 @@ func enterCustomAddr() ([]string, bool, error) { if cancelled { return nil, cancelled, nil } - //if len(addr) != 0 { - // return addr, false, nil - //} - //ux.Logger.PrintToUser("This tool does not allow to proceed without any control key set") return addr, false, nil } } From ff53b0896e9464d4c56eba5b918e1f5c6acb021c Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 11 Sep 2024 10:35:52 -0400 Subject: [PATCH 03/43] fix lint --- cmd/blockchaincmd/create.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 50b4628a9..2869ac7eb 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -406,6 +406,7 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return nil } +// nolint: gocritic func getValidatorContractManagerAddr() ([]string, bool, error) { controllerAddrPrompt := "Enter Validator Manager Contract controller address" for { From 39f17e0ea9d638ec0136504d5bca640a99c65de9 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 11 Sep 2024 10:39:59 -0400 Subject: [PATCH 04/43] fix lint --- cmd/blockchaincmd/create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 2869ac7eb..735660cd3 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -406,7 +406,6 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return nil } -// nolint: gocritic func getValidatorContractManagerAddr() ([]string, bool, error) { controllerAddrPrompt := "Enter Validator Manager Contract controller address" for { @@ -446,6 +445,7 @@ func getTokenMinterAddr() ([]string, error) { return addr, nil } +//nolint: gocritic func getAddr() ([]string, bool, error) { addrPrompt := "Enter addresses that can mint new native tokens" for { From aef7fc1a50dcbb60b3998dcff727913b134105c0 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 11 Sep 2024 10:59:24 -0400 Subject: [PATCH 05/43] fix lint --- cmd/blockchaincmd/create.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 735660cd3..d441dfc82 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -49,6 +49,7 @@ type CreateFlags struct { useExternalGasToken bool proofOfStake bool proofOfAuthority bool + validatorManagerMintOnly bool tokenMinterAddress []string validatorManagerController []string } @@ -110,7 +111,8 @@ configuration, pass the -f flag.`, cmd.Flags().BoolVar(&createFlags.useExternalGasToken, "external-gas-token", false, "use a gas token from another blockchain") cmd.Flags().BoolVar(&createFlags.proofOfAuthority, "proof-of-authority", false, "use proof of authority for validator management") cmd.Flags().BoolVar(&createFlags.proofOfStake, "proof-of-stake", false, "use proof of stake for validator management") - cmd.Flags().StringSliceVar(&createFlags.tokenMinterAddress, "token-minter-address", nil, "addresses that may make mint new native tokens") + cmd.Flags().BoolVar(&createFlags.validatorManagerMintOnly, "validator-manager-mint-only", false, "only enable validator manager contract to mint new native tokens") + cmd.Flags().StringSliceVar(&createFlags.tokenMinterAddress, "token-minter-address", nil, "addresses that can mint new native tokens (for proof of authority validator management only)") cmd.Flags().StringSliceVar(&createFlags.validatorManagerController, "validator-manager-controller", nil, "addresses that will control Validator Manager contract") return cmd } @@ -445,19 +447,16 @@ func getTokenMinterAddr() ([]string, error) { return addr, nil } -//nolint: gocritic func getAddr() ([]string, bool, error) { addrPrompt := "Enter addresses that can mint new native tokens" - for { - addr, cancelled, err := getAddrLoop(addrPrompt, constants.TokenMinter, models.UndefinedNetwork) - if err != nil { - return nil, false, err - } - if cancelled { - return nil, cancelled, nil - } - return addr, false, nil + addr, cancelled, err := getAddrLoop(addrPrompt, constants.TokenMinter, models.UndefinedNetwork) + if err != nil { + return nil, false, err + } + if cancelled { + return nil, cancelled, nil } + return addr, false, nil } func addSubnetEVMGenesisPrefundedAddress(genesisBytes []byte, address string, balance string) ([]byte, error) { From 2866231c3ee83fae3856f2384c61d613ee2f5533 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 11 Sep 2024 17:32:38 -0400 Subject: [PATCH 06/43] add flags --- cmd/blockchaincmd/create.go | 27 ++++++++++++++++++++++----- cmd/blockchaincmd/prompt_owners.go | 1 + 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index d441dfc82..760e6edd8 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -66,6 +66,8 @@ var ( errMutuallyExlusiveVersionOptions = errors.New("version flags --latest,--pre-release,vm-version are mutually exclusive") errMutuallyExclusiveVMConfigOptions = errors.New("--genesis flag disables --evm-chain-id,--evm-defaults,--production-defaults,--test-defaults") errMutuallyExlusiveValidatorManagementOptions = errors.New("validator management type flags --proof-of-authority,--proof-of-stake are mutually exclusive") + errTokenMinterAddressConflict = errors.New("--validator-manager-mint-only means that no additional addresses can be provided in --token-minter-address") + errTokenMinterAddressForPoS = errors.New("--token-minter-address is only applicable to proof of authority") ) // avalanche blockchain create @@ -203,6 +205,19 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return errMutuallyExlusiveValidatorManagementOptions } + if createFlags.proofOfAuthority { + return errMutuallyExlusiveValidatorManagementOptions + } + + if len(createFlags.tokenMinterAddress) > 0 { + if createFlags.proofOfStake { + return errTokenMinterAddressForPoS + } + if createFlags.validatorManagerMintOnly { + return errTokenMinterAddressConflict + } + } + // get vm kind vmType, err := vm.PromptVMType(app, createFlags.useSubnetEvm, createFlags.useCustomVM) if err != nil { @@ -365,17 +380,19 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return err } if sc.ValidatorManagement == models.ProofOfAuthority { - if createFlags.tokenMinterAddress == nil { + if !createFlags.validatorManagerMintOnly && createFlags.tokenMinterAddress == nil { createFlags.tokenMinterAddress, err = getTokenMinterAddr() if err != nil { return err } } } - if len(createFlags.tokenMinterAddress) > 0 { - ux.Logger.GreenCheckmarkToUser("Addresses added as new native token minter", createFlags.tokenMinterAddress) - } else { - ux.Logger.GreenCheckmarkToUser("No additional addresses added as new native token minter") + if !createFlags.validatorManagerMintOnly { + if len(createFlags.tokenMinterAddress) > 0 { + ux.Logger.GreenCheckmarkToUser("Addresses added as new native token minter %s", createFlags.tokenMinterAddress) + } else { + ux.Logger.GreenCheckmarkToUser("No additional addresses added as new native token minter") + } } sc.NewNativeTokenMinter = createFlags.tokenMinterAddress if createFlags.validatorManagerController == nil { diff --git a/cmd/blockchaincmd/prompt_owners.go b/cmd/blockchaincmd/prompt_owners.go index ff97f7a59..a5c87a905 100644 --- a/cmd/blockchaincmd/prompt_owners.go +++ b/cmd/blockchaincmd/prompt_owners.go @@ -232,6 +232,7 @@ func enterCustomKeys(network models.Network) ([]string, bool, error) { } // getAddrLoop asks as many addresses the user requires, until Done or Cancel is selected +// TODO: add info for TokenMinter and ValidatorManagerController func getAddrLoop(prompt, label string, network models.Network) ([]string, bool, error) { info := "" goal := "" From 4295956ba75b27e66ca439d08a1f2d04ef7f3dff Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 12 Sep 2024 15:16:57 -0400 Subject: [PATCH 07/43] validator prompt --- cmd/blockchaincmd/create.go | 39 +++++++++++++++++++++---------------- pkg/constants/constants.go | 13 +++++++------ pkg/prompts/prompts.go | 29 +++++++++++++++++++++++++++ pkg/prompts/validations.go | 11 +++++++++++ 4 files changed, 69 insertions(+), 23 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 760e6edd8..f2f306384 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -35,23 +35,24 @@ const ( ) type CreateFlags struct { - useSubnetEvm bool - useCustomVM bool - chainID uint64 - tokenSymbol string - useTestDefaults bool - useProductionDefaults bool - useWarp bool - useTeleporter bool - vmVersion string - useLatestReleasedVMVersion bool - useLatestPreReleasedVMVersion bool - useExternalGasToken bool - proofOfStake bool - proofOfAuthority bool - validatorManagerMintOnly bool - tokenMinterAddress []string - validatorManagerController []string + useSubnetEvm bool + useCustomVM bool + chainID uint64 + tokenSymbol string + useTestDefaults bool + useProductionDefaults bool + useWarp bool + useTeleporter bool + vmVersion string + useLatestReleasedVMVersion bool + useLatestPreReleasedVMVersion bool + useExternalGasToken bool + proofOfStake bool + proofOfAuthority bool + validatorManagerMintOnly bool + tokenMinterAddress []string + validatorManagerController []string + bootstrapValidatorInitialBalance []int } var ( @@ -116,6 +117,7 @@ configuration, pass the -f flag.`, cmd.Flags().BoolVar(&createFlags.validatorManagerMintOnly, "validator-manager-mint-only", false, "only enable validator manager contract to mint new native tokens") cmd.Flags().StringSliceVar(&createFlags.tokenMinterAddress, "token-minter-address", nil, "addresses that can mint new native tokens (for proof of authority validator management only)") cmd.Flags().StringSliceVar(&createFlags.validatorManagerController, "validator-manager-controller", nil, "addresses that will control Validator Manager contract") + cmd.Flags().IntSliceVar(&createFlags.bootstrapValidatorInitialBalance, "bootstrap-validators-balanche", []int{}, "starting P-Chain balance of each bootstrap validator (minimum of 5 AVAX)") return cmd } @@ -412,6 +414,9 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return err } + if createFlags.bootstrapValidatorInitialBalance == nil { + + } if err = app.CreateSidecar(sc); err != nil { return err } diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index dd127aca2..16bbb81df 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -154,12 +154,13 @@ const ( Disable = "disable" - TimeParseLayout = "2006-01-02 15:04:05" - MinStakeWeight = 1 - DefaultStakeWeight = 20 - AVAXSymbol = "AVAX" - DefaultFujiStakeDuration = "48h" - DefaultMainnetStakeDuration = "336h" + TimeParseLayout = "2006-01-02 15:04:05" + MinStakeWeight = 1 + MinInitialBalanceBootstrapValidator = 5 + DefaultStakeWeight = 20 + AVAXSymbol = "AVAX" + DefaultFujiStakeDuration = "48h" + DefaultMainnetStakeDuration = "336h" // The absolute minimum is 25 seconds, but set to 1 minute to allow for // time to go through the command DevnetStakingStartLeadTime = 30 * time.Second diff --git a/pkg/prompts/prompts.go b/pkg/prompts/prompts.go index 9ec6248d1..1a96374e8 100644 --- a/pkg/prompts/prompts.go +++ b/pkg/prompts/prompts.go @@ -483,6 +483,35 @@ func (*realPrompter) CaptureAddresses(promptStr string) ([]common.Address, error return addresses, nil } +func (*realPrompter) CaptureInitialBalances(promptStr string, minBalance int) ([]int, error) { + addressesStr := "" + validated := false + for !validated { + var err error + addressesStr, err = utils.ReadLongString(promptui.IconGood + " " + promptStr + " ") + if err != nil { + return nil, err + } + if err := validateAddresses(addressesStr); err != nil { + fmt.Println(err) + } else { + validated = true + } + } + + prompt := promptui.Prompt{ + Label: promptStr, + Validate: validateBootstrapBalance, + } + + addressStr, err := prompt.Run() + if err != nil { + return common.Address{}, err + } + initialBalances, err := strings.Split(addressesStr, ","), + return addresses, nil +} + func (*realPrompter) CaptureExistingFilepath(promptStr string) (string, error) { prompt := promptui.Prompt{ Label: promptStr, diff --git a/pkg/prompts/validations.go b/pkg/prompts/validations.go index 416b036d3..b901a9b03 100644 --- a/pkg/prompts/validations.go +++ b/pkg/prompts/validations.go @@ -129,6 +129,17 @@ func validateWeight(input string) error { return nil } +func validateBootstrapBalance(input string) error { + val, err := strconv.ParseUint(input, 10, 64) + if err != nil { + return err + } + if val < constants.MinInitialBalanceBootstrapValidator { + return fmt.Errorf("initial bootstrap validator balance must be at least %d", constants.MinInitialBalanceBootstrapValidator) + } + return nil +} + func validateBiggerThanZero(input string) error { val, err := strconv.ParseUint(input, 0, 64) if err != nil { From ee79685b3b75b6450ca823a2e6ad9e5fdbf7c8ec Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 12 Sep 2024 17:59:29 -0400 Subject: [PATCH 08/43] bootstrap validators --- cmd/blockchaincmd/create.go | 33 +++++++++++++++++++++++++--- pkg/prompts/prompts.go | 44 ++++++++++++++++++------------------- pkg/prompts/validations.go | 2 +- 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index f2f306384..7a2892cae 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -117,7 +117,7 @@ configuration, pass the -f flag.`, cmd.Flags().BoolVar(&createFlags.validatorManagerMintOnly, "validator-manager-mint-only", false, "only enable validator manager contract to mint new native tokens") cmd.Flags().StringSliceVar(&createFlags.tokenMinterAddress, "token-minter-address", nil, "addresses that can mint new native tokens (for proof of authority validator management only)") cmd.Flags().StringSliceVar(&createFlags.validatorManagerController, "validator-manager-controller", nil, "addresses that will control Validator Manager contract") - cmd.Flags().IntSliceVar(&createFlags.bootstrapValidatorInitialBalance, "bootstrap-validators-balanche", []int{}, "starting P-Chain balance of each bootstrap validator (minimum of 5 AVAX)") + cmd.Flags().IntSliceVar(&createFlags.bootstrapValidatorInitialBalance, "bootstrap-validators-balance", []int{}, "starting P-Chain balance of each bootstrap validator (minimum of 5 AVAX)") return cmd } @@ -220,6 +220,14 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { } } + if len(createFlags.bootstrapValidatorInitialBalance) > 0 { + for _, balance := range createFlags.bootstrapValidatorInitialBalance { + if balance < constants.MinInitialBalanceBootstrapValidator { + return fmt.Errorf("initial bootstrap validator balance must be at least %d AVAX", constants.MinInitialBalanceBootstrapValidator) + } + } + } + // get vm kind vmType, err := vm.PromptVMType(app, createFlags.useSubnetEvm, createFlags.useCustomVM) if err != nil { @@ -414,9 +422,16 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return err } - if createFlags.bootstrapValidatorInitialBalance == nil { - + if len(createFlags.bootstrapValidatorInitialBalance) == 0 { + createFlags.bootstrapValidatorInitialBalance, err = promptValidatorInitialBalance() + if err != nil { + return err + } } + + ux.Logger.GreenCheckmarkToUser("Number of initial bootstrap validators %d", len(createFlags.bootstrapValidatorInitialBalance)) + ux.Logger.GreenCheckmarkToUser("Initial bootstrap validator balances %d", createFlags.bootstrapValidatorInitialBalance) + if err = app.CreateSidecar(sc); err != nil { return err } @@ -542,6 +557,18 @@ func checkInvalidSubnetNames(name string) error { } return nil } +func promptValidatorInitialBalance() ([]int, error) { + numBootstrapValidators, err := app.Prompt.CaptureInt( + "How many bootstrap validators to set up?", + ) + if err != nil { + return nil, err + } + return app.Prompt.CaptureInitialBalances( + "What are the initial balances of the bootstrap validators (use comma separated values e.g. 5,5)?", + numBootstrapValidators, + ) +} // TODO: add explain the difference for different validator management type func promptValidatorManagementType( diff --git a/pkg/prompts/prompts.go b/pkg/prompts/prompts.go index 1a96374e8..91b2540e0 100644 --- a/pkg/prompts/prompts.go +++ b/pkg/prompts/prompts.go @@ -114,6 +114,7 @@ type Prompter interface { CaptureXChainAddress(promptStr string, network models.Network) (string, error) CaptureFutureDate(promptStr string, minDate time.Time) (time.Time, error) ChooseKeyOrLedger(goal string) (bool, error) + CaptureInitialBalances(promptStr string, numValidators int) ([]int, error) } type realPrompter struct{} @@ -483,33 +484,32 @@ func (*realPrompter) CaptureAddresses(promptStr string) ([]common.Address, error return addresses, nil } -func (*realPrompter) CaptureInitialBalances(promptStr string, minBalance int) ([]int, error) { - addressesStr := "" - validated := false - for !validated { - var err error - addressesStr, err = utils.ReadLongString(promptui.IconGood + " " + promptStr + " ") - if err != nil { - return nil, err - } - if err := validateAddresses(addressesStr); err != nil { - fmt.Println(err) - } else { - validated = true - } - } - +func (*realPrompter) CaptureInitialBalances(promptStr string, numValidators int) ([]int, error) { prompt := promptui.Prompt{ - Label: promptStr, - Validate: validateBootstrapBalance, + Label: promptStr, } - addressStr, err := prompt.Run() + balanceStr, err := prompt.Run() if err != nil { - return common.Address{}, err + return nil, err } - initialBalances, err := strings.Split(addressesStr, ","), - return addresses, nil + + initialBalances := strings.Split(balanceStr, ",") + if len(initialBalances) != numValidators { + return nil, fmt.Errorf("number of initial balances provided does not match number of bootstrap validators") + } + validatorBalances := []int{} + for _, balance := range initialBalances { + if err = validateBootstrapBalance(balance); err != nil { + return nil, err + } + balanceInt, err := strconv.Atoi(balance) + if err != nil { + return nil, err + } + validatorBalances = append(validatorBalances, balanceInt) + } + return validatorBalances, nil } func (*realPrompter) CaptureExistingFilepath(promptStr string) (string, error) { diff --git a/pkg/prompts/validations.go b/pkg/prompts/validations.go index b901a9b03..48438851b 100644 --- a/pkg/prompts/validations.go +++ b/pkg/prompts/validations.go @@ -135,7 +135,7 @@ func validateBootstrapBalance(input string) error { return err } if val < constants.MinInitialBalanceBootstrapValidator { - return fmt.Errorf("initial bootstrap validator balance must be at least %d", constants.MinInitialBalanceBootstrapValidator) + return fmt.Errorf("initial bootstrap validator balance must be at least %d AVAX", constants.MinInitialBalanceBootstrapValidator) } return nil } From f586cb639dbf6c1fc123bdb6a3500e36d28c1de2 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 12 Sep 2024 18:52:08 -0400 Subject: [PATCH 09/43] add mock --- internal/mocks/prompter.go | 181 +++++++------------------------------ 1 file changed, 35 insertions(+), 146 deletions(-) diff --git a/internal/mocks/prompter.go b/internal/mocks/prompter.go index 0d38e7b05..6cb81ac1c 100644 --- a/internal/mocks/prompter.go +++ b/internal/mocks/prompter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.1. DO NOT EDIT. +// Code generated by mockery v2.26.1. DO NOT EDIT. package mocks @@ -28,10 +28,6 @@ type Prompter struct { func (_m *Prompter) CaptureAddress(promptStr string) (common.Address, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureAddress") - } - var r0 common.Address var r1 error if rf, ok := ret.Get(0).(func(string) (common.Address, error)); ok { @@ -58,10 +54,6 @@ func (_m *Prompter) CaptureAddress(promptStr string) (common.Address, error) { func (_m *Prompter) CaptureAddresses(promptStr string) ([]common.Address, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureList") - } - var r0 []common.Address var r1 error if rf, ok := ret.Get(0).(func(string) ([]common.Address, error)); ok { @@ -70,7 +62,9 @@ func (_m *Prompter) CaptureAddresses(promptStr string) ([]common.Address, error) if rf, ok := ret.Get(0).(func(string) []common.Address); ok { r0 = rf(promptStr) } else { - r0 = ret.Get(0).([]common.Address) + if ret.Get(0) != nil { + r0 = ret.Get(0).([]common.Address) + } } if rf, ok := ret.Get(1).(func(string) error); ok { @@ -86,10 +80,6 @@ func (_m *Prompter) CaptureAddresses(promptStr string) ([]common.Address, error) func (_m *Prompter) CaptureDate(promptStr string) (time.Time, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureDate") - } - var r0 time.Time var r1 error if rf, ok := ret.Get(0).(func(string) (time.Time, error)); ok { @@ -114,10 +104,6 @@ func (_m *Prompter) CaptureDate(promptStr string) (time.Time, error) { func (_m *Prompter) CaptureEmail(promptStr string) (string, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureEmail") - } - var r0 string var r1 error if rf, ok := ret.Get(0).(func(string) (string, error)); ok { @@ -142,10 +128,6 @@ func (_m *Prompter) CaptureEmail(promptStr string) (string, error) { func (_m *Prompter) CaptureExistingFilepath(promptStr string) (string, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureExistingFilepath") - } - var r0 string var r1 error if rf, ok := ret.Get(0).(func(string) (string, error)); ok { @@ -170,10 +152,6 @@ func (_m *Prompter) CaptureExistingFilepath(promptStr string) (string, error) { func (_m *Prompter) CaptureFloat(promptStr string, validator func(float64) error) (float64, error) { ret := _m.Called(promptStr, validator) - if len(ret) == 0 { - panic("no return value specified for CaptureFloat") - } - var r0 float64 var r1 error if rf, ok := ret.Get(0).(func(string, func(float64) error) (float64, error)); ok { @@ -198,10 +176,6 @@ func (_m *Prompter) CaptureFloat(promptStr string, validator func(float64) error func (_m *Prompter) CaptureFujiDuration(promptStr string) (time.Duration, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureFujiDuration") - } - var r0 time.Duration var r1 error if rf, ok := ret.Get(0).(func(string) (time.Duration, error)); ok { @@ -226,10 +200,6 @@ func (_m *Prompter) CaptureFujiDuration(promptStr string) (time.Duration, error) func (_m *Prompter) CaptureFutureDate(promptStr string, minDate time.Time) (time.Time, error) { ret := _m.Called(promptStr, minDate) - if len(ret) == 0 { - panic("no return value specified for CaptureFutureDate") - } - var r0 time.Time var r1 error if rf, ok := ret.Get(0).(func(string, time.Time) (time.Time, error)); ok { @@ -254,10 +224,6 @@ func (_m *Prompter) CaptureFutureDate(promptStr string, minDate time.Time) (time func (_m *Prompter) CaptureGitURL(promptStr string) (*url.URL, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureGitURL") - } - var r0 *url.URL var r1 error if rf, ok := ret.Get(0).(func(string) (*url.URL, error)); ok { @@ -284,10 +250,6 @@ func (_m *Prompter) CaptureGitURL(promptStr string) (*url.URL, error) { func (_m *Prompter) CaptureID(promptStr string) (ids.ID, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureID") - } - var r0 ids.ID var r1 error if rf, ok := ret.Get(0).(func(string) (ids.ID, error)); ok { @@ -314,10 +276,6 @@ func (_m *Prompter) CaptureID(promptStr string) (ids.ID, error) { func (_m *Prompter) CaptureIndex(promptStr string, options []interface{}) (int, error) { ret := _m.Called(promptStr, options) - if len(ret) == 0 { - panic("no return value specified for CaptureIndex") - } - var r0 int var r1 error if rf, ok := ret.Get(0).(func(string, []interface{}) (int, error)); ok { @@ -338,14 +296,36 @@ func (_m *Prompter) CaptureIndex(promptStr string, options []interface{}) (int, return r0, r1 } +// CaptureInitialBalances provides a mock function with given fields: promptStr, numValidators +func (_m *Prompter) CaptureInitialBalances(promptStr string, numValidators int) ([]int, error) { + ret := _m.Called(promptStr, numValidators) + + var r0 []int + var r1 error + if rf, ok := ret.Get(0).(func(string, int) ([]int, error)); ok { + return rf(promptStr, numValidators) + } + if rf, ok := ret.Get(0).(func(string, int) []int); ok { + r0 = rf(promptStr, numValidators) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]int) + } + } + + if rf, ok := ret.Get(1).(func(string, int) error); ok { + r1 = rf(promptStr, numValidators) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // CaptureInt provides a mock function with given fields: promptStr func (_m *Prompter) CaptureInt(promptStr string) (int, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureInt") - } - var r0 int var r1 error if rf, ok := ret.Get(0).(func(string) (int, error)); ok { @@ -370,10 +350,6 @@ func (_m *Prompter) CaptureInt(promptStr string) (int, error) { func (_m *Prompter) CaptureList(promptStr string, options []string) (string, error) { ret := _m.Called(promptStr, options) - if len(ret) == 0 { - panic("no return value specified for CaptureList") - } - var r0 string var r1 error if rf, ok := ret.Get(0).(func(string, []string) (string, error)); ok { @@ -398,10 +374,6 @@ func (_m *Prompter) CaptureList(promptStr string, options []string) (string, err func (_m *Prompter) CaptureListWithSize(promptStr string, options []string, size int) (string, error) { ret := _m.Called(promptStr, options, size) - if len(ret) == 0 { - panic("no return value specified for CaptureListWithSize") - } - var r0 string var r1 error if rf, ok := ret.Get(0).(func(string, []string, int) (string, error)); ok { @@ -426,10 +398,6 @@ func (_m *Prompter) CaptureListWithSize(promptStr string, options []string, size func (_m *Prompter) CaptureMainnetDuration(promptStr string) (time.Duration, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureMainnetDuration") - } - var r0 time.Duration var r1 error if rf, ok := ret.Get(0).(func(string) (time.Duration, error)); ok { @@ -454,10 +422,6 @@ func (_m *Prompter) CaptureMainnetDuration(promptStr string) (time.Duration, err func (_m *Prompter) CaptureNewFilepath(promptStr string) (string, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureNewFilepath") - } - var r0 string var r1 error if rf, ok := ret.Get(0).(func(string) (string, error)); ok { @@ -482,10 +446,6 @@ func (_m *Prompter) CaptureNewFilepath(promptStr string) (string, error) { func (_m *Prompter) CaptureNoYes(promptStr string) (bool, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureNoYes") - } - var r0 bool var r1 error if rf, ok := ret.Get(0).(func(string) (bool, error)); ok { @@ -510,10 +470,6 @@ func (_m *Prompter) CaptureNoYes(promptStr string) (bool, error) { func (_m *Prompter) CaptureNodeID(promptStr string) (ids.NodeID, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureNodeID") - } - var r0 ids.NodeID var r1 error if rf, ok := ret.Get(0).(func(string) (ids.NodeID, error)); ok { @@ -540,10 +496,6 @@ func (_m *Prompter) CaptureNodeID(promptStr string) (ids.NodeID, error) { func (_m *Prompter) CapturePChainAddress(promptStr string, network models.Network) (string, error) { ret := _m.Called(promptStr, network) - if len(ret) == 0 { - panic("no return value specified for CapturePChainAddress") - } - var r0 string var r1 error if rf, ok := ret.Get(0).(func(string, models.Network) (string, error)); ok { @@ -568,10 +520,6 @@ func (_m *Prompter) CapturePChainAddress(promptStr string, network models.Networ func (_m *Prompter) CapturePositiveBigInt(promptStr string) (*big.Int, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CapturePositiveBigInt") - } - var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(string) (*big.Int, error)); ok { @@ -598,10 +546,6 @@ func (_m *Prompter) CapturePositiveBigInt(promptStr string) (*big.Int, error) { func (_m *Prompter) CapturePositiveInt(promptStr string, comparators []prompts.Comparator) (int, error) { ret := _m.Called(promptStr, comparators) - if len(ret) == 0 { - panic("no return value specified for CapturePositiveInt") - } - var r0 int var r1 error if rf, ok := ret.Get(0).(func(string, []prompts.Comparator) (int, error)); ok { @@ -626,10 +570,6 @@ func (_m *Prompter) CapturePositiveInt(promptStr string, comparators []prompts.C func (_m *Prompter) CaptureRepoBranch(promptStr string, repo string) (string, error) { ret := _m.Called(promptStr, repo) - if len(ret) == 0 { - panic("no return value specified for CaptureRepoBranch") - } - var r0 string var r1 error if rf, ok := ret.Get(0).(func(string, string) (string, error)); ok { @@ -654,10 +594,6 @@ func (_m *Prompter) CaptureRepoBranch(promptStr string, repo string) (string, er func (_m *Prompter) CaptureRepoFile(promptStr string, repo string, branch string) (string, error) { ret := _m.Called(promptStr, repo, branch) - if len(ret) == 0 { - panic("no return value specified for CaptureRepoFile") - } - var r0 string var r1 error if rf, ok := ret.Get(0).(func(string, string, string) (string, error)); ok { @@ -682,10 +618,6 @@ func (_m *Prompter) CaptureRepoFile(promptStr string, repo string, branch string func (_m *Prompter) CaptureString(promptStr string) (string, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureString") - } - var r0 string var r1 error if rf, ok := ret.Get(0).(func(string) (string, error)); ok { @@ -710,10 +642,6 @@ func (_m *Prompter) CaptureString(promptStr string) (string, error) { func (_m *Prompter) CaptureStringAllowEmpty(promptStr string) (string, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureStringAllowEmpty") - } - var r0 string var r1 error if rf, ok := ret.Get(0).(func(string) (string, error)); ok { @@ -738,10 +666,6 @@ func (_m *Prompter) CaptureStringAllowEmpty(promptStr string) (string, error) { func (_m *Prompter) CaptureURL(promptStr string, validateConnection bool) (string, error) { ret := _m.Called(promptStr, validateConnection) - if len(ret) == 0 { - panic("no return value specified for CaptureURL") - } - var r0 string var r1 error if rf, ok := ret.Get(0).(func(string, bool) (string, error)); ok { @@ -766,10 +690,6 @@ func (_m *Prompter) CaptureURL(promptStr string, validateConnection bool) (strin func (_m *Prompter) CaptureUint32(promptStr string) (uint32, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureUint32") - } - var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(string) (uint32, error)); ok { @@ -794,10 +714,6 @@ func (_m *Prompter) CaptureUint32(promptStr string) (uint32, error) { func (_m *Prompter) CaptureUint64(promptStr string) (uint64, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureUint64") - } - var r0 uint64 var r1 error if rf, ok := ret.Get(0).(func(string) (uint64, error)); ok { @@ -822,10 +738,6 @@ func (_m *Prompter) CaptureUint64(promptStr string) (uint64, error) { func (_m *Prompter) CaptureUint64Compare(promptStr string, comparators []prompts.Comparator) (uint64, error) { ret := _m.Called(promptStr, comparators) - if len(ret) == 0 { - panic("no return value specified for CaptureUint64Compare") - } - var r0 uint64 var r1 error if rf, ok := ret.Get(0).(func(string, []prompts.Comparator) (uint64, error)); ok { @@ -850,10 +762,6 @@ func (_m *Prompter) CaptureUint64Compare(promptStr string, comparators []prompts func (_m *Prompter) CaptureValidatedString(promptStr string, validator func(string) error) (string, error) { ret := _m.Called(promptStr, validator) - if len(ret) == 0 { - panic("no return value specified for CaptureValidatedString") - } - var r0 string var r1 error if rf, ok := ret.Get(0).(func(string, func(string) error) (string, error)); ok { @@ -878,10 +786,6 @@ func (_m *Prompter) CaptureValidatedString(promptStr string, validator func(stri func (_m *Prompter) CaptureVersion(promptStr string) (string, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureVersion") - } - var r0 string var r1 error if rf, ok := ret.Get(0).(func(string) (string, error)); ok { @@ -906,10 +810,6 @@ func (_m *Prompter) CaptureVersion(promptStr string) (string, error) { func (_m *Prompter) CaptureWeight(promptStr string) (uint64, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureWeight") - } - var r0 uint64 var r1 error if rf, ok := ret.Get(0).(func(string) (uint64, error)); ok { @@ -934,10 +834,6 @@ func (_m *Prompter) CaptureWeight(promptStr string) (uint64, error) { func (_m *Prompter) CaptureXChainAddress(promptStr string, network models.Network) (string, error) { ret := _m.Called(promptStr, network) - if len(ret) == 0 { - panic("no return value specified for CaptureXChainAddress") - } - var r0 string var r1 error if rf, ok := ret.Get(0).(func(string, models.Network) (string, error)); ok { @@ -962,10 +858,6 @@ func (_m *Prompter) CaptureXChainAddress(promptStr string, network models.Networ func (_m *Prompter) CaptureYesNo(promptStr string) (bool, error) { ret := _m.Called(promptStr) - if len(ret) == 0 { - panic("no return value specified for CaptureYesNo") - } - var r0 bool var r1 error if rf, ok := ret.Get(0).(func(string) (bool, error)); ok { @@ -990,10 +882,6 @@ func (_m *Prompter) CaptureYesNo(promptStr string) (bool, error) { func (_m *Prompter) ChooseKeyOrLedger(goal string) (bool, error) { ret := _m.Called(goal) - if len(ret) == 0 { - panic("no return value specified for ChooseKeyOrLedger") - } - var r0 bool var r1 error if rf, ok := ret.Get(0).(func(string) (bool, error)); ok { @@ -1014,12 +902,13 @@ func (_m *Prompter) ChooseKeyOrLedger(goal string) (bool, error) { return r0, r1 } -// NewPrompter creates a new instance of Prompter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewPrompter(t interface { +type mockConstructorTestingTNewPrompter interface { mock.TestingT Cleanup(func()) -}) *Prompter { +} + +// NewPrompter creates a new instance of Prompter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewPrompter(t mockConstructorTestingTNewPrompter) *Prompter { mock := &Prompter{} mock.Mock.Test(t) From d15757ccc64d8174ed2fcefc66d004a3ba093278 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 12 Sep 2024 18:54:56 -0400 Subject: [PATCH 10/43] fix lint --- cmd/blockchaincmd/create.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 7a2892cae..0112187e6 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -557,6 +557,7 @@ func checkInvalidSubnetNames(name string) error { } return nil } + func promptValidatorInitialBalance() ([]int, error) { numBootstrapValidators, err := app.Prompt.CaptureInt( "How many bootstrap validators to set up?", From 059519d154a309090c0ba538d2de566cfb870c4a Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 13 Sep 2024 19:00:43 -0400 Subject: [PATCH 11/43] update prompt validator --- cmd/blockchaincmd/create.go | 91 +++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 0112187e6..31db2a631 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -6,6 +6,9 @@ import ( "encoding/json" "errors" "fmt" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/platformvm/fx" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" "os" "sort" "strconv" @@ -558,6 +561,94 @@ func checkInvalidSubnetNames(name string) error { return nil } +// TODO: replace this object with avalanchego struct SubnetValidator +type SubnetValidator struct { + // Must be Ed25519 NodeID + NodeID ids.NodeID + // Weight of this validator used when sampling + Weight uint64 + // Initial balance for this validator + Balance uint64 + // [Signer] is the BLS key for this validator. + // Note: We do not enforce that the BLS key is unique across all validators. + // This means that validators can share a key if they so choose. + // However, a NodeID + Subnet does uniquely map to a BLS key + Signer signer.Signer + // Leftover $AVAX from the [Balance] will be issued to this + // owner once it is removed from the validator set. + ChangeOwner fx.Owner +} + +func PromptWeightPrimaryNetwork(network models.Network) (uint64, error) { + defaultStake := network.GenesisParams().MinValidatorStake + defaultWeight := fmt.Sprintf("Default (%s)", convertNanoAvaxToAvaxString(defaultStake)) + txt := "What stake weight would you like to assign to the validator?" + weightOptions := []string{defaultWeight, "Custom"} + weightOption, err := app.Prompt.CaptureList(txt, weightOptions) + if err != nil { + return 0, err + } + + switch weightOption { + case defaultWeight: + return defaultStake, nil + default: + return app.Prompt.CaptureWeight(txt) + } +} + +func promptValidators() ([]int, error) { + subnetValidators := []SubnetValidator{} + numBootstrapValidators, err := app.Prompt.CaptureInt( + "How many bootstrap validators to set up?", + ) + for len(subnetValidators) < numBootstrapValidators { + nodeID, err := PromptNodeID() + if err != nil { + return err + } + weight, err := PromptWeightPrimaryNetwork(network) + if err != nil { + return err + } + balance, err := PromptBalance() + if err != nil { + return err + } + jsonPop, err := promptProofOfPossession() + if err != nil { + return err + } + popBytes, err := json.Marshal(jsonPop) + if err != nil { + return err + } + var proofOfPossession signer.Signer + pop := &signer.ProofOfPossession{} + err := pop.UnmarshalJSON(popBytes) + if err != nil { + return ids.Empty, err + } + proofOfPossession = pop + changeAddr, err := PromptChangeAddr() + if err != nil { + return err + } + subnetValidator := SubnetValidator{ + NodeID: nodeID, + Weight: weight, + Balance: balance, + Signer: proofOfPossession, + ChangeOwner: changeAddr, + } + subnetValidators = append(subnetValidators, subnetValidator) + } + if err != nil { + return nil, err + } + +} + func promptValidatorInitialBalance() ([]int, error) { numBootstrapValidators, err := app.Prompt.CaptureInt( "How many bootstrap validators to set up?", From 4809209e4611ae5f19ceddb894c2402cc1224fc1 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 13 Sep 2024 19:02:42 -0400 Subject: [PATCH 12/43] update prompt validator --- cmd/blockchaincmd/create.go | 60 +++++++++++++------------------------ 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 31db2a631..7a832701e 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -38,24 +38,24 @@ const ( ) type CreateFlags struct { - useSubnetEvm bool - useCustomVM bool - chainID uint64 - tokenSymbol string - useTestDefaults bool - useProductionDefaults bool - useWarp bool - useTeleporter bool - vmVersion string - useLatestReleasedVMVersion bool - useLatestPreReleasedVMVersion bool - useExternalGasToken bool - proofOfStake bool - proofOfAuthority bool - validatorManagerMintOnly bool - tokenMinterAddress []string - validatorManagerController []string - bootstrapValidatorInitialBalance []int + useSubnetEvm bool + useCustomVM bool + chainID uint64 + tokenSymbol string + useTestDefaults bool + useProductionDefaults bool + useWarp bool + useTeleporter bool + vmVersion string + useLatestReleasedVMVersion bool + useLatestPreReleasedVMVersion bool + useExternalGasToken bool + proofOfStake bool + proofOfAuthority bool + validatorManagerMintOnly bool + tokenMinterAddress []string + validatorManagerController []string + bootstrapValidators []SubnetValidator } var ( @@ -120,7 +120,6 @@ configuration, pass the -f flag.`, cmd.Flags().BoolVar(&createFlags.validatorManagerMintOnly, "validator-manager-mint-only", false, "only enable validator manager contract to mint new native tokens") cmd.Flags().StringSliceVar(&createFlags.tokenMinterAddress, "token-minter-address", nil, "addresses that can mint new native tokens (for proof of authority validator management only)") cmd.Flags().StringSliceVar(&createFlags.validatorManagerController, "validator-manager-controller", nil, "addresses that will control Validator Manager contract") - cmd.Flags().IntSliceVar(&createFlags.bootstrapValidatorInitialBalance, "bootstrap-validators-balance", []int{}, "starting P-Chain balance of each bootstrap validator (minimum of 5 AVAX)") return cmd } @@ -425,12 +424,8 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return err } - if len(createFlags.bootstrapValidatorInitialBalance) == 0 { - createFlags.bootstrapValidatorInitialBalance, err = promptValidatorInitialBalance() - if err != nil { - return err - } - } + subnetValidators, err := promptValidators() + //TODO: update subnetvalidators in sidecar ux.Logger.GreenCheckmarkToUser("Number of initial bootstrap validators %d", len(createFlags.bootstrapValidatorInitialBalance)) ux.Logger.GreenCheckmarkToUser("Initial bootstrap validator balances %d", createFlags.bootstrapValidatorInitialBalance) @@ -597,7 +592,7 @@ func PromptWeightPrimaryNetwork(network models.Network) (uint64, error) { } } -func promptValidators() ([]int, error) { +func promptValidators() ([]SubnetValidator, error) { subnetValidators := []SubnetValidator{} numBootstrapValidators, err := app.Prompt.CaptureInt( "How many bootstrap validators to set up?", @@ -649,19 +644,6 @@ func promptValidators() ([]int, error) { } -func promptValidatorInitialBalance() ([]int, error) { - numBootstrapValidators, err := app.Prompt.CaptureInt( - "How many bootstrap validators to set up?", - ) - if err != nil { - return nil, err - } - return app.Prompt.CaptureInitialBalances( - "What are the initial balances of the bootstrap validators (use comma separated values e.g. 5,5)?", - numBootstrapValidators, - ) -} - // TODO: add explain the difference for different validator management type func promptValidatorManagementType( app *application.Avalanche, From e87b8f18488b1771cd3a97e8aba43ddddc162ba9 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 16 Sep 2024 18:11:44 -0400 Subject: [PATCH 13/43] implement prompts --- cmd/blockchaincmd/create.go | 123 +++++++++++++++++--------- cmd/blockchaincmd/prompt_owners.go | 81 +++++++++++++++++ pkg/networkoptions/network_options.go | 29 +++++- pkg/prompts/prompts.go | 44 ++++----- 4 files changed, 205 insertions(+), 72 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 7a832701e..63b9c6c73 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -6,9 +6,12 @@ import ( "encoding/json" "errors" "fmt" + "github.com/ava-labs/avalanche-cli/pkg/prompts" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/formatting/address" "github.com/ava-labs/avalanchego/vms/platformvm/fx" "github.com/ava-labs/avalanchego/vms/platformvm/signer" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" "os" "sort" "strconv" @@ -222,13 +225,13 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { } } - if len(createFlags.bootstrapValidatorInitialBalance) > 0 { - for _, balance := range createFlags.bootstrapValidatorInitialBalance { - if balance < constants.MinInitialBalanceBootstrapValidator { - return fmt.Errorf("initial bootstrap validator balance must be at least %d AVAX", constants.MinInitialBalanceBootstrapValidator) - } - } - } + //if len(createFlags.bootstrapValidatorInitialBalance) > 0 { + // for _, balance := range createFlags.bootstrapValidatorInitialBalance { + // if balance < constants.MinInitialBalanceBootstrapValidator { + // return fmt.Errorf("initial bootstrap validator balance must be at least %d AVAX", constants.MinInitialBalanceBootstrapValidator) + // } + // } + //} // get vm kind vmType, err := vm.PromptVMType(app, createFlags.useSubnetEvm, createFlags.useCustomVM) @@ -424,12 +427,9 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return err } - subnetValidators, err := promptValidators() + _, err = promptValidators() //TODO: update subnetvalidators in sidecar - ux.Logger.GreenCheckmarkToUser("Number of initial bootstrap validators %d", len(createFlags.bootstrapValidatorInitialBalance)) - ux.Logger.GreenCheckmarkToUser("Initial bootstrap validator balances %d", createFlags.bootstrapValidatorInitialBalance) - if err = app.CreateSidecar(sc); err != nil { return err } @@ -574,74 +574,115 @@ type SubnetValidator struct { ChangeOwner fx.Owner } -func PromptWeightPrimaryNetwork(network models.Network) (uint64, error) { - defaultStake := network.GenesisParams().MinValidatorStake - defaultWeight := fmt.Sprintf("Default (%s)", convertNanoAvaxToAvaxString(defaultStake)) +// TODO: find the min weight for bootstrap validator +func PromptWeightBootstrapValidator() (uint64, error) { txt := "What stake weight would you like to assign to the validator?" - weightOptions := []string{defaultWeight, "Custom"} + return app.Prompt.CaptureWeight(txt) +} + +func PromptInitialBalance() (uint64, error) { + defaultInitialBalance := fmt.Sprintf("Default (%d) AVAX", constants.MinInitialBalanceBootstrapValidator) + txt := "What initial balance would you like to assign to the bootstrap validator (in AVAX)?" + weightOptions := []string{defaultInitialBalance, "Custom"} weightOption, err := app.Prompt.CaptureList(txt, weightOptions) if err != nil { return 0, err } switch weightOption { - case defaultWeight: - return defaultStake, nil + case defaultInitialBalance: + return constants.MinInitialBalanceBootstrapValidator, nil default: - return app.Prompt.CaptureWeight(txt) + return app.Prompt.CaptureBootstrapInitialBalance(txt) } } func promptValidators() ([]SubnetValidator, error) { - subnetValidators := []SubnetValidator{} + var subnetValidators []SubnetValidator numBootstrapValidators, err := app.Prompt.CaptureInt( - "How many bootstrap validators to set up?", + "How many bootstrap validators do you want to set up?", ) + if err != nil { + return nil, err + } + previousAddr := "" for len(subnetValidators) < numBootstrapValidators { nodeID, err := PromptNodeID() if err != nil { - return err + return nil, err } - weight, err := PromptWeightPrimaryNetwork(network) + weight, err := PromptWeightBootstrapValidator() if err != nil { - return err + return nil, err } - balance, err := PromptBalance() + balance, err := PromptInitialBalance() if err != nil { - return err + return nil, err } - jsonPop, err := promptProofOfPossession() + proofOfPossession, err := promptProofOfPossession() + changeAddr, err := getKeyForChangeOwner(previousAddr) if err != nil { - return err + return nil, err } - popBytes, err := json.Marshal(jsonPop) + addrs, err := address.ParseToIDs([]string{changeAddr}) if err != nil { - return err + return nil, fmt.Errorf("failure parsing change owner address: %w", err) } - var proofOfPossession signer.Signer - pop := &signer.ProofOfPossession{} - err := pop.UnmarshalJSON(popBytes) - if err != nil { - return ids.Empty, err - } - proofOfPossession = pop - changeAddr, err := PromptChangeAddr() - if err != nil { - return err + changeOwner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: addrs, } + previousAddr = changeAddr subnetValidator := SubnetValidator{ NodeID: nodeID, Weight: weight, Balance: balance, Signer: proofOfPossession, - ChangeOwner: changeAddr, + ChangeOwner: changeOwner, } subnetValidators = append(subnetValidators, subnetValidator) } + return subnetValidators, nil +} + +func validateProofOfPossession(publicKey, pop string) { + if publicKey != "" { + err := prompts.ValidateHexa(publicKey) + if err != nil { + ux.Logger.PrintToUser("Format error in given public key: %s", err) + publicKey = "" + } + } + if pop != "" { + err := prompts.ValidateHexa(pop) + if err != nil { + ux.Logger.PrintToUser("Format error in given proof of possession: %s", err) + pop = "" + } + } +} + +func promptProofOfPossession() (signer.Signer, error) { + ux.Logger.PrintToUser("Next, we need the public key and proof of possession of the node's BLS") + ux.Logger.PrintToUser("Check https://docs.avax.network/api-reference/info-api#infogetnodeid for instructions on calling info.getNodeID API") + var err error + txt := "What is the public key of the node's BLS?" + publicKey, err := app.Prompt.CaptureValidatedString(txt, prompts.ValidateHexa) if err != nil { return nil, err } - + txt = "What is the proof of possession of the node's BLS?" + proofOfPossesion, err := app.Prompt.CaptureValidatedString(txt, prompts.ValidateHexa) + if err != nil { + return nil, err + } + var proofOfPossession signer.Signer + pop := &signer.ProofOfPossession{ + PublicKey: [48]byte([]byte(publicKey)), + ProofOfPossession: [96]byte([]byte(proofOfPossesion)), + } + proofOfPossession = pop + return proofOfPossession, nil } // TODO: add explain the difference for different validator management type diff --git a/cmd/blockchaincmd/prompt_owners.go b/cmd/blockchaincmd/prompt_owners.go index a5c87a905..64d7aefd5 100644 --- a/cmd/blockchaincmd/prompt_owners.go +++ b/cmd/blockchaincmd/prompt_owners.go @@ -4,6 +4,8 @@ package blockchaincmd import ( "fmt" + "github.com/ava-labs/avalanche-cli/pkg/networkoptions" + "github.com/ava-labs/avalanche-cli/pkg/utils" "os" "path/filepath" "strconv" @@ -305,3 +307,82 @@ func getThreshold(maxLen int) (uint32, error) { } return uint32(intTh), err } + +func getKeyForChangeOwner(previouslyUsedAddr string) (string, error) { + moreKeysPrompt := "Which key would you like to set as change owner for leftover AVAX if the node is removed from validator set?" + + const ( + getFromStored = "Get address from an existing stored key (created from avalanche key create or avalanche key import)" + custom = "Custom" + ) + previousAddres := fmt.Sprintf("Previously used address %s", previouslyUsedAddr) + + listOptions := []string{getFromStored, custom} + if previouslyUsedAddr != "" { + listOptions = []string{previousAddres, getFromStored, custom} + } + listDecision, err := app.Prompt.CaptureList(moreKeysPrompt, listOptions) + if err != nil { + return "", err + } + + var key string + + switch listDecision { + case previousAddres: + key = previouslyUsedAddr + case getFromStored: + network, err := promptNetwork() + if err != nil { + return "", err + } + key, err = prompts.CaptureKeyAddress( + app.Prompt, + "be set as a change owner for leftover AVAX", + app.GetKeyDir(), + app.GetKey, + network, + prompts.PChainFormat, + ) + if err != nil { + return "", err + } + case custom: + addrPrompt := "Enter change address (P-chain format)" + changeAddr, err := app.Prompt.CaptureAddress(addrPrompt) + if err != nil { + return "", err + } + key = changeAddr.String() + } + if err != nil { + return "", err + } + return key, nil +} + +func promptNetwork() (models.Network, error) { + promptStr := "Choose a network to get the key from" + supportedNetworkOptionsToPrompt := []networkoptions.NetworkOption{networkoptions.Local, networkoptions.Devnet, networkoptions.Fuji, networkoptions.Mainnet} + + networkOptionStr, err := app.Prompt.CaptureList( + promptStr, + utils.Map(supportedNetworkOptionsToPrompt, func(n networkoptions.NetworkOption) string { return n.String() }), + ) + if err != nil { + return models.UndefinedNetwork, err + } + networkOption := networkoptions.NetworkOptionFromString(networkOptionStr) + network := models.UndefinedNetwork + if networkOption == networkoptions.Devnet { + endpoint, err := app.Prompt.CaptureURL(fmt.Sprintf("%s Endpoint", networkOption.String()), false) + if err != nil { + return models.UndefinedNetwork, err + } + network, err = networkOption.ModelNetwork(endpoint) + if err != nil { + return models.UndefinedNetwork, err + } + } + return network, nil +} diff --git a/pkg/networkoptions/network_options.go b/pkg/networkoptions/network_options.go index 9cbef5ec2..3eeb9f394 100644 --- a/pkg/networkoptions/network_options.go +++ b/pkg/networkoptions/network_options.go @@ -46,7 +46,7 @@ func (n NetworkOption) String() string { return "invalid network" } -func networkOptionFromString(s string) NetworkOption { +func NetworkOptionFromString(s string) NetworkOption { switch s { case "Mainnet": return Mainnet @@ -62,6 +62,31 @@ func networkOptionFromString(s string) NetworkOption { return Undefined } +func (n NetworkOption) ModelNetwork(devnetEndpoint string) (models.Network, error) { + network := models.UndefinedNetwork + switch n { + case Local: + network = models.NewLocalNetwork() + case Devnet: + networkID := uint32(0) + if devnetEndpoint != "" { + infoClient := info.NewClient(devnetEndpoint) + ctx, cancel := utils.GetAPIContext() + defer cancel() + _, err := infoClient.GetNetworkID(ctx) + if err != nil { + return models.UndefinedNetwork, err + } + } + network = models.NewDevnetNetwork(devnetEndpoint, networkID) + case Fuji: + network = models.NewFujiNetwork() + case Mainnet: + network = models.NewMainnetNetwork() + } + return network, nil +} + type NetworkFlags struct { UseLocal bool UseDevnet bool @@ -283,7 +308,7 @@ func GetNetworkFromCmdLineFlags( if err != nil { return models.UndefinedNetwork, err } - networkOption = networkOptionFromString(networkOptionStr) + networkOption = NetworkOptionFromString(networkOptionStr) if networkOption == Devnet && !onlyEndpointBasedDevnets && len(clusterNames) != 0 { endpointOptions := []string{ "Get Devnet RPC endpoint from an existing node cluster (created from avalanche node create or avalanche devnet wiz)", diff --git a/pkg/prompts/prompts.go b/pkg/prompts/prompts.go index 91b2540e0..2006eecb5 100644 --- a/pkg/prompts/prompts.go +++ b/pkg/prompts/prompts.go @@ -104,6 +104,7 @@ type Prompter interface { CaptureNodeID(promptStr string) (ids.NodeID, error) CaptureID(promptStr string) (ids.ID, error) CaptureWeight(promptStr string) (uint64, error) + CaptureBootstrapInitialBalance(promptStr string) (uint64, error) CapturePositiveInt(promptStr string, comparators []Comparator) (int, error) CaptureInt(promptStr string) (int, error) CaptureUint32(promptStr string) (uint32, error) @@ -114,7 +115,6 @@ type Prompter interface { CaptureXChainAddress(promptStr string, network models.Network) (string, error) CaptureFutureDate(promptStr string, minDate time.Time) (time.Time, error) ChooseKeyOrLedger(goal string) (bool, error) - CaptureInitialBalances(promptStr string, numValidators int) ([]int, error) } type realPrompter struct{} @@ -266,6 +266,20 @@ func (*realPrompter) CaptureNodeID(promptStr string) (ids.NodeID, error) { return ids.NodeIDFromString(nodeIDStr) } +func (*realPrompter) CaptureBootstrapInitialBalance(promptStr string) (uint64, error) { + prompt := promptui.Prompt{ + Label: promptStr, + Validate: validateBootstrapBalance, + } + + amountStr, err := prompt.Run() + if err != nil { + return 0, err + } + + return strconv.ParseUint(amountStr, 10, 64) +} + func (*realPrompter) CaptureWeight(promptStr string) (uint64, error) { prompt := promptui.Prompt{ Label: promptStr, @@ -484,34 +498,6 @@ func (*realPrompter) CaptureAddresses(promptStr string) ([]common.Address, error return addresses, nil } -func (*realPrompter) CaptureInitialBalances(promptStr string, numValidators int) ([]int, error) { - prompt := promptui.Prompt{ - Label: promptStr, - } - - balanceStr, err := prompt.Run() - if err != nil { - return nil, err - } - - initialBalances := strings.Split(balanceStr, ",") - if len(initialBalances) != numValidators { - return nil, fmt.Errorf("number of initial balances provided does not match number of bootstrap validators") - } - validatorBalances := []int{} - for _, balance := range initialBalances { - if err = validateBootstrapBalance(balance); err != nil { - return nil, err - } - balanceInt, err := strconv.Atoi(balance) - if err != nil { - return nil, err - } - validatorBalances = append(validatorBalances, balanceInt) - } - return validatorBalances, nil -} - func (*realPrompter) CaptureExistingFilepath(promptStr string) (string, error) { prompt := promptui.Prompt{ Label: promptStr, From b12908d3926d16b7596900b663ec55a88853c592 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 16 Sep 2024 18:27:16 -0400 Subject: [PATCH 14/43] fix prompt mock --- internal/mocks/prompter.go | 50 ++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/internal/mocks/prompter.go b/internal/mocks/prompter.go index 6cb81ac1c..4d43df3df 100644 --- a/internal/mocks/prompter.go +++ b/internal/mocks/prompter.go @@ -76,6 +76,30 @@ func (_m *Prompter) CaptureAddresses(promptStr string) ([]common.Address, error) return r0, r1 } +// CaptureBootstrapInitialBalance provides a mock function with given fields: promptStr +func (_m *Prompter) CaptureBootstrapInitialBalance(promptStr string) (uint64, error) { + ret := _m.Called(promptStr) + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(string) (uint64, error)); ok { + return rf(promptStr) + } + if rf, ok := ret.Get(0).(func(string) uint64); ok { + r0 = rf(promptStr) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(promptStr) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // CaptureDate provides a mock function with given fields: promptStr func (_m *Prompter) CaptureDate(promptStr string) (time.Time, error) { ret := _m.Called(promptStr) @@ -296,32 +320,6 @@ func (_m *Prompter) CaptureIndex(promptStr string, options []interface{}) (int, return r0, r1 } -// CaptureInitialBalances provides a mock function with given fields: promptStr, numValidators -func (_m *Prompter) CaptureInitialBalances(promptStr string, numValidators int) ([]int, error) { - ret := _m.Called(promptStr, numValidators) - - var r0 []int - var r1 error - if rf, ok := ret.Get(0).(func(string, int) ([]int, error)); ok { - return rf(promptStr, numValidators) - } - if rf, ok := ret.Get(0).(func(string, int) []int); ok { - r0 = rf(promptStr, numValidators) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]int) - } - } - - if rf, ok := ret.Get(1).(func(string, int) error); ok { - r1 = rf(promptStr, numValidators) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // CaptureInt provides a mock function with given fields: promptStr func (_m *Prompter) CaptureInt(promptStr string) (int, error) { ret := _m.Called(promptStr) From 10a0377c3495afb41d5d13abb7841b37e5f6454a Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 16 Sep 2024 18:32:58 -0400 Subject: [PATCH 15/43] update sidecar --- cmd/blockchaincmd/create.go | 39 ++++++++---------------------- cmd/blockchaincmd/prompt_owners.go | 5 ++-- pkg/models/bootstrap_validator.go | 26 ++++++++++++++++++++ pkg/models/sidecar.go | 2 ++ 4 files changed, 41 insertions(+), 31 deletions(-) create mode 100644 pkg/models/bootstrap_validator.go diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 63b9c6c73..ba788ef5f 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -6,18 +6,17 @@ import ( "encoding/json" "errors" "fmt" - "github.com/ava-labs/avalanche-cli/pkg/prompts" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/formatting/address" - "github.com/ava-labs/avalanchego/vms/platformvm/fx" - "github.com/ava-labs/avalanchego/vms/platformvm/signer" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" "os" "sort" "strconv" "strings" "unicode" + "github.com/ava-labs/avalanche-cli/pkg/prompts" + "github.com/ava-labs/avalanchego/utils/formatting/address" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanche-cli/pkg/application" "github.com/ava-labs/avalanche-cli/cmd/flags" @@ -58,7 +57,7 @@ type CreateFlags struct { validatorManagerMintOnly bool tokenMinterAddress []string validatorManagerController []string - bootstrapValidators []SubnetValidator + bootstrapValidators []models.SubnetValidator } var ( @@ -428,7 +427,7 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { } _, err = promptValidators() - //TODO: update subnetvalidators in sidecar + // TODO: update subnetvalidators in sidecar if err = app.CreateSidecar(sc); err != nil { return err @@ -556,24 +555,6 @@ func checkInvalidSubnetNames(name string) error { return nil } -// TODO: replace this object with avalanchego struct SubnetValidator -type SubnetValidator struct { - // Must be Ed25519 NodeID - NodeID ids.NodeID - // Weight of this validator used when sampling - Weight uint64 - // Initial balance for this validator - Balance uint64 - // [Signer] is the BLS key for this validator. - // Note: We do not enforce that the BLS key is unique across all validators. - // This means that validators can share a key if they so choose. - // However, a NodeID + Subnet does uniquely map to a BLS key - Signer signer.Signer - // Leftover $AVAX from the [Balance] will be issued to this - // owner once it is removed from the validator set. - ChangeOwner fx.Owner -} - // TODO: find the min weight for bootstrap validator func PromptWeightBootstrapValidator() (uint64, error) { txt := "What stake weight would you like to assign to the validator?" @@ -597,8 +578,8 @@ func PromptInitialBalance() (uint64, error) { } } -func promptValidators() ([]SubnetValidator, error) { - var subnetValidators []SubnetValidator +func promptValidators() ([]models.SubnetValidator, error) { + var subnetValidators []models.SubnetValidator numBootstrapValidators, err := app.Prompt.CaptureInt( "How many bootstrap validators do you want to set up?", ) @@ -633,7 +614,7 @@ func promptValidators() ([]SubnetValidator, error) { Addrs: addrs, } previousAddr = changeAddr - subnetValidator := SubnetValidator{ + subnetValidator := models.SubnetValidator{ NodeID: nodeID, Weight: weight, Balance: balance, diff --git a/cmd/blockchaincmd/prompt_owners.go b/cmd/blockchaincmd/prompt_owners.go index 64d7aefd5..245f3b8a9 100644 --- a/cmd/blockchaincmd/prompt_owners.go +++ b/cmd/blockchaincmd/prompt_owners.go @@ -4,13 +4,14 @@ package blockchaincmd import ( "fmt" - "github.com/ava-labs/avalanche-cli/pkg/networkoptions" - "github.com/ava-labs/avalanche-cli/pkg/utils" "os" "path/filepath" "strconv" "strings" + "github.com/ava-labs/avalanche-cli/pkg/networkoptions" + "github.com/ava-labs/avalanche-cli/pkg/utils" + "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/key" "github.com/ava-labs/avalanche-cli/pkg/keychain" diff --git a/pkg/models/bootstrap_validator.go b/pkg/models/bootstrap_validator.go new file mode 100644 index 000000000..65f4fae19 --- /dev/null +++ b/pkg/models/bootstrap_validator.go @@ -0,0 +1,26 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package models + +import ( + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/platformvm/fx" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" +) + +type SubnetValidator struct { + // Must be Ed25519 NodeID + NodeID ids.NodeID + // Weight of this validator used when sampling + Weight uint64 + // Initial balance for this validator + Balance uint64 + // [Signer] is the BLS key for this validator. + // Note: We do not enforce that the BLS key is unique across all validators. + // This means that validators can share a key if they so choose. + // However, a NodeID + Subnet does uniquely map to a BLS key + Signer signer.Signer + // Leftover $AVAX from the [Balance] will be issued to this + // owner once it is removed from the validator set. + ChangeOwner fx.Owner +} diff --git a/pkg/models/sidecar.go b/pkg/models/sidecar.go index ff09ed6e6..babba8605 100644 --- a/pkg/models/sidecar.go +++ b/pkg/models/sidecar.go @@ -42,6 +42,8 @@ type Sidecar struct { ValidatorManagement ValidatorManagementType ValidatorManagerController []string NewNativeTokenMinter []string + // TODO: replace this object with avalanchego struct SubnetValidator + BootstrapValidators []SubnetValidator } func (sc Sidecar) GetVMID() (string, error) { From 3d6ade0893a07d5f322308cc919668729b58b3ca Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 16 Sep 2024 19:14:03 -0400 Subject: [PATCH 16/43] update prompts --- cmd/blockchaincmd/add_validator.go | 7 +------ cmd/blockchaincmd/create.go | 9 +++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cmd/blockchaincmd/add_validator.go b/cmd/blockchaincmd/add_validator.go index 794368ed1..1df8f9473 100644 --- a/cmd/blockchaincmd/add_validator.go +++ b/cmd/blockchaincmd/add_validator.go @@ -402,12 +402,7 @@ func promptStart() (time.Time, error) { } func PromptNodeID() (ids.NodeID, error) { - ux.Logger.PrintToUser("Next, we need the NodeID of the validator you want to whitelist.") - ux.Logger.PrintToUser("") - ux.Logger.PrintToUser("Check https://docs.avax.network/apis/avalanchego/apis/info#infogetnodeid for instructions about how to query the NodeID from your node") - ux.Logger.PrintToUser("(Edit host IP address and port to match your deployment, if needed).") - - txt := "What is the NodeID of the validator you'd like to whitelist?" + txt := "What is the NodeID of the node you want to add as bootstrap validator?" return app.Prompt.CaptureNodeID(txt) } diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index ba788ef5f..8f9c31e38 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -427,6 +427,9 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { } _, err = promptValidators() + if err != nil { + return err + } // TODO: update subnetvalidators in sidecar if err = app.CreateSidecar(sc); err != nil { @@ -588,6 +591,7 @@ func promptValidators() ([]models.SubnetValidator, error) { } previousAddr := "" for len(subnetValidators) < numBootstrapValidators { + ux.Logger.PrintToUser("Getting info for bootstrap validator %d", len(subnetValidators)+1) nodeID, err := PromptNodeID() if err != nil { return nil, err @@ -622,6 +626,11 @@ func promptValidators() ([]models.SubnetValidator, error) { ChangeOwner: changeOwner, } subnetValidators = append(subnetValidators, subnetValidator) + ux.Logger.GreenCheckmarkToUser("Bootstrap Validator %d:", len(subnetValidators)) + ux.Logger.PrintToUser("- Node ID: %s", nodeID) + ux.Logger.PrintToUser("- Weight: %d", weight) + ux.Logger.PrintToUser("- Initial Balance: %d AVAX", balance) + ux.Logger.PrintToUser("- Change Address: %s", changeAddr) } return subnetValidators, nil } From 13955117300648e7872ba33d6385a8264250c8b7 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Tue, 17 Sep 2024 10:59:44 -0400 Subject: [PATCH 17/43] update prompts --- cmd/blockchaincmd/create.go | 8 ++++---- cmd/blockchaincmd/prompt_owners.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 8f9c31e38..7a00b10fb 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -426,11 +426,11 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return err } - _, err = promptValidators() + bootstrapValidators, err := promptBootstrapValidators() if err != nil { return err } - // TODO: update subnetvalidators in sidecar + sc.BootstrapValidators = bootstrapValidators if err = app.CreateSidecar(sc); err != nil { return err @@ -565,7 +565,7 @@ func PromptWeightBootstrapValidator() (uint64, error) { } func PromptInitialBalance() (uint64, error) { - defaultInitialBalance := fmt.Sprintf("Default (%d) AVAX", constants.MinInitialBalanceBootstrapValidator) + defaultInitialBalance := fmt.Sprintf("Default (%d AVAX)", constants.MinInitialBalanceBootstrapValidator) txt := "What initial balance would you like to assign to the bootstrap validator (in AVAX)?" weightOptions := []string{defaultInitialBalance, "Custom"} weightOption, err := app.Prompt.CaptureList(txt, weightOptions) @@ -581,7 +581,7 @@ func PromptInitialBalance() (uint64, error) { } } -func promptValidators() ([]models.SubnetValidator, error) { +func promptBootstrapValidators() ([]models.SubnetValidator, error) { var subnetValidators []models.SubnetValidator numBootstrapValidators, err := app.Prompt.CaptureInt( "How many bootstrap validators do you want to set up?", diff --git a/cmd/blockchaincmd/prompt_owners.go b/cmd/blockchaincmd/prompt_owners.go index 245f3b8a9..deecbb91c 100644 --- a/cmd/blockchaincmd/prompt_owners.go +++ b/cmd/blockchaincmd/prompt_owners.go @@ -363,7 +363,7 @@ func getKeyForChangeOwner(previouslyUsedAddr string) (string, error) { } func promptNetwork() (models.Network, error) { - promptStr := "Choose a network to get the key from" + promptStr := "Choose a network that the bootstrap validators will be validating" supportedNetworkOptionsToPrompt := []networkoptions.NetworkOption{networkoptions.Local, networkoptions.Devnet, networkoptions.Fuji, networkoptions.Mainnet} networkOptionStr, err := app.Prompt.CaptureList( From b8a0a88b3b851d7f503cd300a1f605578e8979a2 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Tue, 17 Sep 2024 11:04:59 -0400 Subject: [PATCH 18/43] refactor --- cmd/blockchaincmd/create.go | 215 --------------------- cmd/blockchaincmd/prompt_genesis_input.go | 223 ++++++++++++++++++++++ 2 files changed, 223 insertions(+), 215 deletions(-) create mode 100644 cmd/blockchaincmd/prompt_genesis_input.go diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 7a00b10fb..ec282c593 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -12,13 +12,6 @@ import ( "strings" "unicode" - "github.com/ava-labs/avalanche-cli/pkg/prompts" - "github.com/ava-labs/avalanchego/utils/formatting/address" - "github.com/ava-labs/avalanchego/vms/platformvm/signer" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" - - "github.com/ava-labs/avalanche-cli/pkg/application" - "github.com/ava-labs/avalanche-cli/cmd/flags" "github.com/ava-labs/avalanche-cli/pkg/cobrautils" "github.com/ava-labs/avalanche-cli/pkg/constants" @@ -445,57 +438,6 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return nil } -func getValidatorContractManagerAddr() ([]string, bool, error) { - controllerAddrPrompt := "Enter Validator Manager Contract controller address" - for { - // ask in a loop so that if some condition is not met we can keep asking - controlAddr, cancelled, err := getAddrLoop(controllerAddrPrompt, constants.ValidatorManagerController, models.UndefinedNetwork) - if err != nil { - return nil, false, err - } - if cancelled { - return nil, cancelled, nil - } - if len(controlAddr) != 0 { - return controlAddr, false, nil - } - ux.Logger.RedXToUser("An address to control Validator Manage Contract is required before proceeding") - } -} - -// Configure which addresses may make mint new native tokens -func getTokenMinterAddr() ([]string, error) { - addTokenMinterAddrPrompt := "Currently only Validator Manager Contract can mint new native tokens" - ux.Logger.PrintToUser(addTokenMinterAddrPrompt) - yes, err := app.Prompt.CaptureNoYes("Add additional addresses that can mint new native tokens?") - if err != nil { - return nil, err - } - if !yes { - return nil, nil - } - addr, cancelled, err := getAddr() - if err != nil { - return nil, err - } - if cancelled { - return nil, nil - } - return addr, nil -} - -func getAddr() ([]string, bool, error) { - addrPrompt := "Enter addresses that can mint new native tokens" - addr, cancelled, err := getAddrLoop(addrPrompt, constants.TokenMinter, models.UndefinedNetwork) - if err != nil { - return nil, false, err - } - if cancelled { - return nil, cancelled, nil - } - return addr, false, nil -} - func addSubnetEVMGenesisPrefundedAddress(genesisBytes []byte, address string, balance string) ([]byte, error) { var genesisMap map[string]interface{} if err := json.Unmarshal(genesisBytes, &genesisMap); err != nil { @@ -557,160 +499,3 @@ func checkInvalidSubnetNames(name string) error { } return nil } - -// TODO: find the min weight for bootstrap validator -func PromptWeightBootstrapValidator() (uint64, error) { - txt := "What stake weight would you like to assign to the validator?" - return app.Prompt.CaptureWeight(txt) -} - -func PromptInitialBalance() (uint64, error) { - defaultInitialBalance := fmt.Sprintf("Default (%d AVAX)", constants.MinInitialBalanceBootstrapValidator) - txt := "What initial balance would you like to assign to the bootstrap validator (in AVAX)?" - weightOptions := []string{defaultInitialBalance, "Custom"} - weightOption, err := app.Prompt.CaptureList(txt, weightOptions) - if err != nil { - return 0, err - } - - switch weightOption { - case defaultInitialBalance: - return constants.MinInitialBalanceBootstrapValidator, nil - default: - return app.Prompt.CaptureBootstrapInitialBalance(txt) - } -} - -func promptBootstrapValidators() ([]models.SubnetValidator, error) { - var subnetValidators []models.SubnetValidator - numBootstrapValidators, err := app.Prompt.CaptureInt( - "How many bootstrap validators do you want to set up?", - ) - if err != nil { - return nil, err - } - previousAddr := "" - for len(subnetValidators) < numBootstrapValidators { - ux.Logger.PrintToUser("Getting info for bootstrap validator %d", len(subnetValidators)+1) - nodeID, err := PromptNodeID() - if err != nil { - return nil, err - } - weight, err := PromptWeightBootstrapValidator() - if err != nil { - return nil, err - } - balance, err := PromptInitialBalance() - if err != nil { - return nil, err - } - proofOfPossession, err := promptProofOfPossession() - changeAddr, err := getKeyForChangeOwner(previousAddr) - if err != nil { - return nil, err - } - addrs, err := address.ParseToIDs([]string{changeAddr}) - if err != nil { - return nil, fmt.Errorf("failure parsing change owner address: %w", err) - } - changeOwner := &secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: addrs, - } - previousAddr = changeAddr - subnetValidator := models.SubnetValidator{ - NodeID: nodeID, - Weight: weight, - Balance: balance, - Signer: proofOfPossession, - ChangeOwner: changeOwner, - } - subnetValidators = append(subnetValidators, subnetValidator) - ux.Logger.GreenCheckmarkToUser("Bootstrap Validator %d:", len(subnetValidators)) - ux.Logger.PrintToUser("- Node ID: %s", nodeID) - ux.Logger.PrintToUser("- Weight: %d", weight) - ux.Logger.PrintToUser("- Initial Balance: %d AVAX", balance) - ux.Logger.PrintToUser("- Change Address: %s", changeAddr) - } - return subnetValidators, nil -} - -func validateProofOfPossession(publicKey, pop string) { - if publicKey != "" { - err := prompts.ValidateHexa(publicKey) - if err != nil { - ux.Logger.PrintToUser("Format error in given public key: %s", err) - publicKey = "" - } - } - if pop != "" { - err := prompts.ValidateHexa(pop) - if err != nil { - ux.Logger.PrintToUser("Format error in given proof of possession: %s", err) - pop = "" - } - } -} - -func promptProofOfPossession() (signer.Signer, error) { - ux.Logger.PrintToUser("Next, we need the public key and proof of possession of the node's BLS") - ux.Logger.PrintToUser("Check https://docs.avax.network/api-reference/info-api#infogetnodeid for instructions on calling info.getNodeID API") - var err error - txt := "What is the public key of the node's BLS?" - publicKey, err := app.Prompt.CaptureValidatedString(txt, prompts.ValidateHexa) - if err != nil { - return nil, err - } - txt = "What is the proof of possession of the node's BLS?" - proofOfPossesion, err := app.Prompt.CaptureValidatedString(txt, prompts.ValidateHexa) - if err != nil { - return nil, err - } - var proofOfPossession signer.Signer - pop := &signer.ProofOfPossession{ - PublicKey: [48]byte([]byte(publicKey)), - ProofOfPossession: [96]byte([]byte(proofOfPossesion)), - } - proofOfPossession = pop - return proofOfPossession, nil -} - -// TODO: add explain the difference for different validator management type -func promptValidatorManagementType( - app *application.Avalanche, - sidecar *models.Sidecar, -) error { - proofOfAuthorityOption := "Proof of Authority" - proofOfStakeOption := "Proof of Stake" - explainOption := "Explain the difference" - if createFlags.proofOfStake { - sidecar.ValidatorManagement = models.ValidatorManagementTypeFromString(proofOfStakeOption) - return nil - } - if createFlags.proofOfAuthority { - sidecar.ValidatorManagement = models.ValidatorManagementTypeFromString(proofOfAuthorityOption) - return nil - } - options := []string{proofOfAuthorityOption, proofOfStakeOption, explainOption} - var subnetTypeStr string - for { - option, err := app.Prompt.CaptureList( - "Which validator management protocol would you like to use in your blockchain?", - options, - ) - if err != nil { - return err - } - switch option { - case proofOfAuthorityOption: - subnetTypeStr = models.ProofOfAuthority - case proofOfStakeOption: - subnetTypeStr = models.ProofOfStake - case explainOption: - continue - } - break - } - sidecar.ValidatorManagement = models.ValidatorManagementTypeFromString(subnetTypeStr) - return nil -} diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go new file mode 100644 index 000000000..1df05c559 --- /dev/null +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -0,0 +1,223 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package blockchaincmd + +import ( + "fmt" + "github.com/ava-labs/avalanche-cli/pkg/application" + "github.com/ava-labs/avalanche-cli/pkg/constants" + "github.com/ava-labs/avalanche-cli/pkg/models" + "github.com/ava-labs/avalanche-cli/pkg/prompts" + "github.com/ava-labs/avalanche-cli/pkg/ux" + "github.com/ava-labs/avalanchego/utils/formatting/address" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" +) + +func getValidatorContractManagerAddr() ([]string, bool, error) { + controllerAddrPrompt := "Enter Validator Manager Contract controller address" + for { + // ask in a loop so that if some condition is not met we can keep asking + controlAddr, cancelled, err := getAddrLoop(controllerAddrPrompt, constants.ValidatorManagerController, models.UndefinedNetwork) + if err != nil { + return nil, false, err + } + if cancelled { + return nil, cancelled, nil + } + if len(controlAddr) != 0 { + return controlAddr, false, nil + } + ux.Logger.RedXToUser("An address to control Validator Manage Contract is required before proceeding") + } +} + +// Configure which addresses may make mint new native tokens +func getTokenMinterAddr() ([]string, error) { + addTokenMinterAddrPrompt := "Currently only Validator Manager Contract can mint new native tokens" + ux.Logger.PrintToUser(addTokenMinterAddrPrompt) + yes, err := app.Prompt.CaptureNoYes("Add additional addresses that can mint new native tokens?") + if err != nil { + return nil, err + } + if !yes { + return nil, nil + } + addr, cancelled, err := getAddr() + if err != nil { + return nil, err + } + if cancelled { + return nil, nil + } + return addr, nil +} + +func getAddr() ([]string, bool, error) { + addrPrompt := "Enter addresses that can mint new native tokens" + addr, cancelled, err := getAddrLoop(addrPrompt, constants.TokenMinter, models.UndefinedNetwork) + if err != nil { + return nil, false, err + } + if cancelled { + return nil, cancelled, nil + } + return addr, false, nil +} + +func promptProofOfPossession() (signer.Signer, error) { + ux.Logger.PrintToUser("Next, we need the public key and proof of possession of the node's BLS") + ux.Logger.PrintToUser("Check https://docs.avax.network/api-reference/info-api#infogetnodeid for instructions on calling info.getNodeID API") + var err error + txt := "What is the public key of the node's BLS?" + publicKey, err := app.Prompt.CaptureValidatedString(txt, prompts.ValidateHexa) + if err != nil { + return nil, err + } + txt = "What is the proof of possession of the node's BLS?" + proofOfPossesion, err := app.Prompt.CaptureValidatedString(txt, prompts.ValidateHexa) + if err != nil { + return nil, err + } + var proofOfPossession signer.Signer + pop := &signer.ProofOfPossession{ + PublicKey: [48]byte([]byte(publicKey)), + ProofOfPossession: [96]byte([]byte(proofOfPossesion)), + } + proofOfPossession = pop + return proofOfPossession, nil +} + +// TODO: add explain the difference for different validator management type +func promptValidatorManagementType( + app *application.Avalanche, + sidecar *models.Sidecar, +) error { + proofOfAuthorityOption := "Proof of Authority" + proofOfStakeOption := "Proof of Stake" + explainOption := "Explain the difference" + if createFlags.proofOfStake { + sidecar.ValidatorManagement = models.ValidatorManagementTypeFromString(proofOfStakeOption) + return nil + } + if createFlags.proofOfAuthority { + sidecar.ValidatorManagement = models.ValidatorManagementTypeFromString(proofOfAuthorityOption) + return nil + } + options := []string{proofOfAuthorityOption, proofOfStakeOption, explainOption} + var subnetTypeStr string + for { + option, err := app.Prompt.CaptureList( + "Which validator management protocol would you like to use in your blockchain?", + options, + ) + if err != nil { + return err + } + switch option { + case proofOfAuthorityOption: + subnetTypeStr = models.ProofOfAuthority + case proofOfStakeOption: + subnetTypeStr = models.ProofOfStake + case explainOption: + continue + } + break + } + sidecar.ValidatorManagement = models.ValidatorManagementTypeFromString(subnetTypeStr) + return nil +} + +// TODO: find the min weight for bootstrap validator +func PromptWeightBootstrapValidator() (uint64, error) { + txt := "What stake weight would you like to assign to the validator?" + return app.Prompt.CaptureWeight(txt) +} + +func PromptInitialBalance() (uint64, error) { + defaultInitialBalance := fmt.Sprintf("Default (%d AVAX)", constants.MinInitialBalanceBootstrapValidator) + txt := "What initial balance would you like to assign to the bootstrap validator (in AVAX)?" + weightOptions := []string{defaultInitialBalance, "Custom"} + weightOption, err := app.Prompt.CaptureList(txt, weightOptions) + if err != nil { + return 0, err + } + + switch weightOption { + case defaultInitialBalance: + return constants.MinInitialBalanceBootstrapValidator, nil + default: + return app.Prompt.CaptureBootstrapInitialBalance(txt) + } +} + +func promptBootstrapValidators() ([]models.SubnetValidator, error) { + var subnetValidators []models.SubnetValidator + numBootstrapValidators, err := app.Prompt.CaptureInt( + "How many bootstrap validators do you want to set up?", + ) + if err != nil { + return nil, err + } + previousAddr := "" + for len(subnetValidators) < numBootstrapValidators { + ux.Logger.PrintToUser("Getting info for bootstrap validator %d", len(subnetValidators)+1) + nodeID, err := PromptNodeID() + if err != nil { + return nil, err + } + weight, err := PromptWeightBootstrapValidator() + if err != nil { + return nil, err + } + balance, err := PromptInitialBalance() + if err != nil { + return nil, err + } + proofOfPossession, err := promptProofOfPossession() + changeAddr, err := getKeyForChangeOwner(previousAddr) + if err != nil { + return nil, err + } + addrs, err := address.ParseToIDs([]string{changeAddr}) + if err != nil { + return nil, fmt.Errorf("failure parsing change owner address: %w", err) + } + changeOwner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: addrs, + } + previousAddr = changeAddr + subnetValidator := models.SubnetValidator{ + NodeID: nodeID, + Weight: weight, + Balance: balance, + Signer: proofOfPossession, + ChangeOwner: changeOwner, + } + subnetValidators = append(subnetValidators, subnetValidator) + ux.Logger.GreenCheckmarkToUser("Bootstrap Validator %d:", len(subnetValidators)) + ux.Logger.PrintToUser("- Node ID: %s", nodeID) + ux.Logger.PrintToUser("- Weight: %d", weight) + ux.Logger.PrintToUser("- Initial Balance: %d AVAX", balance) + ux.Logger.PrintToUser("- Change Address: %s", changeAddr) + } + return subnetValidators, nil +} + +func validateProofOfPossession(publicKey, pop string) { + if publicKey != "" { + err := prompts.ValidateHexa(publicKey) + if err != nil { + ux.Logger.PrintToUser("Format error in given public key: %s", err) + publicKey = "" + } + } + if pop != "" { + err := prompts.ValidateHexa(pop) + if err != nil { + ux.Logger.PrintToUser("Format error in given proof of possession: %s", err) + pop = "" + } + } +} From 7d829d77525e34a15e546f9e48e7c23a1e3f79f5 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 18 Sep 2024 14:57:47 -0400 Subject: [PATCH 19/43] update prompts --- cmd/blockchaincmd/create.go | 51 +++++++---- cmd/blockchaincmd/prompt_genesis_input.go | 104 ++++++++++++++++++---- pkg/models/bootstrap_validator.go | 18 ++++ 3 files changed, 142 insertions(+), 31 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index ec282c593..e42a402bd 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -54,11 +54,12 @@ type CreateFlags struct { } var ( - createFlags CreateFlags - forceCreate bool - genesisFile string - vmFile string - useRepo bool + createFlags CreateFlags + forceCreate bool + genesisFile string + vmFile string + useRepo bool + bootstrapValidatorsJSONFilePath string errIllegalNameCharacter = errors.New( "illegal name character: only letters, no special characters allowed") @@ -115,6 +116,7 @@ configuration, pass the -f flag.`, cmd.Flags().BoolVar(&createFlags.validatorManagerMintOnly, "validator-manager-mint-only", false, "only enable validator manager contract to mint new native tokens") cmd.Flags().StringSliceVar(&createFlags.tokenMinterAddress, "token-minter-address", nil, "addresses that can mint new native tokens (for proof of authority validator management only)") cmd.Flags().StringSliceVar(&createFlags.validatorManagerController, "validator-manager-controller", nil, "addresses that will control Validator Manager contract") + cmd.Flags().StringVar(&bootstrapValidatorsJSONFilePath, "bootstrap-filepath", "", "JSON file path that provides details about bootstrap validators") return cmd } @@ -217,13 +219,14 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { } } - //if len(createFlags.bootstrapValidatorInitialBalance) > 0 { - // for _, balance := range createFlags.bootstrapValidatorInitialBalance { - // if balance < constants.MinInitialBalanceBootstrapValidator { - // return fmt.Errorf("initial bootstrap validator balance must be at least %d AVAX", constants.MinInitialBalanceBootstrapValidator) - // } - // } - //} + var bootstrapValidators []models.SubnetValidator + var err error + if bootstrapValidatorsJSONFilePath != "" { + bootstrapValidators, err = LoadBootstrapValidator(bootstrapValidatorsJSONFilePath) + if err != nil { + return err + } + } // get vm kind vmType, err := vm.PromptVMType(app, createFlags.useSubnetEvm, createFlags.useCustomVM) @@ -419,9 +422,11 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return err } - bootstrapValidators, err := promptBootstrapValidators() - if err != nil { - return err + if bootstrapValidatorsJSONFilePath == "" { + bootstrapValidators, err = promptBootstrapValidators() + if err != nil { + return err + } } sc.BootstrapValidators = bootstrapValidators @@ -499,3 +504,19 @@ func checkInvalidSubnetNames(name string) error { } return nil } + +func LoadBootstrapValidator(filepath string) ([]models.SubnetValidator, error) { + jsonBytes, err := os.ReadFile(filepath) + if err != nil { + return nil, err + } + var subnetValidatorsJSON []models.SubnetValidatorJSON + if err = json.Unmarshal(jsonBytes, &subnetValidatorsJSON); err != nil { + return nil, err + } + subnetValidators, err := convertToSubnetValidators(subnetValidatorsJSON) + if err != nil { + return nil, err + } + return subnetValidators, nil +} diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 1df05c559..d49dec1dc 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -3,12 +3,14 @@ package blockchaincmd import ( + "encoding/json" "fmt" "github.com/ava-labs/avalanche-cli/pkg/application" "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/models" "github.com/ava-labs/avalanche-cli/pkg/prompts" "github.com/ava-labs/avalanche-cli/pkg/ux" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/formatting/address" "github.com/ava-labs/avalanchego/vms/platformvm/signer" "github.com/ava-labs/avalanchego/vms/secp256k1fx" @@ -79,13 +81,11 @@ func promptProofOfPossession() (signer.Signer, error) { if err != nil { return nil, err } - var proofOfPossession signer.Signer - pop := &signer.ProofOfPossession{ - PublicKey: [48]byte([]byte(publicKey)), - ProofOfPossession: [96]byte([]byte(proofOfPossesion)), + pop, err := getBLSInfo(publicKey, proofOfPossesion) + if err != nil { + return nil, err } - proofOfPossession = pop - return proofOfPossession, nil + return pop, nil } // TODO: add explain the difference for different validator management type @@ -205,19 +205,91 @@ func promptBootstrapValidators() ([]models.SubnetValidator, error) { return subnetValidators, nil } -func validateProofOfPossession(publicKey, pop string) { - if publicKey != "" { - err := prompts.ValidateHexa(publicKey) +func validateBLS(publicKey, pop string) error { + if err := prompts.ValidateHexa(publicKey); err != nil { + return fmt.Errorf("format error in given public key: %s", err) + } + if err := prompts.ValidateHexa(pop); err != nil { + return fmt.Errorf("format error in given proof of possession: %s", err) + } + return nil +} + +func getBLSInfo(publicKey, proofOfPossesion string) (signer.Signer, error) { + type jsonProofOfPossession struct { + PublicKey string + ProofOfPossession string + } + jsonPop := jsonProofOfPossession{ + PublicKey: publicKey, + ProofOfPossession: proofOfPossesion, + } + popBytes, err := json.Marshal(jsonPop) + if err != nil { + return nil, err + } + pop := &signer.ProofOfPossession{} + err = pop.UnmarshalJSON(popBytes) + if err != nil { + return nil, err + } + return pop, nil +} + +func convertToSubnetValidators(validatorJSONS []models.SubnetValidatorJSON) ([]models.SubnetValidator, error) { + subnetValidators := []models.SubnetValidator{} + type jsonProofOfPossession struct { + PublicKey string + ProofOfPossession string + } + for _, validatorJSON := range validatorJSONS { + nodeID, err := ids.NodeIDFromString(validatorJSON.NodeID) if err != nil { - ux.Logger.PrintToUser("Format error in given public key: %s", err) - publicKey = "" + return nil, fmt.Errorf("invalid node id %s", validatorJSON.NodeID) } - } - if pop != "" { - err := prompts.ValidateHexa(pop) + if validatorJSON.Weight <= 0 { + return nil, fmt.Errorf("bootstrap validator weight has to be greater than 0") + } + if validatorJSON.Balance <= 0 { + return nil, fmt.Errorf("bootstrap validator balance has to be greater than 0") + } + if err = validateBLS(validatorJSON.BLSPublicKey, validatorJSON.BLSProofOfPossession); err != nil { + return nil, err + } + //jsonPop := jsonProofOfPossession{ + // PublicKey: validatorJSON.BLSPublicKey, + // ProofOfPossession: validatorJSON.BLSProofOfPossession, + //} + //popBytes, err := json.Marshal(jsonPop) + //if err != nil { + // return nil, err + //} + //pop := &signer.ProofOfPossession{} + //err = pop.UnmarshalJSON(popBytes) + //if err != nil { + // return nil, err + //} + pop, err := getBLSInfo(validatorJSON.BLSPublicKey, validatorJSON.BLSProofOfPossession) + if err != nil { + return nil, err + } + changeAddr, err := ids.ShortFromString(validatorJSON.ChangeOwnerAddr) if err != nil { - ux.Logger.PrintToUser("Format error in given proof of possession: %s", err) - pop = "" + return nil, err + } + changeOwner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{changeAddr}, } + subnetValidators = append(subnetValidators, + models.SubnetValidator{ + NodeID: nodeID, + Weight: validatorJSON.Weight, + Balance: validatorJSON.Balance, + Signer: pop, + ChangeOwner: changeOwner, + }, + ) } + return subnetValidators, nil } diff --git a/pkg/models/bootstrap_validator.go b/pkg/models/bootstrap_validator.go index 65f4fae19..7f36417b8 100644 --- a/pkg/models/bootstrap_validator.go +++ b/pkg/models/bootstrap_validator.go @@ -11,16 +11,34 @@ import ( type SubnetValidator struct { // Must be Ed25519 NodeID NodeID ids.NodeID + // Weight of this validator used when sampling Weight uint64 + // Initial balance for this validator Balance uint64 + // [Signer] is the BLS key for this validator. // Note: We do not enforce that the BLS key is unique across all validators. // This means that validators can share a key if they so choose. // However, a NodeID + Subnet does uniquely map to a BLS key Signer signer.Signer + // Leftover $AVAX from the [Balance] will be issued to this // owner once it is removed from the validator set. ChangeOwner fx.Owner } + +type SubnetValidatorJSON struct { + NodeID string `json:"NodeID"` + + Weight uint64 `json:"Weight"` + + Balance uint64 `json:"Balance"` + + BLSPublicKey string `json:"BLSPublicKey"` + + BLSProofOfPossession string `json:"BLSProofOfPossession"` + + ChangeOwnerAddr string `json:"ChangeOwnerAddr"` +} From 4cbe6e74c8563d52b3a6056afbbec3892647efca Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 18 Sep 2024 15:33:11 -0400 Subject: [PATCH 20/43] update prompts --- cmd/blockchaincmd/create.go | 7 +- cmd/blockchaincmd/prompt_genesis_input.go | 90 +++++------------------ pkg/models/bootstrap_validator.go | 27 ------- 3 files changed, 23 insertions(+), 101 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index e42a402bd..95c5e06d8 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -510,12 +510,11 @@ func LoadBootstrapValidator(filepath string) ([]models.SubnetValidator, error) { if err != nil { return nil, err } - var subnetValidatorsJSON []models.SubnetValidatorJSON - if err = json.Unmarshal(jsonBytes, &subnetValidatorsJSON); err != nil { + var subnetValidators []models.SubnetValidator + if err = json.Unmarshal(jsonBytes, &subnetValidators); err != nil { return nil, err } - subnetValidators, err := convertToSubnetValidators(subnetValidatorsJSON) - if err != nil { + if err = validateSubnetValidatorsJSON(subnetValidators); err != nil { return nil, err } return subnetValidators, nil diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index d49dec1dc..417c25c18 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -11,9 +11,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/prompts" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/formatting/address" "github.com/ava-labs/avalanchego/vms/platformvm/signer" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) func getValidatorContractManagerAddr() ([]string, bool, error) { @@ -67,25 +65,21 @@ func getAddr() ([]string, bool, error) { return addr, false, nil } -func promptProofOfPossession() (signer.Signer, error) { +func promptProofOfPossession() (string, string, error) { ux.Logger.PrintToUser("Next, we need the public key and proof of possession of the node's BLS") ux.Logger.PrintToUser("Check https://docs.avax.network/api-reference/info-api#infogetnodeid for instructions on calling info.getNodeID API") var err error txt := "What is the public key of the node's BLS?" publicKey, err := app.Prompt.CaptureValidatedString(txt, prompts.ValidateHexa) if err != nil { - return nil, err + return "", "", err } txt = "What is the proof of possession of the node's BLS?" proofOfPossesion, err := app.Prompt.CaptureValidatedString(txt, prompts.ValidateHexa) if err != nil { - return nil, err + return "", "", err } - pop, err := getBLSInfo(publicKey, proofOfPossesion) - if err != nil { - return nil, err - } - return pop, nil + return publicKey, proofOfPossesion, nil } // TODO: add explain the difference for different validator management type @@ -128,7 +122,6 @@ func promptValidatorManagementType( return nil } -// TODO: find the min weight for bootstrap validator func PromptWeightBootstrapValidator() (uint64, error) { txt := "What stake weight would you like to assign to the validator?" return app.Prompt.CaptureWeight(txt) @@ -174,26 +167,22 @@ func promptBootstrapValidators() ([]models.SubnetValidator, error) { if err != nil { return nil, err } - proofOfPossession, err := promptProofOfPossession() - changeAddr, err := getKeyForChangeOwner(previousAddr) + publicKey, pop, err := promptProofOfPossession() if err != nil { return nil, err } - addrs, err := address.ParseToIDs([]string{changeAddr}) + changeAddr, err := getKeyForChangeOwner(previousAddr) if err != nil { - return nil, fmt.Errorf("failure parsing change owner address: %w", err) - } - changeOwner := &secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: addrs, + return nil, err } previousAddr = changeAddr subnetValidator := models.SubnetValidator{ - NodeID: nodeID, - Weight: weight, - Balance: balance, - Signer: proofOfPossession, - ChangeOwner: changeOwner, + NodeID: nodeID.String(), + Weight: weight, + Balance: balance, + BLSPublicKey: publicKey, + BLSProofOfPossession: pop, + ChangeOwnerAddr: changeAddr, } subnetValidators = append(subnetValidators, subnetValidator) ux.Logger.GreenCheckmarkToUser("Bootstrap Validator %d:", len(subnetValidators)) @@ -236,60 +225,21 @@ func getBLSInfo(publicKey, proofOfPossesion string) (signer.Signer, error) { return pop, nil } -func convertToSubnetValidators(validatorJSONS []models.SubnetValidatorJSON) ([]models.SubnetValidator, error) { - subnetValidators := []models.SubnetValidator{} - type jsonProofOfPossession struct { - PublicKey string - ProofOfPossession string - } +func validateSubnetValidatorsJSON(validatorJSONS []models.SubnetValidator) error { for _, validatorJSON := range validatorJSONS { - nodeID, err := ids.NodeIDFromString(validatorJSON.NodeID) + _, err := ids.NodeIDFromString(validatorJSON.NodeID) if err != nil { - return nil, fmt.Errorf("invalid node id %s", validatorJSON.NodeID) + return fmt.Errorf("invalid node id %s", validatorJSON.NodeID) } if validatorJSON.Weight <= 0 { - return nil, fmt.Errorf("bootstrap validator weight has to be greater than 0") + return fmt.Errorf("bootstrap validator weight has to be greater than 0") } if validatorJSON.Balance <= 0 { - return nil, fmt.Errorf("bootstrap validator balance has to be greater than 0") + return fmt.Errorf("bootstrap validator balance has to be greater than 0") } if err = validateBLS(validatorJSON.BLSPublicKey, validatorJSON.BLSProofOfPossession); err != nil { - return nil, err - } - //jsonPop := jsonProofOfPossession{ - // PublicKey: validatorJSON.BLSPublicKey, - // ProofOfPossession: validatorJSON.BLSProofOfPossession, - //} - //popBytes, err := json.Marshal(jsonPop) - //if err != nil { - // return nil, err - //} - //pop := &signer.ProofOfPossession{} - //err = pop.UnmarshalJSON(popBytes) - //if err != nil { - // return nil, err - //} - pop, err := getBLSInfo(validatorJSON.BLSPublicKey, validatorJSON.BLSProofOfPossession) - if err != nil { - return nil, err - } - changeAddr, err := ids.ShortFromString(validatorJSON.ChangeOwnerAddr) - if err != nil { - return nil, err - } - changeOwner := &secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: []ids.ShortID{changeAddr}, + return err } - subnetValidators = append(subnetValidators, - models.SubnetValidator{ - NodeID: nodeID, - Weight: validatorJSON.Weight, - Balance: validatorJSON.Balance, - Signer: pop, - ChangeOwner: changeOwner, - }, - ) } - return subnetValidators, nil + return nil } diff --git a/pkg/models/bootstrap_validator.go b/pkg/models/bootstrap_validator.go index 7f36417b8..8072bf39a 100644 --- a/pkg/models/bootstrap_validator.go +++ b/pkg/models/bootstrap_validator.go @@ -2,34 +2,7 @@ // See the file LICENSE for licensing terms. package models -import ( - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/fx" - "github.com/ava-labs/avalanchego/vms/platformvm/signer" -) - type SubnetValidator struct { - // Must be Ed25519 NodeID - NodeID ids.NodeID - - // Weight of this validator used when sampling - Weight uint64 - - // Initial balance for this validator - Balance uint64 - - // [Signer] is the BLS key for this validator. - // Note: We do not enforce that the BLS key is unique across all validators. - // This means that validators can share a key if they so choose. - // However, a NodeID + Subnet does uniquely map to a BLS key - Signer signer.Signer - - // Leftover $AVAX from the [Balance] will be issued to this - // owner once it is removed from the validator set. - ChangeOwner fx.Owner -} - -type SubnetValidatorJSON struct { NodeID string `json:"NodeID"` Weight uint64 `json:"Weight"` From 3756c9d714d1e72cb74b0167b46233425ce98f0c Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 18 Sep 2024 15:37:02 -0400 Subject: [PATCH 21/43] use default balance and weight --- cmd/blockchaincmd/prompt_genesis_input.go | 36 ++--------------------- pkg/constants/constants.go | 15 +++++----- pkg/prompts/validations.go | 4 +-- 3 files changed, 12 insertions(+), 43 deletions(-) diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 417c25c18..685e7d5ea 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -122,28 +122,6 @@ func promptValidatorManagementType( return nil } -func PromptWeightBootstrapValidator() (uint64, error) { - txt := "What stake weight would you like to assign to the validator?" - return app.Prompt.CaptureWeight(txt) -} - -func PromptInitialBalance() (uint64, error) { - defaultInitialBalance := fmt.Sprintf("Default (%d AVAX)", constants.MinInitialBalanceBootstrapValidator) - txt := "What initial balance would you like to assign to the bootstrap validator (in AVAX)?" - weightOptions := []string{defaultInitialBalance, "Custom"} - weightOption, err := app.Prompt.CaptureList(txt, weightOptions) - if err != nil { - return 0, err - } - - switch weightOption { - case defaultInitialBalance: - return constants.MinInitialBalanceBootstrapValidator, nil - default: - return app.Prompt.CaptureBootstrapInitialBalance(txt) - } -} - func promptBootstrapValidators() ([]models.SubnetValidator, error) { var subnetValidators []models.SubnetValidator numBootstrapValidators, err := app.Prompt.CaptureInt( @@ -159,14 +137,6 @@ func promptBootstrapValidators() ([]models.SubnetValidator, error) { if err != nil { return nil, err } - weight, err := PromptWeightBootstrapValidator() - if err != nil { - return nil, err - } - balance, err := PromptInitialBalance() - if err != nil { - return nil, err - } publicKey, pop, err := promptProofOfPossession() if err != nil { return nil, err @@ -178,8 +148,8 @@ func promptBootstrapValidators() ([]models.SubnetValidator, error) { previousAddr = changeAddr subnetValidator := models.SubnetValidator{ NodeID: nodeID.String(), - Weight: weight, - Balance: balance, + Weight: constants.DefaultWeightBootstrapValidator, + Balance: constants.InitialBalanceBootstrapValidator, BLSPublicKey: publicKey, BLSProofOfPossession: pop, ChangeOwnerAddr: changeAddr, @@ -187,8 +157,6 @@ func promptBootstrapValidators() ([]models.SubnetValidator, error) { subnetValidators = append(subnetValidators, subnetValidator) ux.Logger.GreenCheckmarkToUser("Bootstrap Validator %d:", len(subnetValidators)) ux.Logger.PrintToUser("- Node ID: %s", nodeID) - ux.Logger.PrintToUser("- Weight: %d", weight) - ux.Logger.PrintToUser("- Initial Balance: %d AVAX", balance) ux.Logger.PrintToUser("- Change Address: %s", changeAddr) } return subnetValidators, nil diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 16bbb81df..6d28d8cff 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -154,13 +154,14 @@ const ( Disable = "disable" - TimeParseLayout = "2006-01-02 15:04:05" - MinStakeWeight = 1 - MinInitialBalanceBootstrapValidator = 5 - DefaultStakeWeight = 20 - AVAXSymbol = "AVAX" - DefaultFujiStakeDuration = "48h" - DefaultMainnetStakeDuration = "336h" + TimeParseLayout = "2006-01-02 15:04:05" + MinStakeWeight = 1 + InitialBalanceBootstrapValidator = 1 + DefaultWeightBootstrapValidator = 1000000 + DefaultStakeWeight = 20 + AVAXSymbol = "AVAX" + DefaultFujiStakeDuration = "48h" + DefaultMainnetStakeDuration = "336h" // The absolute minimum is 25 seconds, but set to 1 minute to allow for // time to go through the command DevnetStakingStartLeadTime = 30 * time.Second diff --git a/pkg/prompts/validations.go b/pkg/prompts/validations.go index 48438851b..ad9fd4326 100644 --- a/pkg/prompts/validations.go +++ b/pkg/prompts/validations.go @@ -134,8 +134,8 @@ func validateBootstrapBalance(input string) error { if err != nil { return err } - if val < constants.MinInitialBalanceBootstrapValidator { - return fmt.Errorf("initial bootstrap validator balance must be at least %d AVAX", constants.MinInitialBalanceBootstrapValidator) + if val < constants.InitialBalanceBootstrapValidator { + return fmt.Errorf("initial bootstrap validator balance must be at least %d AVAX", constants.InitialBalanceBootstrapValidator) } return nil } From 5042124bef26b7a966421ccfd41c2a619a139434 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 18 Sep 2024 17:03:39 -0400 Subject: [PATCH 22/43] generate new node ids and bls --- cmd/blockchaincmd/create.go | 1 - cmd/blockchaincmd/prompt_genesis_input.go | 82 +++++++++++++++-------- 2 files changed, 53 insertions(+), 30 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 95c5e06d8..72fe2548e 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -50,7 +50,6 @@ type CreateFlags struct { validatorManagerMintOnly bool tokenMinterAddress []string validatorManagerController []string - bootstrapValidators []models.SubnetValidator } var ( diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 685e7d5ea..0088f506a 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -3,14 +3,17 @@ package blockchaincmd import ( - "encoding/json" "fmt" "github.com/ava-labs/avalanche-cli/pkg/application" "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/models" "github.com/ava-labs/avalanche-cli/pkg/prompts" + "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/staking" + "github.com/ava-labs/avalanchego/utils/crypto/bls" + "github.com/ava-labs/avalanchego/utils/formatting" "github.com/ava-labs/avalanchego/vms/platformvm/signer" ) @@ -130,16 +133,46 @@ func promptBootstrapValidators() ([]models.SubnetValidator, error) { if err != nil { return nil, err } + setUpNodes, err := promptSetUpNodes() + if err != nil { + return nil, err + } previousAddr := "" for len(subnetValidators) < numBootstrapValidators { ux.Logger.PrintToUser("Getting info for bootstrap validator %d", len(subnetValidators)+1) - nodeID, err := PromptNodeID() - if err != nil { - return nil, err - } - publicKey, pop, err := promptProofOfPossession() - if err != nil { - return nil, err + var nodeID ids.NodeID + var publicKey, pop string + if setUpNodes { + nodeID, err = PromptNodeID() + if err != nil { + return nil, err + } + publicKey, pop, err = promptProofOfPossession() + if err != nil { + return nil, err + } + } else { + certBytes, _, err := staking.NewCertAndKeyBytes() + if err != nil { + return nil, err + } + nodeID, err = utils.ToNodeID(certBytes) + if err != nil { + return nil, err + } + blsSignerKey, err := bls.NewSecretKey() + if err != nil { + return nil, err + } + p := signer.NewProofOfPossession(blsSignerKey) + publicKey, err = formatting.Encode(formatting.HexNC, p.PublicKey[:]) + if err != nil { + return nil, err + } + pop, err = formatting.Encode(formatting.HexNC, p.ProofOfPossession[:]) + if err != nil { + return nil, err + } } changeAddr, err := getKeyForChangeOwner(previousAddr) if err != nil { @@ -172,27 +205,6 @@ func validateBLS(publicKey, pop string) error { return nil } -func getBLSInfo(publicKey, proofOfPossesion string) (signer.Signer, error) { - type jsonProofOfPossession struct { - PublicKey string - ProofOfPossession string - } - jsonPop := jsonProofOfPossession{ - PublicKey: publicKey, - ProofOfPossession: proofOfPossesion, - } - popBytes, err := json.Marshal(jsonPop) - if err != nil { - return nil, err - } - pop := &signer.ProofOfPossession{} - err = pop.UnmarshalJSON(popBytes) - if err != nil { - return nil, err - } - return pop, nil -} - func validateSubnetValidatorsJSON(validatorJSONS []models.SubnetValidator) error { for _, validatorJSON := range validatorJSONS { _, err := ids.NodeIDFromString(validatorJSON.NodeID) @@ -211,3 +223,15 @@ func validateSubnetValidatorsJSON(validatorJSONS []models.SubnetValidator) error } return nil } + +// promptProvideNodeID returns false if user doesn't have any Avalanche node set up yet to be +// bootstrap validators +func promptSetUpNodes() (bool, error) { + ux.Logger.PrintToUser("If you have set up your own Avalanche Nodes, you can provide the Node ID and BLS Key from those nodes in the next step.") + ux.Logger.PrintToUser("Otherwise, we will generate new Node IDs and BLS Key for you.") + setUpNodes, err := app.Prompt.CaptureYesNo("Have you set up your own Avalanche Nodes?") + if err != nil { + return false, err + } + return setUpNodes, nil +} From f2189392c75fcfdad4de044f3e317d2196b83e69 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 18 Sep 2024 17:09:39 -0400 Subject: [PATCH 23/43] fix lint --- cmd/blockchaincmd/prompt_genesis_input.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 0088f506a..70fd0d656 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -4,6 +4,7 @@ package blockchaincmd import ( "fmt" + "github.com/ava-labs/avalanche-cli/pkg/application" "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/models" @@ -197,10 +198,10 @@ func promptBootstrapValidators() ([]models.SubnetValidator, error) { func validateBLS(publicKey, pop string) error { if err := prompts.ValidateHexa(publicKey); err != nil { - return fmt.Errorf("format error in given public key: %s", err) + return fmt.Errorf("format error in given public key: %w", err) } if err := prompts.ValidateHexa(pop); err != nil { - return fmt.Errorf("format error in given proof of possession: %s", err) + return fmt.Errorf("format error in given proof of possession: %w", err) } return nil } From 773f313b7d80da514567c5ec9e90c4117f56465c Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 18 Sep 2024 17:35:05 -0400 Subject: [PATCH 24/43] add flags --- cmd/blockchaincmd/create.go | 17 +++++- cmd/blockchaincmd/prompt_genesis_input.go | 70 +++++++++++++++-------- 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 72fe2548e..3cc31a1d5 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -50,6 +50,7 @@ type CreateFlags struct { validatorManagerMintOnly bool tokenMinterAddress []string validatorManagerController []string + generateNodeID bool } var ( @@ -115,7 +116,8 @@ configuration, pass the -f flag.`, cmd.Flags().BoolVar(&createFlags.validatorManagerMintOnly, "validator-manager-mint-only", false, "only enable validator manager contract to mint new native tokens") cmd.Flags().StringSliceVar(&createFlags.tokenMinterAddress, "token-minter-address", nil, "addresses that can mint new native tokens (for proof of authority validator management only)") cmd.Flags().StringSliceVar(&createFlags.validatorManagerController, "validator-manager-controller", nil, "addresses that will control Validator Manager contract") - cmd.Flags().StringVar(&bootstrapValidatorsJSONFilePath, "bootstrap-filepath", "", "JSON file path that provides details about bootstrap validators") + cmd.Flags().StringVar(&bootstrapValidatorsJSONFilePath, "bootstrap-filepath", "", "JSON file path that provides details about bootstrap validators, leave Node-ID and BLS values empty if using --generate-node-id=true") + cmd.Flags().BoolVar(&createFlags.generateNodeID, "generate-node-id", false, "whether to create new node id for bootstrap validators (Node-ID and BLS values in bootstrap JSON file will be overridden if --bootstrap-filepath flag is used)") return cmd } @@ -513,8 +515,19 @@ func LoadBootstrapValidator(filepath string) ([]models.SubnetValidator, error) { if err = json.Unmarshal(jsonBytes, &subnetValidators); err != nil { return nil, err } - if err = validateSubnetValidatorsJSON(subnetValidators); err != nil { + if err = validateSubnetValidatorsJSON(createFlags.generateNodeID, subnetValidators); err != nil { return nil, err } + if createFlags.generateNodeID { + for _, subnetValidator := range subnetValidators { + nodeID, publicKey, pop, err := generateNewNodeAndBLS() + if err != nil { + return nil, err + } + subnetValidator.NodeID = nodeID + subnetValidator.BLSPublicKey = publicKey + subnetValidator.BLSProofOfPossession = pop + } + } return subnetValidators, nil } diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 70fd0d656..1c74cc430 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -126,6 +126,32 @@ func promptValidatorManagementType( return nil } +// generateNewNodeAndBLS returns node id, bls public key and bls pop +func generateNewNodeAndBLS() (string, string, string, error) { + certBytes, _, err := staking.NewCertAndKeyBytes() + if err != nil { + return "", "", "", err + } + nodeID, err := utils.ToNodeID(certBytes) + if err != nil { + return "", "", "", err + } + blsSignerKey, err := bls.NewSecretKey() + if err != nil { + return "", "", "", err + } + p := signer.NewProofOfPossession(blsSignerKey) + publicKey, err := formatting.Encode(formatting.HexNC, p.PublicKey[:]) + if err != nil { + return "", "", "", err + } + pop, err := formatting.Encode(formatting.HexNC, p.ProofOfPossession[:]) + if err != nil { + return "", "", "", err + } + return nodeID.String(), publicKey, pop, nil +} + func promptBootstrapValidators() ([]models.SubnetValidator, error) { var subnetValidators []models.SubnetValidator numBootstrapValidators, err := app.Prompt.CaptureInt( @@ -134,9 +160,14 @@ func promptBootstrapValidators() ([]models.SubnetValidator, error) { if err != nil { return nil, err } - setUpNodes, err := promptSetUpNodes() - if err != nil { - return nil, err + var setUpNodes bool + if createFlags.generateNodeID { + setUpNodes = true + } else { + setUpNodes, err = promptSetUpNodes() + if err != nil { + return nil, err + } } previousAddr := "" for len(subnetValidators) < numBootstrapValidators { @@ -153,24 +184,11 @@ func promptBootstrapValidators() ([]models.SubnetValidator, error) { return nil, err } } else { - certBytes, _, err := staking.NewCertAndKeyBytes() - if err != nil { - return nil, err - } - nodeID, err = utils.ToNodeID(certBytes) - if err != nil { - return nil, err - } - blsSignerKey, err := bls.NewSecretKey() - if err != nil { - return nil, err - } - p := signer.NewProofOfPossession(blsSignerKey) - publicKey, err = formatting.Encode(formatting.HexNC, p.PublicKey[:]) + nodeIDStr, publicKey, pop, err = generateNewNodeAndBLS() if err != nil { return nil, err } - pop, err = formatting.Encode(formatting.HexNC, p.ProofOfPossession[:]) + nodeID, err = ids.NodeIDFromString(nodeIDStr) if err != nil { return nil, err } @@ -206,11 +224,16 @@ func validateBLS(publicKey, pop string) error { return nil } -func validateSubnetValidatorsJSON(validatorJSONS []models.SubnetValidator) error { +func validateSubnetValidatorsJSON(generateNewNodeID bool, validatorJSONS []models.SubnetValidator) error { for _, validatorJSON := range validatorJSONS { - _, err := ids.NodeIDFromString(validatorJSON.NodeID) - if err != nil { - return fmt.Errorf("invalid node id %s", validatorJSON.NodeID) + if !generateNewNodeID { + _, err := ids.NodeIDFromString(validatorJSON.NodeID) + if err != nil { + return fmt.Errorf("invalid node id %s", validatorJSON.NodeID) + } + if err = validateBLS(validatorJSON.BLSPublicKey, validatorJSON.BLSProofOfPossession); err != nil { + return err + } } if validatorJSON.Weight <= 0 { return fmt.Errorf("bootstrap validator weight has to be greater than 0") @@ -218,9 +241,6 @@ func validateSubnetValidatorsJSON(validatorJSONS []models.SubnetValidator) error if validatorJSON.Balance <= 0 { return fmt.Errorf("bootstrap validator balance has to be greater than 0") } - if err = validateBLS(validatorJSON.BLSPublicKey, validatorJSON.BLSProofOfPossession); err != nil { - return err - } } return nil } From 1dcf24cd94904bd5c3016a84f59f348fbcc3957f Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 18 Sep 2024 17:38:19 -0400 Subject: [PATCH 25/43] add flags --- cmd/blockchaincmd/prompt_genesis_input.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 1c74cc430..255e8a986 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -227,6 +227,9 @@ func validateBLS(publicKey, pop string) error { func validateSubnetValidatorsJSON(generateNewNodeID bool, validatorJSONS []models.SubnetValidator) error { for _, validatorJSON := range validatorJSONS { if !generateNewNodeID { + if validatorJSON.NodeID == "" || validatorJSON.BLSPublicKey == "" || validatorJSON.BLSProofOfPossession == "" { + return fmt.Errorf("no Node ID or BLS info provided, use --generate-node-id flag to generate new Node ID and BLS info") + } _, err := ids.NodeIDFromString(validatorJSON.NodeID) if err != nil { return fmt.Errorf("invalid node id %s", validatorJSON.NodeID) From c8c4e5ce98981a3617802dd1ac4240f21ffba894 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 19 Sep 2024 12:56:20 -0400 Subject: [PATCH 26/43] address comments --- cmd/blockchaincmd/add_validator.go | 6 +++--- cmd/blockchaincmd/prompt_genesis_input.go | 6 +++--- cmd/blockchaincmd/remove_validator.go | 2 +- cmd/primarycmd/add_validator.go | 2 +- pkg/prompts/prompts.go | 2 +- pkg/prompts/validations.go | 6 +++--- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cmd/blockchaincmd/add_validator.go b/cmd/blockchaincmd/add_validator.go index 1df8f9473..779d8bf48 100644 --- a/cmd/blockchaincmd/add_validator.go +++ b/cmd/blockchaincmd/add_validator.go @@ -204,7 +204,7 @@ func CallAddValidator( ux.Logger.PrintToUser("Your subnet auth keys for add validator tx creation: %s", subnetAuthKeys) if nodeIDStr == "" { - nodeID, err = PromptNodeID() + nodeID, err = PromptNodeID("add as validator") if err != nil { return err } @@ -401,8 +401,8 @@ func promptStart() (time.Time, error) { return app.Prompt.CaptureDate(txt) } -func PromptNodeID() (ids.NodeID, error) { - txt := "What is the NodeID of the node you want to add as bootstrap validator?" +func PromptNodeID(goal string) (ids.NodeID, error) { + txt := fmt.Sprintf("What is the NodeID of the node you want to %s?", goal) return app.Prompt.CaptureNodeID(txt) } diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 255e8a986..4a6854058 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -91,8 +91,8 @@ func promptValidatorManagementType( app *application.Avalanche, sidecar *models.Sidecar, ) error { - proofOfAuthorityOption := "Proof of Authority" - proofOfStakeOption := "Proof of Stake" + proofOfAuthorityOption := models.ProofOfAuthority + proofOfStakeOption := models.ProofOfStake explainOption := "Explain the difference" if createFlags.proofOfStake { sidecar.ValidatorManagement = models.ValidatorManagementTypeFromString(proofOfStakeOption) @@ -175,7 +175,7 @@ func promptBootstrapValidators() ([]models.SubnetValidator, error) { var nodeID ids.NodeID var publicKey, pop string if setUpNodes { - nodeID, err = PromptNodeID() + nodeID, err = PromptNodeID("add as bootstrap validator") if err != nil { return nil, err } diff --git a/cmd/blockchaincmd/remove_validator.go b/cmd/blockchaincmd/remove_validator.go index d1f495627..cd815b9d7 100644 --- a/cmd/blockchaincmd/remove_validator.go +++ b/cmd/blockchaincmd/remove_validator.go @@ -156,7 +156,7 @@ func removeValidator(_ *cobra.Command, args []string) error { ux.Logger.PrintToUser("Your subnet auth keys for remove validator tx creation: %s", subnetAuthKeys) if nodeIDStr == "" { - nodeID, err = PromptNodeID() + nodeID, err = PromptNodeID("remove as validator") if err != nil { return err } diff --git a/cmd/primarycmd/add_validator.go b/cmd/primarycmd/add_validator.go index ba3a8aa04..85ddab637 100644 --- a/cmd/primarycmd/add_validator.go +++ b/cmd/primarycmd/add_validator.go @@ -154,7 +154,7 @@ func addValidator(_ *cobra.Command, _ []string) error { } if nodeIDStr == "" { - nodeID, err = blockchaincmd.PromptNodeID() + nodeID, err = blockchaincmd.PromptNodeID("add as Primary Network Validator") if err != nil { return err } diff --git a/pkg/prompts/prompts.go b/pkg/prompts/prompts.go index 2006eecb5..ef4967931 100644 --- a/pkg/prompts/prompts.go +++ b/pkg/prompts/prompts.go @@ -269,7 +269,7 @@ func (*realPrompter) CaptureNodeID(promptStr string) (ids.NodeID, error) { func (*realPrompter) CaptureBootstrapInitialBalance(promptStr string) (uint64, error) { prompt := promptui.Prompt{ Label: promptStr, - Validate: validateBootstrapBalance, + Validate: validateBootstrapValidatorBalance, } amountStr, err := prompt.Run() diff --git a/pkg/prompts/validations.go b/pkg/prompts/validations.go index ad9fd4326..2671eb051 100644 --- a/pkg/prompts/validations.go +++ b/pkg/prompts/validations.go @@ -129,13 +129,13 @@ func validateWeight(input string) error { return nil } -func validateBootstrapBalance(input string) error { +func validateBootstrapValidatorBalance(input string) error { val, err := strconv.ParseUint(input, 10, 64) if err != nil { return err } - if val < constants.InitialBalanceBootstrapValidator { - return fmt.Errorf("initial bootstrap validator balance must be at least %d AVAX", constants.InitialBalanceBootstrapValidator) + if val <= 0 { + return fmt.Errorf("initial bootstrap validator balance must be greater than 0 AVAX") } return nil } From a66cb3283b833df44fc1d7e2a1ada4d376c97e32 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 19 Sep 2024 12:59:09 -0400 Subject: [PATCH 27/43] address comments --- cmd/blockchaincmd/prompt_genesis_input.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 4a6854058..618a5271d 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -47,14 +47,14 @@ func getTokenMinterAddr() ([]string, error) { if !yes { return nil, nil } - addr, cancelled, err := getAddr() + addresses, cancelled, err := getAddr() if err != nil { return nil, err } if cancelled { return nil, nil } - return addr, nil + return addresses, nil } func getAddr() ([]string, bool, error) { @@ -73,12 +73,12 @@ func promptProofOfPossession() (string, string, error) { ux.Logger.PrintToUser("Next, we need the public key and proof of possession of the node's BLS") ux.Logger.PrintToUser("Check https://docs.avax.network/api-reference/info-api#infogetnodeid for instructions on calling info.getNodeID API") var err error - txt := "What is the public key of the node's BLS?" + txt := "What is the node's BLS public key?" publicKey, err := app.Prompt.CaptureValidatedString(txt, prompts.ValidateHexa) if err != nil { return "", "", err } - txt = "What is the proof of possession of the node's BLS?" + txt = "What is the node's BLS proof of possession?" proofOfPossesion, err := app.Prompt.CaptureValidatedString(txt, prompts.ValidateHexa) if err != nil { return "", "", err From 890d6dbdf3050db101d39257c706fd26c4d4a63a Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 19 Sep 2024 14:08:29 -0400 Subject: [PATCH 28/43] address comments --- cmd/blockchaincmd/add_validator.go | 2 +- cmd/blockchaincmd/create.go | 3 +++ cmd/blockchaincmd/prompt_genesis_input.go | 2 +- pkg/constants/constants.go | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cmd/blockchaincmd/add_validator.go b/cmd/blockchaincmd/add_validator.go index 779d8bf48..41209362b 100644 --- a/cmd/blockchaincmd/add_validator.go +++ b/cmd/blockchaincmd/add_validator.go @@ -220,7 +220,7 @@ func CallAddValidator( return err } if selectedWeight < constants.MinStakeWeight { - return fmt.Errorf("illegal weight, must be greater than or equal to %d: %d", constants.MinStakeWeight, selectedWeight) + return fmt.Errorf("invalid weight, must be greater than or equal to %d: %d", constants.MinStakeWeight, selectedWeight) } start, selectedDuration, err := getTimeParameters(network, nodeID, true) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 3cc31a1d5..97f930b8e 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -507,6 +507,9 @@ func checkInvalidSubnetNames(name string) error { } func LoadBootstrapValidator(filepath string) ([]models.SubnetValidator, error) { + if !utils.FileExists(filepath) { + return nil, fmt.Errorf("file path %q doesn't exist", filepath) + } jsonBytes, err := os.ReadFile(filepath) if err != nil { return nil, err diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 618a5271d..25a5c3616 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -200,7 +200,7 @@ func promptBootstrapValidators() ([]models.SubnetValidator, error) { previousAddr = changeAddr subnetValidator := models.SubnetValidator{ NodeID: nodeID.String(), - Weight: constants.DefaultWeightBootstrapValidator, + Weight: constants.DefaultBootstrapValidatorWeight, Balance: constants.InitialBalanceBootstrapValidator, BLSPublicKey: publicKey, BLSProofOfPossession: pop, diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 6d28d8cff..d6b2e1628 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -157,7 +157,7 @@ const ( TimeParseLayout = "2006-01-02 15:04:05" MinStakeWeight = 1 InitialBalanceBootstrapValidator = 1 - DefaultWeightBootstrapValidator = 1000000 + DefaultBootstrapValidatorWeight = 1000000 DefaultStakeWeight = 20 AVAXSymbol = "AVAX" DefaultFujiStakeDuration = "48h" From b55a7926f4b081dda0d8c289a3fd116246c141d8 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 19 Sep 2024 14:57:51 -0400 Subject: [PATCH 29/43] move validator prompt to deploy --- cmd/blockchaincmd/create.go | 61 ++------------- cmd/blockchaincmd/deploy.go | 93 +++++++++++++++++------ cmd/blockchaincmd/prompt_genesis_input.go | 6 +- cmd/blockchaincmd/prompt_owners.go | 35 +-------- cmd/blockchaincmd/upgradecmd/apply.go | 4 +- cmd/transactioncmd/transaction_commit.go | 2 +- pkg/application/app.go | 2 + pkg/models/sidecar.go | 14 +++- pkg/networkoptions/network_options.go | 25 ------ 9 files changed, 97 insertions(+), 145 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 97f930b8e..8ef4f6009 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -50,17 +50,14 @@ type CreateFlags struct { validatorManagerMintOnly bool tokenMinterAddress []string validatorManagerController []string - generateNodeID bool } var ( - createFlags CreateFlags - forceCreate bool - genesisFile string - vmFile string - useRepo bool - bootstrapValidatorsJSONFilePath string - + createFlags CreateFlags + forceCreate bool + genesisFile string + vmFile string + useRepo bool errIllegalNameCharacter = errors.New( "illegal name character: only letters, no special characters allowed") errMutuallyExlusiveVersionOptions = errors.New("version flags --latest,--pre-release,vm-version are mutually exclusive") @@ -116,8 +113,6 @@ configuration, pass the -f flag.`, cmd.Flags().BoolVar(&createFlags.validatorManagerMintOnly, "validator-manager-mint-only", false, "only enable validator manager contract to mint new native tokens") cmd.Flags().StringSliceVar(&createFlags.tokenMinterAddress, "token-minter-address", nil, "addresses that can mint new native tokens (for proof of authority validator management only)") cmd.Flags().StringSliceVar(&createFlags.validatorManagerController, "validator-manager-controller", nil, "addresses that will control Validator Manager contract") - cmd.Flags().StringVar(&bootstrapValidatorsJSONFilePath, "bootstrap-filepath", "", "JSON file path that provides details about bootstrap validators, leave Node-ID and BLS values empty if using --generate-node-id=true") - cmd.Flags().BoolVar(&createFlags.generateNodeID, "generate-node-id", false, "whether to create new node id for bootstrap validators (Node-ID and BLS values in bootstrap JSON file will be overridden if --bootstrap-filepath flag is used)") return cmd } @@ -220,15 +215,6 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { } } - var bootstrapValidators []models.SubnetValidator - var err error - if bootstrapValidatorsJSONFilePath != "" { - bootstrapValidators, err = LoadBootstrapValidator(bootstrapValidatorsJSONFilePath) - if err != nil { - return err - } - } - // get vm kind vmType, err := vm.PromptVMType(app, createFlags.useSubnetEvm, createFlags.useCustomVM) if err != nil { @@ -423,14 +409,6 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return err } - if bootstrapValidatorsJSONFilePath == "" { - bootstrapValidators, err = promptBootstrapValidators() - if err != nil { - return err - } - } - sc.BootstrapValidators = bootstrapValidators - if err = app.CreateSidecar(sc); err != nil { return err } @@ -505,32 +483,3 @@ func checkInvalidSubnetNames(name string) error { } return nil } - -func LoadBootstrapValidator(filepath string) ([]models.SubnetValidator, error) { - if !utils.FileExists(filepath) { - return nil, fmt.Errorf("file path %q doesn't exist", filepath) - } - jsonBytes, err := os.ReadFile(filepath) - if err != nil { - return nil, err - } - var subnetValidators []models.SubnetValidator - if err = json.Unmarshal(jsonBytes, &subnetValidators); err != nil { - return nil, err - } - if err = validateSubnetValidatorsJSON(createFlags.generateNodeID, subnetValidators); err != nil { - return nil, err - } - if createFlags.generateNodeID { - for _, subnetValidator := range subnetValidators { - nodeID, publicKey, pop, err := generateNewNodeAndBLS() - if err != nil { - return nil, err - } - subnetValidator.NodeID = nodeID - subnetValidator.BLSPublicKey = publicKey - subnetValidator.BLSProofOfPossession = pop - } - } - return subnetValidators, nil -} diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index 23f451bf6..119d7f6d0 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/ava-labs/avalanche-cli/pkg/utils" "os" "path/filepath" "strings" @@ -36,27 +37,28 @@ import ( var deploySupportedNetworkOptions = []networkoptions.NetworkOption{networkoptions.Local, networkoptions.Devnet, networkoptions.Fuji, networkoptions.Mainnet} var ( - sameControlKey bool - keyName string - threshold uint32 - controlKeys []string - subnetAuthKeys []string - userProvidedAvagoVersion string - outputTxPath string - useLedger bool - useEwoq bool - ledgerAddresses []string - subnetIDStr string - mainnetChainID uint32 - skipCreatePrompt bool - avagoBinaryPath string - subnetOnly bool - teleporterEsp subnet.TeleporterEsp - - errMutuallyExlusiveControlKeys = errors.New("--control-keys and --same-control-key are mutually exclusive") - ErrMutuallyExlusiveKeyLedger = errors.New("key source flags --key, --ledger/--ledger-addrs are mutually exclusive") - ErrStoredKeyOnMainnet = errors.New("key --key is not available for mainnet operations") - errMutuallyExlusiveSubnetFlags = errors.New("--subnet-only and --subnet-id are mutually exclusive") + sameControlKey bool + keyName string + threshold uint32 + controlKeys []string + subnetAuthKeys []string + userProvidedAvagoVersion string + outputTxPath string + useLedger bool + useEwoq bool + ledgerAddresses []string + subnetIDStr string + mainnetChainID uint32 + skipCreatePrompt bool + avagoBinaryPath string + subnetOnly bool + generateNodeID bool + teleporterEsp subnet.TeleporterEsp + bootstrapValidatorsJSONFilePath string + errMutuallyExlusiveControlKeys = errors.New("--control-keys and --same-control-key are mutually exclusive") + ErrMutuallyExlusiveKeyLedger = errors.New("key source flags --key, --ledger/--ledger-addrs are mutually exclusive") + ErrStoredKeyOnMainnet = errors.New("key --key is not available for mainnet operations") + errMutuallyExlusiveSubnetFlags = errors.New("--subnet-only and --subnet-id are mutually exclusive") ) // avalanche blockchain deploy @@ -100,6 +102,8 @@ so you can take your locally tested Subnet and deploy it on Fuji or Mainnet.`, cmd.Flags().StringVar(&teleporterEsp.MessengerDeployerAddressPath, "teleporter-messenger-deployer-address-path", "", "path to a teleporter messenger deployer address file") cmd.Flags().StringVar(&teleporterEsp.MessengerDeployerTxPath, "teleporter-messenger-deployer-tx-path", "", "path to a teleporter messenger deployer tx file") cmd.Flags().StringVar(&teleporterEsp.RegistryBydecodePath, "teleporter-registry-bytecode-path", "", "path to a teleporter registry bytecode file") + cmd.Flags().StringVar(&bootstrapValidatorsJSONFilePath, "bootstrap-filepath", "", "JSON file path that provides details about bootstrap validators, leave Node-ID and BLS values empty if using --generate-node-id=true") + cmd.Flags().BoolVar(&generateNodeID, "generate-node-id", false, "whether to create new node id for bootstrap validators (Node-ID and BLS values in bootstrap JSON file will be overridden if --bootstrap-filepath flag is used)") return cmd } @@ -274,6 +278,14 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { } } + var bootstrapValidators []models.SubnetValidator + if bootstrapValidatorsJSONFilePath != "" { + bootstrapValidators, err = LoadBootstrapValidator(bootstrapValidatorsJSONFilePath) + if err != nil { + return err + } + } + chain := chains[0] sidecar, err := app.LoadSidecar(chain) @@ -335,6 +347,13 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { } } + if bootstrapValidatorsJSONFilePath == "" { + bootstrapValidators, err = promptBootstrapValidators(network) + if err != nil { + return err + } + } + ux.Logger.PrintToUser("Deploying %s to %s", chains, network.Name()) if network.Kind == models.Local { @@ -386,6 +405,7 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { deployInfo.BlockchainID, deployInfo.TeleporterMessengerAddress, deployInfo.TeleporterRegistryAddress, + bootstrapValidators, ); err != nil { return err } @@ -552,7 +572,7 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { // update sidecar // TODO: need to do something for backwards compatibility? - return app.UpdateSidecarNetworks(&sidecar, network, subnetID, blockchainID, "", "") + return app.UpdateSidecarNetworks(&sidecar, network, subnetID, blockchainID, "", "", bootstrapValidators) } func ValidateSubnetNameAndGetChains(args []string) ([]string, error) { @@ -725,3 +745,32 @@ func CheckForInvalidDeployAndGetAvagoVersion( } return desiredAvagoVersion, nil } + +func LoadBootstrapValidator(filepath string) ([]models.SubnetValidator, error) { + if !utils.FileExists(filepath) { + return nil, fmt.Errorf("file path %q doesn't exist", filepath) + } + jsonBytes, err := os.ReadFile(filepath) + if err != nil { + return nil, err + } + var subnetValidators []models.SubnetValidator + if err = json.Unmarshal(jsonBytes, &subnetValidators); err != nil { + return nil, err + } + if err = validateSubnetValidatorsJSON(generateNodeID, subnetValidators); err != nil { + return nil, err + } + if generateNodeID { + for _, subnetValidator := range subnetValidators { + nodeID, publicKey, pop, err := generateNewNodeAndBLS() + if err != nil { + return nil, err + } + subnetValidator.NodeID = nodeID + subnetValidator.BLSPublicKey = publicKey + subnetValidator.BLSProofOfPossession = pop + } + } + return subnetValidators, nil +} diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 25a5c3616..9edc33d85 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -152,7 +152,7 @@ func generateNewNodeAndBLS() (string, string, string, error) { return nodeID.String(), publicKey, pop, nil } -func promptBootstrapValidators() ([]models.SubnetValidator, error) { +func promptBootstrapValidators(network models.Network) ([]models.SubnetValidator, error) { var subnetValidators []models.SubnetValidator numBootstrapValidators, err := app.Prompt.CaptureInt( "How many bootstrap validators do you want to set up?", @@ -161,7 +161,7 @@ func promptBootstrapValidators() ([]models.SubnetValidator, error) { return nil, err } var setUpNodes bool - if createFlags.generateNodeID { + if generateNodeID { setUpNodes = true } else { setUpNodes, err = promptSetUpNodes() @@ -193,7 +193,7 @@ func promptBootstrapValidators() ([]models.SubnetValidator, error) { return nil, err } } - changeAddr, err := getKeyForChangeOwner(previousAddr) + changeAddr, err := getKeyForChangeOwner(previousAddr, network) if err != nil { return nil, err } diff --git a/cmd/blockchaincmd/prompt_owners.go b/cmd/blockchaincmd/prompt_owners.go index deecbb91c..8eeca8ccc 100644 --- a/cmd/blockchaincmd/prompt_owners.go +++ b/cmd/blockchaincmd/prompt_owners.go @@ -9,9 +9,6 @@ import ( "strconv" "strings" - "github.com/ava-labs/avalanche-cli/pkg/networkoptions" - "github.com/ava-labs/avalanche-cli/pkg/utils" - "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/key" "github.com/ava-labs/avalanche-cli/pkg/keychain" @@ -309,7 +306,7 @@ func getThreshold(maxLen int) (uint32, error) { return uint32(intTh), err } -func getKeyForChangeOwner(previouslyUsedAddr string) (string, error) { +func getKeyForChangeOwner(previouslyUsedAddr string, network models.Network) (string, error) { moreKeysPrompt := "Which key would you like to set as change owner for leftover AVAX if the node is removed from validator set?" const ( @@ -333,10 +330,6 @@ func getKeyForChangeOwner(previouslyUsedAddr string) (string, error) { case previousAddres: key = previouslyUsedAddr case getFromStored: - network, err := promptNetwork() - if err != nil { - return "", err - } key, err = prompts.CaptureKeyAddress( app.Prompt, "be set as a change owner for leftover AVAX", @@ -361,29 +354,3 @@ func getKeyForChangeOwner(previouslyUsedAddr string) (string, error) { } return key, nil } - -func promptNetwork() (models.Network, error) { - promptStr := "Choose a network that the bootstrap validators will be validating" - supportedNetworkOptionsToPrompt := []networkoptions.NetworkOption{networkoptions.Local, networkoptions.Devnet, networkoptions.Fuji, networkoptions.Mainnet} - - networkOptionStr, err := app.Prompt.CaptureList( - promptStr, - utils.Map(supportedNetworkOptionsToPrompt, func(n networkoptions.NetworkOption) string { return n.String() }), - ) - if err != nil { - return models.UndefinedNetwork, err - } - networkOption := networkoptions.NetworkOptionFromString(networkOptionStr) - network := models.UndefinedNetwork - if networkOption == networkoptions.Devnet { - endpoint, err := app.Prompt.CaptureURL(fmt.Sprintf("%s Endpoint", networkOption.String()), false) - if err != nil { - return models.UndefinedNetwork, err - } - network, err = networkOption.ModelNetwork(endpoint) - if err != nil { - return models.UndefinedNetwork, err - } - } - return network, nil -} diff --git a/cmd/blockchaincmd/upgradecmd/apply.go b/cmd/blockchaincmd/upgradecmd/apply.go index 5208be1cf..744c348a6 100644 --- a/cmd/blockchaincmd/upgradecmd/apply.go +++ b/cmd/blockchaincmd/upgradecmd/apply.go @@ -254,7 +254,7 @@ func applyPublicNetworkUpgrade(blockchainName, networkKey string, sc *models.Sid if print { blockchainIDstr := "" if sc.Networks != nil && - sc.Networks[networkKey] != (models.NetworkData{}) && + !sc.NetworkDataIsEmpty(networkKey) && sc.Networks[networkKey].BlockchainID != ids.Empty { blockchainIDstr = sc.Networks[networkKey].BlockchainID.String() } @@ -319,7 +319,7 @@ func applyPublicNetworkUpgrade(blockchainName, networkKey string, sc *models.Sid func validateUpgrade(blockchainName, networkKey string, sc *models.Sidecar, skipPrompting bool) ([]params.PrecompileUpgrade, string, error) { // if there's no entry in the Sidecar, we assume there hasn't been a deploy yet - if sc.Networks[networkKey] == (models.NetworkData{}) { + if sc.NetworkDataIsEmpty(networkKey) { return nil, "", subnetNotYetDeployed() } chainID := sc.Networks[networkKey].BlockchainID diff --git a/cmd/transactioncmd/transaction_commit.go b/cmd/transactioncmd/transaction_commit.go index 21712c924..f1ff6568d 100644 --- a/cmd/transactioncmd/transaction_commit.go +++ b/cmd/transactioncmd/transaction_commit.go @@ -97,7 +97,7 @@ func commitTx(_ *cobra.Command, args []string) error { if err := blockchaincmd.PrintDeployResults(subnetName, subnetID, txID); err != nil { return err } - return app.UpdateSidecarNetworks(&sc, network, subnetID, txID, "", "") + return app.UpdateSidecarNetworks(&sc, network, subnetID, txID, "", "", sc.Networks[network.Name()].BootstrapValidators) } return nil diff --git a/pkg/application/app.go b/pkg/application/app.go index cc2b2f1fe..6e8147019 100644 --- a/pkg/application/app.go +++ b/pkg/application/app.go @@ -520,6 +520,7 @@ func (app *Avalanche) UpdateSidecarNetworks( blockchainID ids.ID, teleporterMessengerAddress string, teleporterRegistryAddress string, + bootstrapValidators []models.SubnetValidator, ) error { if sc.Networks == nil { sc.Networks = make(map[string]models.NetworkData) @@ -530,6 +531,7 @@ func (app *Avalanche) UpdateSidecarNetworks( RPCVersion: sc.RPCVersion, TeleporterMessengerAddress: teleporterMessengerAddress, TeleporterRegistryAddress: teleporterRegistryAddress, + BootstrapValidators: bootstrapValidators, } if err := app.UpdateSidecar(sc); err != nil { return fmt.Errorf("creation of chains and subnet was successful, but failed to update sidecar: %w", err) diff --git a/pkg/models/sidecar.go b/pkg/models/sidecar.go index babba8605..f58a2731d 100644 --- a/pkg/models/sidecar.go +++ b/pkg/models/sidecar.go @@ -13,6 +13,7 @@ type NetworkData struct { RPCVersion int TeleporterMessengerAddress string TeleporterRegistryAddress string + BootstrapValidators []SubnetValidator } type Sidecar struct { @@ -42,8 +43,6 @@ type Sidecar struct { ValidatorManagement ValidatorManagementType ValidatorManagerController []string NewNativeTokenMinter []string - // TODO: replace this object with avalanchego struct SubnetValidator - BootstrapValidators []SubnetValidator } func (sc Sidecar) GetVMID() (string, error) { @@ -60,3 +59,14 @@ func (sc Sidecar) GetVMID() (string, error) { } return vmid, nil } + +func (sc Sidecar) NetworkDataIsEmpty(network string) bool { + if sc.Networks[network].SubnetID == ids.Empty && + sc.Networks[network].BlockchainID == ids.Empty && + sc.Networks[network].RPCVersion == 0 && + sc.Networks[network].TeleporterMessengerAddress == "" && + sc.Networks[network].TeleporterRegistryAddress == "" { + return true + } + return false +} diff --git a/pkg/networkoptions/network_options.go b/pkg/networkoptions/network_options.go index 3eeb9f394..bc1afaf51 100644 --- a/pkg/networkoptions/network_options.go +++ b/pkg/networkoptions/network_options.go @@ -62,31 +62,6 @@ func NetworkOptionFromString(s string) NetworkOption { return Undefined } -func (n NetworkOption) ModelNetwork(devnetEndpoint string) (models.Network, error) { - network := models.UndefinedNetwork - switch n { - case Local: - network = models.NewLocalNetwork() - case Devnet: - networkID := uint32(0) - if devnetEndpoint != "" { - infoClient := info.NewClient(devnetEndpoint) - ctx, cancel := utils.GetAPIContext() - defer cancel() - _, err := infoClient.GetNetworkID(ctx) - if err != nil { - return models.UndefinedNetwork, err - } - } - network = models.NewDevnetNetwork(devnetEndpoint, networkID) - case Fuji: - network = models.NewFujiNetwork() - case Mainnet: - network = models.NewMainnetNetwork() - } - return network, nil -} - type NetworkFlags struct { UseLocal bool UseDevnet bool From 58bfdae6045f573ec709a76f07056380a0df8a76 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 19 Sep 2024 15:19:28 -0400 Subject: [PATCH 30/43] address comments --- cmd/blockchaincmd/deploy.go | 3 ++- cmd/blockchaincmd/prompt_genesis_input.go | 26 ++++++++++++++++++++++- cmd/blockchaincmd/prompt_owners.go | 4 +--- pkg/constants/constants.go | 1 - 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index 119d7f6d0..90d1b3487 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -6,11 +6,12 @@ import ( "encoding/json" "errors" "fmt" - "github.com/ava-labs/avalanche-cli/pkg/utils" "os" "path/filepath" "strings" + "github.com/ava-labs/avalanche-cli/pkg/utils" + "github.com/ava-labs/avalanche-cli/pkg/binutils" "github.com/ava-labs/avalanche-cli/pkg/cobrautils" "github.com/ava-labs/avalanche-cli/pkg/constants" diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 9edc33d85..56724724e 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -22,7 +22,31 @@ func getValidatorContractManagerAddr() ([]string, bool, error) { controllerAddrPrompt := "Enter Validator Manager Contract controller address" for { // ask in a loop so that if some condition is not met we can keep asking - controlAddr, cancelled, err := getAddrLoop(controllerAddrPrompt, constants.ValidatorManagerController, models.UndefinedNetwork) + controlAddr, cancelled, err := prompts.CaptureListDecision( + // we need this to be able to mock test + app.Prompt, + // the main prompt for entering address keys + controllerAddrPrompt, + // the Capture function to use + func(_ string) (string, error) { + return prompts.PromptAddress( + app.Prompt, + "enable as controller of ValidatorManager contract", + app.GetKeyDir(), + app.GetKey, + "", + models.UndefinedNetwork, + prompts.EVMFormat, + "Enter address", + ) + }, + // the prompt for each address + "", + // label describes the entity we are prompting for (e.g. address, control key, etc.) + "Validator Manager Controller", + //TODO: add info here on what this validator manager controller is + "", + ) if err != nil { return nil, false, err } diff --git a/cmd/blockchaincmd/prompt_owners.go b/cmd/blockchaincmd/prompt_owners.go index 8eeca8ccc..04da2ff1f 100644 --- a/cmd/blockchaincmd/prompt_owners.go +++ b/cmd/blockchaincmd/prompt_owners.go @@ -232,7 +232,7 @@ func enterCustomKeys(network models.Network) ([]string, bool, error) { } // getAddrLoop asks as many addresses the user requires, until Done or Cancel is selected -// TODO: add info for TokenMinter and ValidatorManagerController +// TODO: add info for TokenMinter func getAddrLoop(prompt, label string, network models.Network) ([]string, bool, error) { info := "" goal := "" @@ -243,8 +243,6 @@ func getAddrLoop(prompt, label string, network models.Network) ([]string, bool, goal = "be set as a subnet control key" case constants.TokenMinter: goal = "enable as new native token minter" - case constants.ValidatorManagerController: - goal = "enable as controller of ValidatorManager contract" default: } customPrompt := "Enter P-Chain address (Example: P-...)" diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index d6b2e1628..a4dfc3d49 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -29,7 +29,6 @@ const ( AliasesFileName = "aliases.json" ControlKey = "Control key" TokenMinter = "Native token minter" - ValidatorManagerController = "Validator Manager Controller" SidecarSuffix = SuffixSeparator + SidecarFileName GenesisSuffix = SuffixSeparator + GenesisFileName NodeFileName = "node.json" From 9fe693e2277be052d500bfa40b2887cec36e2238 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 19 Sep 2024 15:47:22 -0400 Subject: [PATCH 31/43] address comments --- cmd/blockchaincmd/deploy.go | 5 +---- cmd/blockchaincmd/prompt_genesis_input.go | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index 90d1b3487..09c4a16a9 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -764,13 +764,10 @@ func LoadBootstrapValidator(filepath string) ([]models.SubnetValidator, error) { } if generateNodeID { for _, subnetValidator := range subnetValidators { - nodeID, publicKey, pop, err := generateNewNodeAndBLS() + subnetValidator.NodeID, subnetValidator.BLSPublicKey, subnetValidator.BLSProofOfPossession, err = generateNewNodeAndBLS() if err != nil { return nil, err } - subnetValidator.NodeID = nodeID - subnetValidator.BLSPublicKey = publicKey - subnetValidator.BLSProofOfPossession = pop } } return subnetValidators, nil diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 56724724e..6f84729cd 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -119,11 +119,11 @@ func promptValidatorManagementType( proofOfStakeOption := models.ProofOfStake explainOption := "Explain the difference" if createFlags.proofOfStake { - sidecar.ValidatorManagement = models.ValidatorManagementTypeFromString(proofOfStakeOption) + sidecar.ValidatorManagement = models.ProofOfStake return nil } if createFlags.proofOfAuthority { - sidecar.ValidatorManagement = models.ValidatorManagementTypeFromString(proofOfAuthorityOption) + sidecar.ValidatorManagement = models.ProofOfAuthority return nil } options := []string{proofOfAuthorityOption, proofOfStakeOption, explainOption} From bfb0c67191603702067b9527bd40eb55087a74d7 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 19 Sep 2024 16:11:39 -0400 Subject: [PATCH 32/43] fix lint --- cmd/blockchaincmd/prompt_genesis_input.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 6f84729cd..4103564b3 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -44,7 +44,7 @@ func getValidatorContractManagerAddr() ([]string, bool, error) { "", // label describes the entity we are prompting for (e.g. address, control key, etc.) "Validator Manager Controller", - //TODO: add info here on what this validator manager controller is + // TODO: add info here on what this validator manager controller is "", ) if err != nil { From 9d7bac82a4a840e7670ed8352a958436cb310f82 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 19 Sep 2024 16:35:37 -0400 Subject: [PATCH 33/43] address comments --- cmd/blockchaincmd/create.go | 81 +----------------------------- cmd/blockchaincmd/prompt_owners.go | 34 ++++--------- pkg/constants/constants.go | 3 -- pkg/models/sidecar.go | 4 +- 4 files changed, 13 insertions(+), 109 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 760e6edd8..4bf9593dd 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -49,8 +49,6 @@ type CreateFlags struct { useExternalGasToken bool proofOfStake bool proofOfAuthority bool - validatorManagerMintOnly bool - tokenMinterAddress []string validatorManagerController []string } @@ -66,8 +64,6 @@ var ( errMutuallyExlusiveVersionOptions = errors.New("version flags --latest,--pre-release,vm-version are mutually exclusive") errMutuallyExclusiveVMConfigOptions = errors.New("--genesis flag disables --evm-chain-id,--evm-defaults,--production-defaults,--test-defaults") errMutuallyExlusiveValidatorManagementOptions = errors.New("validator management type flags --proof-of-authority,--proof-of-stake are mutually exclusive") - errTokenMinterAddressConflict = errors.New("--validator-manager-mint-only means that no additional addresses can be provided in --token-minter-address") - errTokenMinterAddressForPoS = errors.New("--token-minter-address is only applicable to proof of authority") ) // avalanche blockchain create @@ -113,8 +109,6 @@ configuration, pass the -f flag.`, cmd.Flags().BoolVar(&createFlags.useExternalGasToken, "external-gas-token", false, "use a gas token from another blockchain") cmd.Flags().BoolVar(&createFlags.proofOfAuthority, "proof-of-authority", false, "use proof of authority for validator management") cmd.Flags().BoolVar(&createFlags.proofOfStake, "proof-of-stake", false, "use proof of stake for validator management") - cmd.Flags().BoolVar(&createFlags.validatorManagerMintOnly, "validator-manager-mint-only", false, "only enable validator manager contract to mint new native tokens") - cmd.Flags().StringSliceVar(&createFlags.tokenMinterAddress, "token-minter-address", nil, "addresses that can mint new native tokens (for proof of authority validator management only)") cmd.Flags().StringSliceVar(&createFlags.validatorManagerController, "validator-manager-controller", nil, "addresses that will control Validator Manager contract") return cmd } @@ -209,15 +203,6 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return errMutuallyExlusiveValidatorManagementOptions } - if len(createFlags.tokenMinterAddress) > 0 { - if createFlags.proofOfStake { - return errTokenMinterAddressForPoS - } - if createFlags.validatorManagerMintOnly { - return errTokenMinterAddressConflict - } - } - // get vm kind vmType, err := vm.PromptVMType(app, createFlags.useSubnetEvm, createFlags.useCustomVM) if err != nil { @@ -379,22 +364,6 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { if err = promptValidatorManagementType(app, sc); err != nil { return err } - if sc.ValidatorManagement == models.ProofOfAuthority { - if !createFlags.validatorManagerMintOnly && createFlags.tokenMinterAddress == nil { - createFlags.tokenMinterAddress, err = getTokenMinterAddr() - if err != nil { - return err - } - } - } - if !createFlags.validatorManagerMintOnly { - if len(createFlags.tokenMinterAddress) > 0 { - ux.Logger.GreenCheckmarkToUser("Addresses added as new native token minter %s", createFlags.tokenMinterAddress) - } else { - ux.Logger.GreenCheckmarkToUser("No additional addresses added as new native token minter") - } - } - sc.NewNativeTokenMinter = createFlags.tokenMinterAddress if createFlags.validatorManagerController == nil { var cancelled bool createFlags.validatorManagerController, cancelled, err = getValidatorContractManagerAddr() @@ -426,54 +395,8 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { } func getValidatorContractManagerAddr() ([]string, bool, error) { - controllerAddrPrompt := "Enter Validator Manager Contract controller address" - for { - // ask in a loop so that if some condition is not met we can keep asking - controlAddr, cancelled, err := getAddrLoop(controllerAddrPrompt, constants.ValidatorManagerController, models.UndefinedNetwork) - if err != nil { - return nil, false, err - } - if cancelled { - return nil, cancelled, nil - } - if len(controlAddr) != 0 { - return controlAddr, false, nil - } - ux.Logger.RedXToUser("An address to control Validator Manage Contract is required before proceeding") - } -} - -// Configure which addresses may make mint new native tokens -func getTokenMinterAddr() ([]string, error) { - addTokenMinterAddrPrompt := "Currently only Validator Manager Contract can mint new native tokens" - ux.Logger.PrintToUser(addTokenMinterAddrPrompt) - yes, err := app.Prompt.CaptureNoYes("Add additional addresses that can mint new native tokens?") - if err != nil { - return nil, err - } - if !yes { - return nil, nil - } - addr, cancelled, err := getAddr() - if err != nil { - return nil, err - } - if cancelled { - return nil, nil - } - return addr, nil -} - -func getAddr() ([]string, bool, error) { - addrPrompt := "Enter addresses that can mint new native tokens" - addr, cancelled, err := getAddrLoop(addrPrompt, constants.TokenMinter, models.UndefinedNetwork) - if err != nil { - return nil, false, err - } - if cancelled { - return nil, cancelled, nil - } - return addr, false, nil + // TODO: replace this with implementation in validator prompt PR + return nil, false, nil } func addSubnetEVMGenesisPrefundedAddress(genesisBytes []byte, address string, balance string) ([]byte, error) { diff --git a/cmd/blockchaincmd/prompt_owners.go b/cmd/blockchaincmd/prompt_owners.go index a5c87a905..5233890af 100644 --- a/cmd/blockchaincmd/prompt_owners.go +++ b/cmd/blockchaincmd/prompt_owners.go @@ -217,7 +217,7 @@ func enterCustomKeys(network models.Network) ([]string, bool, error) { controlKeysPrompt := "Enter control keys" for { // ask in a loop so that if some condition is not met we can keep asking - controlKeys, cancelled, err := getAddrLoop(controlKeysPrompt, constants.ControlKey, network) + controlKeys, cancelled, err := controlKeysLoop(controlKeysPrompt, network) if err != nil { return nil, false, err } @@ -231,43 +231,27 @@ func enterCustomKeys(network models.Network) ([]string, bool, error) { } } -// getAddrLoop asks as many addresses the user requires, until Done or Cancel is selected -// TODO: add info for TokenMinter and ValidatorManagerController -func getAddrLoop(prompt, label string, network models.Network) ([]string, bool, error) { - info := "" - goal := "" - switch label { - case constants.ControlKey: - info = "Control keys are P-Chain addresses which have admin rights on the subnet.\n" + - "Only private keys which control such addresses are allowed to make changes on the subnet" - goal = "be set as a subnet control key" - case constants.TokenMinter: - goal = "enable as new native token minter" - case constants.ValidatorManagerController: - goal = "enable as controller of ValidatorManager contract" - default: - } +// controlKeysLoop asks as many controlkeys the user requires, until Done or Cancel is selected +func controlKeysLoop(controlKeysPrompt string, network models.Network) ([]string, bool, error) { + label := "Control key" + info := "Control keys are P-Chain addresses which have admin rights on the subnet.\n" + + "Only private keys which control such addresses are allowed to make changes on the subnet" customPrompt := "Enter P-Chain address (Example: P-...)" - addressFormat := prompts.PChainFormat - if label != constants.ControlKey { - customPrompt = "Enter address" - addressFormat = prompts.EVMFormat - } return prompts.CaptureListDecision( // we need this to be able to mock test app.Prompt, // the main prompt for entering address keys - prompt, + controlKeysPrompt, // the Capture function to use func(_ string) (string, error) { return prompts.PromptAddress( app.Prompt, - goal, + "be set as a subnet control key", app.GetKeyDir(), app.GetKey, "", network, - addressFormat, + prompts.PChainFormat, customPrompt, ) }, diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index dd127aca2..df1b76fe2 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -27,9 +27,6 @@ const ( SidecarFileName = "sidecar.json" GenesisFileName = "genesis.json" AliasesFileName = "aliases.json" - ControlKey = "Control key" - TokenMinter = "Native token minter" - ValidatorManagerController = "Validator Manager Controller" SidecarSuffix = SuffixSeparator + SidecarFileName GenesisSuffix = SuffixSeparator + GenesisFileName NodeFileName = "node.json" diff --git a/pkg/models/sidecar.go b/pkg/models/sidecar.go index ff09ed6e6..3c962022a 100644 --- a/pkg/models/sidecar.go +++ b/pkg/models/sidecar.go @@ -38,10 +38,10 @@ type Sidecar struct { TeleporterVersion string RunRelayer bool // SubnetEVM based VM's only - SubnetEVMMainnetChainID uint + SubnetEVMMainnetChainID uint + // TODO: remove if not needed for subnet acp 77 create flow once avalnache go releases etna ValidatorManagement ValidatorManagementType ValidatorManagerController []string - NewNativeTokenMinter []string } func (sc Sidecar) GetVMID() (string, error) { From 9e2a87c6f3240ac78bc49cdb7d1a5e9f98b3eda9 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 19 Sep 2024 16:42:14 -0400 Subject: [PATCH 34/43] address comments --- cmd/blockchaincmd/prompt_genesis_input.go | 33 ----------------------- 1 file changed, 33 deletions(-) diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 4103564b3..27b2019ff 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -60,39 +60,6 @@ func getValidatorContractManagerAddr() ([]string, bool, error) { } } -// Configure which addresses may make mint new native tokens -func getTokenMinterAddr() ([]string, error) { - addTokenMinterAddrPrompt := "Currently only Validator Manager Contract can mint new native tokens" - ux.Logger.PrintToUser(addTokenMinterAddrPrompt) - yes, err := app.Prompt.CaptureNoYes("Add additional addresses that can mint new native tokens?") - if err != nil { - return nil, err - } - if !yes { - return nil, nil - } - addresses, cancelled, err := getAddr() - if err != nil { - return nil, err - } - if cancelled { - return nil, nil - } - return addresses, nil -} - -func getAddr() ([]string, bool, error) { - addrPrompt := "Enter addresses that can mint new native tokens" - addr, cancelled, err := getAddrLoop(addrPrompt, constants.TokenMinter, models.UndefinedNetwork) - if err != nil { - return nil, false, err - } - if cancelled { - return nil, cancelled, nil - } - return addr, false, nil -} - func promptProofOfPossession() (string, string, error) { ux.Logger.PrintToUser("Next, we need the public key and proof of possession of the node's BLS") ux.Logger.PrintToUser("Check https://docs.avax.network/api-reference/info-api#infogetnodeid for instructions on calling info.getNodeID API") From b7408b36b8d206477423e695348952c156e8a64d Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 19 Sep 2024 16:47:47 -0400 Subject: [PATCH 35/43] fix test --- cmd/blockchaincmd/create.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 91bb1a6a4..aa7f9bb47 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -198,10 +198,6 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return errMutuallyExlusiveValidatorManagementOptions } - if createFlags.proofOfAuthority { - return errMutuallyExlusiveValidatorManagementOptions - } - // get vm kind vmType, err := vm.PromptVMType(app, createFlags.useSubnetEvm, createFlags.useCustomVM) if err != nil { From 2a8c14d3f0d97a8b18273293404108dfd8b6c265 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 20 Sep 2024 12:52:52 -0400 Subject: [PATCH 36/43] address comments --- cmd/blockchaincmd/create.go | 20 +------------------- cmd/blockchaincmd/deploy.go | 23 ++++++++++++++++++++++- cmd/transactioncmd/transaction_commit.go | 2 +- pkg/application/app.go | 2 ++ pkg/models/sidecar.go | 4 ++-- 5 files changed, 28 insertions(+), 23 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index aa7f9bb47..164c674ba 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -49,7 +49,6 @@ type CreateFlags struct { useExternalGasToken bool proofOfStake bool proofOfAuthority bool - validatorManagerController []string } var ( @@ -359,19 +358,7 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { if err = promptValidatorManagementType(app, sc); err != nil { return err } - if createFlags.validatorManagerController == nil { - var cancelled bool - createFlags.validatorManagerController, cancelled, err = getValidatorContractManagerAddr() - if err != nil { - return err - } - if cancelled { - return fmt.Errorf("user cancelled operation") - } - } - sc.ValidatorManagerController = createFlags.validatorManagerController - // TODO: add description of what Validator Manager Contract controller does - ux.Logger.GreenCheckmarkToUser("Validator Manager Contract controller %s", createFlags.validatorManagerController) + if err = app.WriteGenesisFile(blockchainName, genesisBytes); err != nil { return err } @@ -389,11 +376,6 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return nil } -func getValidatorContractManagerAddr() ([]string, bool, error) { - // TODO: replace this with implementation in validator prompt PR - return nil, false, nil -} - func addSubnetEVMGenesisPrefundedAddress(genesisBytes []byte, address string, balance string) ([]byte, error) { var genesisMap map[string]interface{} if err := json.Unmarshal(genesisBytes, &genesisMap); err != nil { diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index 23f451bf6..5665e4be0 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/ethereum/go-ethereum/common" "os" "path/filepath" "strings" @@ -51,6 +52,7 @@ var ( skipCreatePrompt bool avagoBinaryPath string subnetOnly bool + validatorManagerOwner string teleporterEsp subnet.TeleporterEsp errMutuallyExlusiveControlKeys = errors.New("--control-keys and --same-control-key are mutually exclusive") @@ -100,6 +102,7 @@ so you can take your locally tested Subnet and deploy it on Fuji or Mainnet.`, cmd.Flags().StringVar(&teleporterEsp.MessengerDeployerAddressPath, "teleporter-messenger-deployer-address-path", "", "path to a teleporter messenger deployer address file") cmd.Flags().StringVar(&teleporterEsp.MessengerDeployerTxPath, "teleporter-messenger-deployer-tx-path", "", "path to a teleporter messenger deployer tx file") cmd.Flags().StringVar(&teleporterEsp.RegistryBydecodePath, "teleporter-registry-bytecode-path", "", "path to a teleporter registry bytecode file") + cmd.Flags().StringVar(&validatorManagerOwner, "validator-manager-owner", "", "EVM address that controls Validator Manager Controller (for Proof of Authority only)") return cmd } @@ -285,6 +288,10 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { return errors.New("unable to deploy subnets imported from a repo") } + if sidecar.ValidatorManagement != models.ProofOfAuthority && validatorManagerOwner != "" { + return errors.New("--validator-manager-controller flag cannot be used when blockchain validator management type is not Proof of Authority") + } + if outputTxPath != "" { if _, err := os.Stat(outputTxPath); err == nil { return fmt.Errorf("outputTxPath %q already exists", outputTxPath) @@ -386,6 +393,7 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { deployInfo.BlockchainID, deployInfo.TeleporterMessengerAddress, deployInfo.TeleporterRegistryAddress, + validatorManagerOwner, ); err != nil { return err } @@ -488,6 +496,15 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { } ux.Logger.PrintToUser("Your subnet auth keys for chain creation: %s", subnetAuthKeys) + if validatorManagerOwner == "" { + validatorManagerOwnerEVMAddress, err := getValidatorContractOwnerAddr() + if err != nil { + return err + } + validatorManagerOwner = validatorManagerOwnerEVMAddress.String() + } + ux.Logger.PrintToUser("Validator Manager Contract controller address %s", validatorManagerOwner) + // deploy to public network deployer := subnet.NewPublicDeployer(app, kc, network) @@ -552,7 +569,11 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { // update sidecar // TODO: need to do something for backwards compatibility? - return app.UpdateSidecarNetworks(&sidecar, network, subnetID, blockchainID, "", "") + return app.UpdateSidecarNetworks(&sidecar, network, subnetID, blockchainID, "", "", validatorManagerOwner) +} + +func getValidatorContractOwnerAddr() (common.Address, error) { + return app.Prompt.CaptureAddress("What is the EVM address that will control the Validator Manager Contract?") } func ValidateSubnetNameAndGetChains(args []string) ([]string, error) { diff --git a/cmd/transactioncmd/transaction_commit.go b/cmd/transactioncmd/transaction_commit.go index 21712c924..6f2d2c2ff 100644 --- a/cmd/transactioncmd/transaction_commit.go +++ b/cmd/transactioncmd/transaction_commit.go @@ -97,7 +97,7 @@ func commitTx(_ *cobra.Command, args []string) error { if err := blockchaincmd.PrintDeployResults(subnetName, subnetID, txID); err != nil { return err } - return app.UpdateSidecarNetworks(&sc, network, subnetID, txID, "", "") + return app.UpdateSidecarNetworks(&sc, network, subnetID, txID, "", "", sc.Networks[network.Name()].PoAValidatorManagerOwner) } return nil diff --git a/pkg/application/app.go b/pkg/application/app.go index cc2b2f1fe..2afef53c1 100644 --- a/pkg/application/app.go +++ b/pkg/application/app.go @@ -520,6 +520,7 @@ func (app *Avalanche) UpdateSidecarNetworks( blockchainID ids.ID, teleporterMessengerAddress string, teleporterRegistryAddress string, + validatorManagerController string, ) error { if sc.Networks == nil { sc.Networks = make(map[string]models.NetworkData) @@ -530,6 +531,7 @@ func (app *Avalanche) UpdateSidecarNetworks( RPCVersion: sc.RPCVersion, TeleporterMessengerAddress: teleporterMessengerAddress, TeleporterRegistryAddress: teleporterRegistryAddress, + PoAValidatorManagerOwner: validatorManagerController, } if err := app.UpdateSidecar(sc); err != nil { return fmt.Errorf("creation of chains and subnet was successful, but failed to update sidecar: %w", err) diff --git a/pkg/models/sidecar.go b/pkg/models/sidecar.go index 3c962022a..71245ed00 100644 --- a/pkg/models/sidecar.go +++ b/pkg/models/sidecar.go @@ -13,6 +13,7 @@ type NetworkData struct { RPCVersion int TeleporterMessengerAddress string TeleporterRegistryAddress string + PoAValidatorManagerOwner string } type Sidecar struct { @@ -40,8 +41,7 @@ type Sidecar struct { // SubnetEVM based VM's only SubnetEVMMainnetChainID uint // TODO: remove if not needed for subnet acp 77 create flow once avalnache go releases etna - ValidatorManagement ValidatorManagementType - ValidatorManagerController []string + ValidatorManagement ValidatorManagementType } func (sc Sidecar) GetVMID() (string, error) { From 1cc81465e29239b3659e2bcd61f41606923972ab Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 20 Sep 2024 13:02:04 -0400 Subject: [PATCH 37/43] fix lint --- cmd/blockchaincmd/deploy.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index 0c2f60c08..a23ebb2ff 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -6,11 +6,12 @@ import ( "encoding/json" "errors" "fmt" - "github.com/ethereum/go-ethereum/common" "os" "path/filepath" "strings" + "github.com/ethereum/go-ethereum/common" + "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/binutils" From b7f9df74a3d9bef730a6d0d2e85d1bfe02fc3c47 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 23 Sep 2024 12:57:04 -0400 Subject: [PATCH 38/43] address coments --- cmd/blockchaincmd/prompt_genesis_input.go | 65 ++++------------------- cmd/blockchaincmd/prompt_owners.go | 4 +- pkg/constants/constants.go | 18 ++++--- pkg/models/sidecar.go | 10 +--- pkg/prompts/validations.go | 2 +- 5 files changed, 25 insertions(+), 74 deletions(-) diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 27b2019ff..1d5c71a20 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -18,48 +18,6 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/signer" ) -func getValidatorContractManagerAddr() ([]string, bool, error) { - controllerAddrPrompt := "Enter Validator Manager Contract controller address" - for { - // ask in a loop so that if some condition is not met we can keep asking - controlAddr, cancelled, err := prompts.CaptureListDecision( - // we need this to be able to mock test - app.Prompt, - // the main prompt for entering address keys - controllerAddrPrompt, - // the Capture function to use - func(_ string) (string, error) { - return prompts.PromptAddress( - app.Prompt, - "enable as controller of ValidatorManager contract", - app.GetKeyDir(), - app.GetKey, - "", - models.UndefinedNetwork, - prompts.EVMFormat, - "Enter address", - ) - }, - // the prompt for each address - "", - // label describes the entity we are prompting for (e.g. address, control key, etc.) - "Validator Manager Controller", - // TODO: add info here on what this validator manager controller is - "", - ) - if err != nil { - return nil, false, err - } - if cancelled { - return nil, cancelled, nil - } - if len(controlAddr) != 0 { - return controlAddr, false, nil - } - ux.Logger.RedXToUser("An address to control Validator Manage Contract is required before proceeding") - } -} - func promptProofOfPossession() (string, string, error) { ux.Logger.PrintToUser("Next, we need the public key and proof of possession of the node's BLS") ux.Logger.PrintToUser("Check https://docs.avax.network/api-reference/info-api#infogetnodeid for instructions on calling info.getNodeID API") @@ -82,8 +40,6 @@ func promptValidatorManagementType( app *application.Avalanche, sidecar *models.Sidecar, ) error { - proofOfAuthorityOption := models.ProofOfAuthority - proofOfStakeOption := models.ProofOfStake explainOption := "Explain the difference" if createFlags.proofOfStake { sidecar.ValidatorManagement = models.ProofOfStake @@ -93,8 +49,7 @@ func promptValidatorManagementType( sidecar.ValidatorManagement = models.ProofOfAuthority return nil } - options := []string{proofOfAuthorityOption, proofOfStakeOption, explainOption} - var subnetTypeStr string + options := []string{models.ProofOfAuthority, models.ProofOfStake, explainOption} for { option, err := app.Prompt.CaptureList( "Which validator management protocol would you like to use in your blockchain?", @@ -104,16 +59,16 @@ func promptValidatorManagementType( return err } switch option { - case proofOfAuthorityOption: - subnetTypeStr = models.ProofOfAuthority - case proofOfStakeOption: - subnetTypeStr = models.ProofOfStake + case models.ProofOfAuthority: + sidecar.ValidatorManagement = models.ValidatorManagementTypeFromString(option) + case models.ProofOfStake: + ux.Logger.RedXToUser("Proof of Stake is currently unavailable") + continue case explainOption: continue } break } - sidecar.ValidatorManagement = models.ValidatorManagementTypeFromString(subnetTypeStr) return nil } @@ -191,8 +146,8 @@ func promptBootstrapValidators(network models.Network) ([]models.SubnetValidator previousAddr = changeAddr subnetValidator := models.SubnetValidator{ NodeID: nodeID.String(), - Weight: constants.DefaultBootstrapValidatorWeight, - Balance: constants.InitialBalanceBootstrapValidator, + Weight: constants.BootstrapValidatorWeight, + Balance: constants.BootstrapValidatorBalance, BLSPublicKey: publicKey, BLSProofOfPossession: pop, ChangeOwnerAddr: changeAddr, @@ -229,10 +184,10 @@ func validateSubnetValidatorsJSON(generateNewNodeID bool, validatorJSONS []model return err } } - if validatorJSON.Weight <= 0 { + if validatorJSON.Weight == 0 { return fmt.Errorf("bootstrap validator weight has to be greater than 0") } - if validatorJSON.Balance <= 0 { + if validatorJSON.Balance == 0 { return fmt.Errorf("bootstrap validator balance has to be greater than 0") } } diff --git a/cmd/blockchaincmd/prompt_owners.go b/cmd/blockchaincmd/prompt_owners.go index 5b1e09832..1dbbad28d 100644 --- a/cmd/blockchaincmd/prompt_owners.go +++ b/cmd/blockchaincmd/prompt_owners.go @@ -291,7 +291,7 @@ func getThreshold(maxLen int) (uint32, error) { } func getKeyForChangeOwner(previouslyUsedAddr string, network models.Network) (string, error) { - moreKeysPrompt := "Which key would you like to set as change owner for leftover AVAX if the node is removed from validator set?" + changeAddrPrompt := "Which key would you like to set as change owner for leftover AVAX if the node is removed from validator set?" const ( getFromStored = "Get address from an existing stored key (created from avalanche key create or avalanche key import)" @@ -303,7 +303,7 @@ func getKeyForChangeOwner(previouslyUsedAddr string, network models.Network) (st if previouslyUsedAddr != "" { listOptions = []string{previousAddres, getFromStored, custom} } - listDecision, err := app.Prompt.CaptureList(moreKeysPrompt, listOptions) + listDecision, err := app.Prompt.CaptureList(changeAddrPrompt, listOptions) if err != nil { return "", err } diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 2e7d56951..8486b6f62 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -151,14 +151,16 @@ const ( Disable = "disable" - TimeParseLayout = "2006-01-02 15:04:05" - MinStakeWeight = 1 - InitialBalanceBootstrapValidator = 1 - DefaultBootstrapValidatorWeight = 1000000 - DefaultStakeWeight = 20 - AVAXSymbol = "AVAX" - DefaultFujiStakeDuration = "48h" - DefaultMainnetStakeDuration = "336h" + TimeParseLayout = "2006-01-02 15:04:05" + MinStakeWeight = 1 + // Default balance when we prompt users for bootstrap validators + BootstrapValidatorBalance = 1 + // Default weight when we prompt users for bootstrap validators + BootstrapValidatorWeight = 1000000 + DefaultStakeWeight = 20 + AVAXSymbol = "AVAX" + DefaultFujiStakeDuration = "48h" + DefaultMainnetStakeDuration = "336h" // The absolute minimum is 25 seconds, but set to 1 minute to allow for // time to go through the command DevnetStakingStartLeadTime = 30 * time.Second diff --git a/pkg/models/sidecar.go b/pkg/models/sidecar.go index 0ac851d10..d256de9eb 100644 --- a/pkg/models/sidecar.go +++ b/pkg/models/sidecar.go @@ -63,12 +63,6 @@ func (sc Sidecar) GetVMID() (string, error) { } func (sc Sidecar) NetworkDataIsEmpty(network string) bool { - if sc.Networks[network].SubnetID == ids.Empty && - sc.Networks[network].BlockchainID == ids.Empty && - sc.Networks[network].RPCVersion == 0 && - sc.Networks[network].TeleporterMessengerAddress == "" && - sc.Networks[network].TeleporterRegistryAddress == "" { - return true - } - return false + _, networkExists := sc.Networks[network] + return !networkExists } diff --git a/pkg/prompts/validations.go b/pkg/prompts/validations.go index 62dbb4215..3a510c70f 100644 --- a/pkg/prompts/validations.go +++ b/pkg/prompts/validations.go @@ -134,7 +134,7 @@ func validateBootstrapValidatorBalance(input string) error { if err != nil { return err } - if val <= 0 { + if val == 0 { return fmt.Errorf("initial bootstrap validator balance must be greater than 0 AVAX") } return nil From 1ea414fdf81049b99444e2bcb5c0e3ad462ee5de Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 23 Sep 2024 13:06:55 -0400 Subject: [PATCH 39/43] address coments --- cmd/blockchaincmd/prompt_genesis_input.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 1d5c71a20..529c9edcf 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -52,7 +52,7 @@ func promptValidatorManagementType( options := []string{models.ProofOfAuthority, models.ProofOfStake, explainOption} for { option, err := app.Prompt.CaptureList( - "Which validator management protocol would you like to use in your blockchain?", + "Which validator management type would you like to use in your blockchain?", options, ) if err != nil { From 44bfcd7a527210dfa5d535fb47c44375e5e83333 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 23 Sep 2024 18:25:52 -0400 Subject: [PATCH 40/43] fix test --- tests/e2e/assets/test_bootstrap_validator.json | 10 ++++++++++ tests/e2e/commands/subnet.go | 2 ++ tests/e2e/utils/constants.go | 4 ++-- 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 tests/e2e/assets/test_bootstrap_validator.json diff --git a/tests/e2e/assets/test_bootstrap_validator.json b/tests/e2e/assets/test_bootstrap_validator.json new file mode 100644 index 000000000..ec8799573 --- /dev/null +++ b/tests/e2e/assets/test_bootstrap_validator.json @@ -0,0 +1,10 @@ +[ + { + "NodeID": "NodeID-144PM69m93kSFyfTHMwULTmoGZSWzQ4C1", + "Weight": 20, + "Balance": 5, + "BLSPublicKey": "0x80b7851ce335cee149b7cfffbf6cf0bbca3c9b25026a24056e610976d095906e833a66d5ca5c56c23a3fe50e8785a81f", + "BLSProofOfPossession": "0x89e1d6d47ff04ec0c78501a029865140e9ec12baba75a95bfc5710b3fecb8db4b6cecb5ccb1136e19f88db0539deb4420306dd60145024197b41cf89179790f20146fba398bc4d13e08540ea812207f736ca007275e4ebdb840065fdb38573de", + "ChangeOwnerAddr": "P-custom1y5ku603lh583xs9v50p8kk0awcqzgeq0mezkqr" + } +] diff --git a/tests/e2e/commands/subnet.go b/tests/e2e/commands/subnet.go index f0e1f7298..2bee66973 100644 --- a/tests/e2e/commands/subnet.go +++ b/tests/e2e/commands/subnet.go @@ -45,6 +45,7 @@ func CreateSubnetEvmConfigWithVersion(subnetName string, genesisPath string, ver subnetName, "--proof-of-authority", "--" + constants.SkipUpdateFlag, + "--bootstrap-filepath=" + utils.BootstrapValidatorPath, "--teleporter=false", "--evm-token", "TOK", @@ -124,6 +125,7 @@ func CreateCustomVMConfig(subnetName string, genesisPath string, vmPath string) "--genesis", genesisPath, "--proof-of-authority", + "--bootstrap-filepath="+utils.BootstrapValidatorPath, "--custom", subnetName, "--custom-vm-path", diff --git a/tests/e2e/utils/constants.go b/tests/e2e/utils/constants.go index 690eccb6d..462e15d4f 100644 --- a/tests/e2e/utils/constants.go +++ b/tests/e2e/utils/constants.go @@ -28,8 +28,8 @@ const ( EwoqKeyPath = "tests/e2e/assets/ewoq_key.pk" SubnetEvmAllowFeeRecpPath = "tests/e2e/assets/test_subnet_evm_allowFeeRecps_genesis.json" SubnetEvmGenesisBadPath = "tests/e2e/assets/test_subnet_evm_genesis_bad.json" - - PluginDirExt = "plugins" + BootstrapValidatorPath = "tests/e2e/assets/test_bootstrap_validator.json" + PluginDirExt = "plugins" ledgerSimDir = "./tests/e2e/ledgerSim" basicLedgerSimScript = "./launchAndApproveTxs.ts" From d40b767b055d707038c070ced85a3d4481d67c9d Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 23 Sep 2024 18:59:49 -0400 Subject: [PATCH 41/43] fix test --- tests/e2e/commands/subnet.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/e2e/commands/subnet.go b/tests/e2e/commands/subnet.go index 2bee66973..24d048337 100644 --- a/tests/e2e/commands/subnet.go +++ b/tests/e2e/commands/subnet.go @@ -45,7 +45,6 @@ func CreateSubnetEvmConfigWithVersion(subnetName string, genesisPath string, ver subnetName, "--proof-of-authority", "--" + constants.SkipUpdateFlag, - "--bootstrap-filepath=" + utils.BootstrapValidatorPath, "--teleporter=false", "--evm-token", "TOK", @@ -125,7 +124,6 @@ func CreateCustomVMConfig(subnetName string, genesisPath string, vmPath string) "--genesis", genesisPath, "--proof-of-authority", - "--bootstrap-filepath="+utils.BootstrapValidatorPath, "--custom", subnetName, "--custom-vm-path", @@ -222,7 +220,7 @@ func DeploySubnetLocallyWithArgs(subnetName string, version string, confPath str gomega.Expect(exists).Should(gomega.BeTrue()) // Deploy subnet locally - cmdArgs := []string{SubnetCmd, "deploy", "--local", subnetName, "--" + constants.SkipUpdateFlag} + cmdArgs := []string{SubnetCmd, "deploy", "--local", subnetName, "--" + constants.SkipUpdateFlag, "--bootstrap-filepath=" + utils.BootstrapValidatorPath} if version != "" { cmdArgs = append(cmdArgs, "--avalanchego-version", version) } @@ -260,7 +258,7 @@ func DeploySubnetLocallyWithArgsAndOutput(subnetName string, version string, con gomega.Expect(exists).Should(gomega.BeTrue()) // Deploy subnet locally - cmdArgs := []string{SubnetCmd, "deploy", "--local", subnetName, "--" + constants.SkipUpdateFlag} + cmdArgs := []string{SubnetCmd, "deploy", "--local", subnetName, "--" + constants.SkipUpdateFlag, "--bootstrap-filepath=" + utils.BootstrapValidatorPath} if version != "" { cmdArgs = append(cmdArgs, "--avalanchego-version", version) } @@ -304,6 +302,7 @@ func SimulateFujiDeploy( SubnetCmd, "deploy", "--fuji", + "--bootstrap-filepath="+utils.BootstrapValidatorPath, "--threshold", "1", "--key", @@ -354,6 +353,7 @@ func SimulateMainnetDeploy( []string{ SubnetCmd, "deploy", + "--bootstrap-filepath=" + utils.BootstrapValidatorPath, "--mainnet", "--threshold", "1", @@ -393,6 +393,7 @@ func SimulateMultisigMainnetDeploy( SubnetCmd, "deploy", "--mainnet", + "--bootstrap-filepath=" + utils.BootstrapValidatorPath, "--control-keys", strings.Join(subnetControlAddrs, ","), "--subnet-auth-keys", From 361bf6040ad611127477ec335158a72710a5f186 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 23 Sep 2024 19:12:38 -0400 Subject: [PATCH 42/43] fix test --- cmd/blockchaincmd/deploy.go | 18 +----------------- cmd/transactioncmd/transaction_commit.go | 2 +- pkg/application/app.go | 2 -- pkg/models/sidecar.go | 1 - 4 files changed, 2 insertions(+), 21 deletions(-) diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index a23ebb2ff..b881252b2 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -62,7 +62,6 @@ var ( subnetOnly bool icmSpec subnet.ICMSpec generateNodeID bool - validatorManagerOwner string bootstrapValidatorsJSONFilePath string errMutuallyExlusiveControlKeys = errors.New("--control-keys and --same-control-key are mutually exclusive") @@ -113,7 +112,6 @@ so you can take your locally tested Subnet and deploy it on Fuji or Mainnet.`, cmd.Flags().StringVar(&icmSpec.MessengerDeployerAddressPath, "teleporter-messenger-deployer-address-path", "", "path to an interchain messenger deployer address file") cmd.Flags().StringVar(&icmSpec.MessengerDeployerTxPath, "teleporter-messenger-deployer-tx-path", "", "path to an interchain messenger deployer tx file") cmd.Flags().StringVar(&icmSpec.RegistryBydecodePath, "teleporter-registry-bytecode-path", "", "path to an interchain messenger registry bytecode file") - cmd.Flags().StringVar(&validatorManagerOwner, "validator-manager-owner", "", "EVM address that controls Validator Manager Controller (for Proof of Authority only)") cmd.Flags().StringVar(&bootstrapValidatorsJSONFilePath, "bootstrap-filepath", "", "JSON file path that provides details about bootstrap validators, leave Node-ID and BLS values empty if using --generate-node-id=true") cmd.Flags().BoolVar(&generateNodeID, "generate-node-id", false, "whether to create new node id for bootstrap validators (Node-ID and BLS values in bootstrap JSON file will be overridden if --bootstrap-filepath flag is used)") return cmd @@ -309,10 +307,6 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { return errors.New("unable to deploy subnets imported from a repo") } - if sidecar.ValidatorManagement != models.ProofOfAuthority && validatorManagerOwner != "" { - return errors.New("--validator-manager-controller flag cannot be used when blockchain validator management type is not Proof of Authority") - } - if outputTxPath != "" { if _, err := os.Stat(outputTxPath); err == nil { return fmt.Errorf("outputTxPath %q already exists", outputTxPath) @@ -422,7 +416,6 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { deployInfo.ICMMessengerAddress, deployInfo.ICMRegistryAddress, bootstrapValidators, - validatorManagerOwner, ); err != nil { return err } @@ -525,15 +518,6 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { } ux.Logger.PrintToUser("Your subnet auth keys for chain creation: %s", subnetAuthKeys) - if validatorManagerOwner == "" { - validatorManagerOwnerEVMAddress, err := getValidatorContractOwnerAddr() - if err != nil { - return err - } - validatorManagerOwner = validatorManagerOwnerEVMAddress.String() - } - ux.Logger.PrintToUser("Validator Manager Contract controller address %s", validatorManagerOwner) - // deploy to public network deployer := subnet.NewPublicDeployer(app, kc, network) @@ -598,7 +582,7 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { // update sidecar // TODO: need to do something for backwards compatibility? - return app.UpdateSidecarNetworks(&sidecar, network, subnetID, blockchainID, "", "", bootstrapValidators, validatorManagerOwner) + return app.UpdateSidecarNetworks(&sidecar, network, subnetID, blockchainID, "", "", bootstrapValidators) } func getValidatorContractOwnerAddr() (common.Address, error) { diff --git a/cmd/transactioncmd/transaction_commit.go b/cmd/transactioncmd/transaction_commit.go index c74a08a07..f1ff6568d 100644 --- a/cmd/transactioncmd/transaction_commit.go +++ b/cmd/transactioncmd/transaction_commit.go @@ -97,7 +97,7 @@ func commitTx(_ *cobra.Command, args []string) error { if err := blockchaincmd.PrintDeployResults(subnetName, subnetID, txID); err != nil { return err } - return app.UpdateSidecarNetworks(&sc, network, subnetID, txID, "", "", sc.Networks[network.Name()].BootstrapValidators, sc.Networks[network.Name()].PoAValidatorManagerOwner) + return app.UpdateSidecarNetworks(&sc, network, subnetID, txID, "", "", sc.Networks[network.Name()].BootstrapValidators) } return nil diff --git a/pkg/application/app.go b/pkg/application/app.go index 3cafaef58..474f5e247 100644 --- a/pkg/application/app.go +++ b/pkg/application/app.go @@ -534,7 +534,6 @@ func (app *Avalanche) UpdateSidecarNetworks( teleporterMessengerAddress string, teleporterRegistryAddress string, bootstrapValidators []models.SubnetValidator, - validatorManagerController string, ) error { if sc.Networks == nil { sc.Networks = make(map[string]models.NetworkData) @@ -545,7 +544,6 @@ func (app *Avalanche) UpdateSidecarNetworks( RPCVersion: sc.RPCVersion, TeleporterMessengerAddress: teleporterMessengerAddress, TeleporterRegistryAddress: teleporterRegistryAddress, - PoAValidatorManagerOwner: validatorManagerController, BootstrapValidators: bootstrapValidators, } if err := app.UpdateSidecar(sc); err != nil { diff --git a/pkg/models/sidecar.go b/pkg/models/sidecar.go index d256de9eb..36bd429cb 100644 --- a/pkg/models/sidecar.go +++ b/pkg/models/sidecar.go @@ -13,7 +13,6 @@ type NetworkData struct { RPCVersion int TeleporterMessengerAddress string TeleporterRegistryAddress string - PoAValidatorManagerOwner string RPCEndpoints []string WSEndpoints []string BootstrapValidators []SubnetValidator From 7cc625ee3e4f6c7f5d4b925210aef612a77352d4 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 23 Sep 2024 19:23:46 -0400 Subject: [PATCH 43/43] fix lint --- cmd/blockchaincmd/deploy.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index b881252b2..a4177a31d 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -10,8 +10,6 @@ import ( "path/filepath" "strings" - "github.com/ethereum/go-ethereum/common" - "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/binutils" @@ -585,10 +583,6 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { return app.UpdateSidecarNetworks(&sidecar, network, subnetID, blockchainID, "", "", bootstrapValidators) } -func getValidatorContractOwnerAddr() (common.Address, error) { - return app.Prompt.CaptureAddress("What is the EVM address that will control the Validator Manager Contract?") -} - func ValidateSubnetNameAndGetChains(args []string) ([]string, error) { // this should not be necessary but some bright guy might just be creating // the genesis by hand or something...