From 63cb85b83f69357d9c72f21a44668c3d05a1e9d7 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 11 Sep 2024 10:09:24 -0400 Subject: [PATCH 01/74] 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/74] 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/74] 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/74] 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/74] 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/74] 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 6fd7726150f971bc40702b4b718e7f7f1271307f Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Wed, 11 Sep 2024 21:22:20 -0300 Subject: [PATCH 07/74] adding teleporter contract to genesis --- cmd/blockchaincmd/create.go | 1 + pkg/vm/create_evm.go | 54 +++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 07ee0835d..30bcbbe40 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -276,6 +276,7 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { deployTeleporter = params.UseTeleporter useExternalGasToken = params.UseExternalGasToken genesisBytes, err = vm.CreateEVMGenesis( + app, blockchainName, params, teleporterInfo, diff --git a/pkg/vm/create_evm.go b/pkg/vm/create_evm.go index 1d8f3ea05..ef7d44d11 100644 --- a/pkg/vm/create_evm.go +++ b/pkg/vm/create_evm.go @@ -4,10 +4,12 @@ package vm import ( "bytes" + "encoding/hex" "encoding/json" "errors" "fmt" "math/big" + "strings" "time" "github.com/ava-labs/avalanche-cli/pkg/application" @@ -16,6 +18,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/teleporter" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" subnetevmparams "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" @@ -72,6 +75,7 @@ func CreateEvmSidecar( } func CreateEVMGenesis( + app *application.Avalanche, blockchainName string, params SubnetEVMGenesisParams, teleporterInfo *teleporter.Info, @@ -111,6 +115,28 @@ func CreateEVMGenesis( params.initialTokenAllocation[common.HexToAddress(teleporterInfo.FundedAddress)] = core.GenesisAccount{ Balance: balance, } + messengerContractAddress, deployedMessengerString, messengerDeployerAddress, err := getTeleporterMessengerParamsForGenesis( + app, + "v1.0.0", + ) + if err != nil { + return nil, err + } + deployedMessengerBytes := common.FromHex(deployedMessengerString) + storage := map[common.Hash]common.Hash{ + common.HexToHash("0x0"): common.HexToHash("0x1"), + common.HexToHash("0x1"): common.HexToHash("0x1"), + } + params.initialTokenAllocation[common.HexToAddress(messengerContractAddress)] = core.GenesisAccount{ + Balance: big.NewInt(0), + Code: deployedMessengerBytes, + Storage: storage, + Nonce: 1, + } + params.initialTokenAllocation[common.HexToAddress(messengerDeployerAddress)] = core.GenesisAccount{ + Balance: big.NewInt(0), + Nonce: 1, + } } if params.UseExternalGasToken { @@ -168,3 +194,31 @@ func someAllowedHasBalance(allowList AllowList, allocations core.GenesisAlloc) b } return false } + +func getTeleporterMessengerParamsForGenesis( + app *application.Avalanche, + teleporterVersion string, +) (string, string, string, error) { + td := teleporter.Deployer{} + messengerContractAddress, messengerDeployerAddress, messengerDeployerTxString, registryBytecode, err := td.GetAssets( + app.GetTeleporterBinDir(), + teleporterVersion, + ) + if err != nil { + return "", "", "", err + } + messengerDeployerTxBytes, err := hex.DecodeString(strings.TrimPrefix(messengerDeployerTxString, "0x")) + if err != nil { + return "", "", "", err + } + messengerDeployerTx := types.NewTx(&types.LegacyTx{}) + if err := messengerDeployerTx.UnmarshalBinary(messengerDeployerTxBytes); err != nil { + return "", "", "", err + } + messengerBytes := messengerDeployerTx.Data() + deployedMessengerBytes := messengerBytes[:19] + deployedMessengerBytes = append(deployedMessengerBytes, messengerBytes[19+41:]...) + deployedMessengerString := "0x" + hex.EncodeToString(deployedMessengerBytes) + _ = registryBytecode + return messengerContractAddress, deployedMessengerString, messengerDeployerAddress, nil +} From 4295956ba75b27e66ca439d08a1f2d04ef7f3dff Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 12 Sep 2024 15:16:57 -0400 Subject: [PATCH 08/74] 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 09/74] 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 10/74] 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 11/74] 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 12/74] 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 13/74] 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 606c4fc58516167ee5dde159914bcf183ed68e40 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Mon, 16 Sep 2024 11:52:12 -0400 Subject: [PATCH 14/74] fixes for icm on genesis --- cmd/blockchaincmd/deploy.go | 3 ++- cmd/teleportercmd/deploy.go | 2 ++ pkg/subnet/local.go | 11 +++++++---- pkg/teleporter/teleporter.go | 30 +++++++++++++++--------------- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index e892db32d..9b89088b1 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -96,7 +96,8 @@ so you can take your locally tested Subnet and deploy it on Fuji or Mainnet.`, cmd.Flags().BoolVar(&icmSpec.SkipICMDeploy, "skip-local-teleporter", false, "skip automatic teleporter deploy on local networks [to be deprecated]") cmd.Flags().BoolVar(&icmSpec.SkipICMDeploy, "skip-teleporter-deploy", false, "skip automatic teleporter deploy") cmd.Flags().BoolVar(&icmSpec.SkipRelayerDeploy, "skip-relayer", false, "skip relayer deploy") - cmd.Flags().StringVar(&icmSpec.Version, "teleporter-version", "latest", "teleporter version to deploy") + cmd.Flags().StringVar(&icmSpec.ICMVersion, "teleporter-version", "latest", "teleporter version to deploy") + cmd.Flags().StringVar(&icmSpec.RelayerVersion, "relayer-version", "latest", "relayer version to deploy") cmd.Flags().StringVar(&icmSpec.MessengerContractAddressPath, "teleporter-messenger-contract-address-path", "", "path to an interchain messenger contract address file") 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") diff --git a/cmd/teleportercmd/deploy.go b/cmd/teleportercmd/deploy.go index fef0f1061..e9b516860 100644 --- a/cmd/teleportercmd/deploy.go +++ b/cmd/teleportercmd/deploy.go @@ -188,6 +188,7 @@ func CallDeploy(_ []string, flags DeployFlags) error { privateKey, flags.DeployMessenger, flags.DeployRegistry, + false, ) if err != nil { return err @@ -224,6 +225,7 @@ func CallDeploy(_ []string, flags DeployFlags) error { ewoq.PrivKeyHex(), flags.DeployMessenger, flags.DeployRegistry, + false, ) if err != nil { return err diff --git a/pkg/subnet/local.go b/pkg/subnet/local.go index cd6efa2af..18b87fab6 100644 --- a/pkg/subnet/local.go +++ b/pkg/subnet/local.go @@ -81,11 +81,12 @@ type setDefaultSnapshotFunc func(string, bool, string, bool) (bool, error) type ICMSpec struct { SkipICMDeploy bool SkipRelayerDeploy bool - Version string + ICMVersion string MessengerContractAddressPath string MessengerDeployerAddressPath string MessengerDeployerTxPath string RegistryBydecodePath string + RelayerVersion string } type DeployInfo struct { @@ -384,8 +385,8 @@ func (d *LocalDeployer) doDeploy(chain string, genesisPath string, icmSpec ICMSp } else { icmVersion := "" switch { - case icmSpec.Version != "" && icmSpec.Version != "latest": - icmVersion = icmSpec.Version + case icmSpec.ICMVersion != "" && icmSpec.ICMVersion != "latest": + icmVersion = icmSpec.ICMVersion case sc.TeleporterVersion != "": icmVersion = sc.TeleporterVersion default: @@ -412,6 +413,7 @@ func (d *LocalDeployer) doDeploy(chain string, genesisPath string, icmSpec ICMSp cChainKey.PrivKeyHex(), true, true, + false, ) if err != nil { return nil, err @@ -448,6 +450,7 @@ func (d *LocalDeployer) doDeploy(chain string, genesisPath string, icmSpec ICMSp blockchainKey.PrivKeyHex(), true, true, + true, ) if err != nil { return nil, err @@ -502,7 +505,7 @@ func (d *LocalDeployer) doDeploy(chain string, genesisPath string, icmSpec ICMSp ux.Logger.PrintToUser("") // start relayer if err := teleporter.DeployRelayer( - "latest", + icmSpec.RelayerVersion, d.app.GetAWMRelayerBinDir(), relayerConfigPath, d.app.GetLocalRelayerLogPath(models.Local), diff --git a/pkg/teleporter/teleporter.go b/pkg/teleporter/teleporter.go index 05f5ab288..c4d73481b 100644 --- a/pkg/teleporter/teleporter.go +++ b/pkg/teleporter/teleporter.go @@ -240,6 +240,7 @@ func (t *Deployer) Deploy( privateKey string, deployMessenger bool, deployRegistry bool, + forceRegistryDeploy bool, ) (bool, string, string, error) { var ( messengerAddress string @@ -255,7 +256,7 @@ func (t *Deployer) Deploy( ) } if err == nil && deployRegistry { - if !deployMessenger || !alreadyDeployed { + if !deployMessenger || !alreadyDeployed || forceRegistryDeploy { registryAddress, err = t.DeployRegistry(subnetName, rpcURL, privateKey) } } @@ -404,24 +405,23 @@ func DeployAndFundRelayer( privKeyStr, true, true, + true, ) if err != nil { return false, "", "", err } - if !alreadyDeployed { - // get relayer address - relayerAddress, _, err := GetRelayerKeyInfo(app.GetKeyPath(constants.AWMRelayerKeyName)) - if err != nil { - return false, "", "", err - } - // fund relayer - if err := FundRelayer( - endpoint, - privKeyStr, - relayerAddress, - ); err != nil { - return false, "", "", err - } + // get relayer address + relayerAddress, _, err := GetRelayerKeyInfo(app.GetKeyPath(constants.AWMRelayerKeyName)) + if err != nil { + return false, "", "", err + } + // fund relayer + if err := FundRelayer( + endpoint, + privKeyStr, + relayerAddress, + ); err != nil { + return false, "", "", err } return alreadyDeployed, messengerAddress, registryAddress, err } From 29966df3b9007951924771f77b4348b46534f470 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Mon, 16 Sep 2024 13:11:25 -0400 Subject: [PATCH 15/74] add icm from a given embedded file --- pkg/vm/create_evm.go | 54 +------------------------- pkg/vm/deployed_messenger_bytecode.txt | 1 + pkg/vm/deployed_registry_bytecode.txt | 1 + pkg/vm/icm.go | 40 +++++++++++++++++++ 4 files changed, 43 insertions(+), 53 deletions(-) create mode 100644 pkg/vm/deployed_messenger_bytecode.txt create mode 100644 pkg/vm/deployed_registry_bytecode.txt create mode 100644 pkg/vm/icm.go diff --git a/pkg/vm/create_evm.go b/pkg/vm/create_evm.go index ef7d44d11..5b62a7760 100644 --- a/pkg/vm/create_evm.go +++ b/pkg/vm/create_evm.go @@ -4,12 +4,10 @@ package vm import ( "bytes" - "encoding/hex" "encoding/json" "errors" "fmt" "math/big" - "strings" "time" "github.com/ava-labs/avalanche-cli/pkg/application" @@ -18,7 +16,6 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/teleporter" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/subnet-evm/core" - "github.com/ava-labs/subnet-evm/core/types" subnetevmparams "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" @@ -115,28 +112,7 @@ func CreateEVMGenesis( params.initialTokenAllocation[common.HexToAddress(teleporterInfo.FundedAddress)] = core.GenesisAccount{ Balance: balance, } - messengerContractAddress, deployedMessengerString, messengerDeployerAddress, err := getTeleporterMessengerParamsForGenesis( - app, - "v1.0.0", - ) - if err != nil { - return nil, err - } - deployedMessengerBytes := common.FromHex(deployedMessengerString) - storage := map[common.Hash]common.Hash{ - common.HexToHash("0x0"): common.HexToHash("0x1"), - common.HexToHash("0x1"): common.HexToHash("0x1"), - } - params.initialTokenAllocation[common.HexToAddress(messengerContractAddress)] = core.GenesisAccount{ - Balance: big.NewInt(0), - Code: deployedMessengerBytes, - Storage: storage, - Nonce: 1, - } - params.initialTokenAllocation[common.HexToAddress(messengerDeployerAddress)] = core.GenesisAccount{ - Balance: big.NewInt(0), - Nonce: 1, - } + addICMContractToGenesisAllocations(params.initialTokenAllocation) } if params.UseExternalGasToken { @@ -194,31 +170,3 @@ func someAllowedHasBalance(allowList AllowList, allocations core.GenesisAlloc) b } return false } - -func getTeleporterMessengerParamsForGenesis( - app *application.Avalanche, - teleporterVersion string, -) (string, string, string, error) { - td := teleporter.Deployer{} - messengerContractAddress, messengerDeployerAddress, messengerDeployerTxString, registryBytecode, err := td.GetAssets( - app.GetTeleporterBinDir(), - teleporterVersion, - ) - if err != nil { - return "", "", "", err - } - messengerDeployerTxBytes, err := hex.DecodeString(strings.TrimPrefix(messengerDeployerTxString, "0x")) - if err != nil { - return "", "", "", err - } - messengerDeployerTx := types.NewTx(&types.LegacyTx{}) - if err := messengerDeployerTx.UnmarshalBinary(messengerDeployerTxBytes); err != nil { - return "", "", "", err - } - messengerBytes := messengerDeployerTx.Data() - deployedMessengerBytes := messengerBytes[:19] - deployedMessengerBytes = append(deployedMessengerBytes, messengerBytes[19+41:]...) - deployedMessengerString := "0x" + hex.EncodeToString(deployedMessengerBytes) - _ = registryBytecode - return messengerContractAddress, deployedMessengerString, messengerDeployerAddress, nil -} diff --git a/pkg/vm/deployed_messenger_bytecode.txt b/pkg/vm/deployed_messenger_bytecode.txt new file mode 100644 index 000000000..aa3ae3c6c --- /dev/null +++ b/pkg/vm/deployed_messenger_bytecode.txt @@ -0,0 +1 @@ +0x608060405234801561001057600080fd5b506004361061014d5760003560e01c8063a8898181116100c3578063df20e8bc1161007c578063df20e8bc1461033b578063e69d606a1461034e578063e6e67bd5146103b6578063ebc3b1ba146103f2578063ecc7042814610415578063fc2d61971461041e57600080fd5b8063a8898181146102b2578063a9a85614146102c5578063b771b3bc146102d8578063c473eef8146102e6578063ccb5f8091461031f578063d127dc9b1461033257600080fd5b8063399b77da11610115578063399b77da1461021957806362448850146102395780638245a1b01461024c578063860a3b061461025f578063892bf4121461027f5780638ac0fd041461029f57600080fd5b80630af5b4ff1461015257806322296c3a1461016d5780632bc8b0bf146101825780632ca40f55146101955780632e27c223146101ee575b600080fd5b61015a610431565b6040519081526020015b60405180910390f35b61018061017b366004612251565b610503565b005b61015a61019036600461226e565b6105f8565b6101e06101a336600461226e565b6005602090815260009182526040918290208054835180850190945260018201546001600160a01b03168452600290910154918301919091529082565b604051610164929190612287565b6102016101fc36600461226e565b610615565b6040516001600160a01b039091168152602001610164565b61015a61022736600461226e565b60009081526005602052604090205490565b61015a6102473660046122ae565b61069e565b61018061025a366004612301565b6106fc565b61015a61026d36600461226e565b60066020526000908152604090205481565b61029261028d366004612335565b6108a7565b6040516101649190612357565b6101806102ad366004612377565b6108da565b61015a6102c03660046123af565b610b19565b61015a6102d3366004612426565b610b5c565b6102016005600160991b0181565b61015a6102f43660046124be565b6001600160a01b03918216600090815260096020908152604080832093909416825291909152205490565b61018061032d3660046124f7565b610e03565b61015a60025481565b61015a61034936600461226e565b61123d565b61039761035c36600461226e565b600090815260056020908152604091829020825180840190935260018101546001600160a01b03168084526002909101549290910182905291565b604080516001600160a01b039093168352602083019190915201610164565b6103dd6103c436600461226e565b6004602052600090815260409020805460019091015482565b60408051928352602083019190915201610164565b61040561040036600461226e565b611286565b6040519015158152602001610164565b61015a60035481565b61018061042c36600461251e565b61129c565b600254600090806104fe576005600160991b016001600160a01b0316634213cf786040518163ffffffff1660e01b8152600401602060405180830381865afa158015610481573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104a59190612564565b9050806104cd5760405162461bcd60e51b81526004016104c49061257d565b60405180910390fd5b600281905560405181907f1eac640109dc937d2a9f42735a05f794b39a5e3759d681951d671aabbce4b10490600090a25b919050565b3360009081526009602090815260408083206001600160a01b0385168452909152902054806105855760405162461bcd60e51b815260206004820152602860248201527f54656c65706f727465724d657373656e6765723a206e6f2072657761726420746044820152676f2072656465656d60c01b60648201526084016104c4565b3360008181526009602090815260408083206001600160a01b03871680855290835281842093909355518481529192917f3294c84e5b0f29d9803655319087207bc94f4db29f7927846944822773780b88910160405180910390a36105f46001600160a01b03831633836114f7565b5050565b600081815260046020526040812061060f9061155f565b92915050565b6000818152600760205260408120546106825760405162461bcd60e51b815260206004820152602960248201527f54656c65706f727465724d657373656e6765723a206d657373616765206e6f74604482015268081c9958d95a5d995960ba1b60648201526084016104c4565b506000908152600860205260409020546001600160a01b031690565b60006001600054146106c25760405162461bcd60e51b81526004016104c4906125c4565b60026000556106f16106d383612804565b833560009081526004602052604090206106ec90611572565b61167c565b600160005592915050565b60016000541461071e5760405162461bcd60e51b81526004016104c4906125c4565b6002600081815590546107379060408401358435610b19565b6000818152600560209081526040918290208251808401845281548152835180850190945260018201546001600160a01b03168452600290910154838301529081019190915280519192509061079f5760405162461bcd60e51b81526004016104c4906128a7565b6000836040516020016107b29190612b42565b60408051601f19818403018152919052825181516020830120919250146107eb5760405162461bcd60e51b81526004016104c490612b55565b8360400135837f2a211ad4a59ab9d003852404f9c57c690704ee755f3c79d2c2812ad32da99df8868560200151604051610826929190612b9e565b60405180910390a360405163ee5b48eb60e01b81526005600160991b019063ee5b48eb90610858908490600401612c23565b6020604051808303816000875af1158015610877573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061089b9190612564565b50506001600055505050565b604080518082019091526000808252602082015260008381526004602052604090206108d390836118bc565b9392505050565b6001600054146108fc5760405162461bcd60e51b81526004016104c4906125c4565b600260005560018054146109225760405162461bcd60e51b81526004016104c490612c36565b60026001558061098c5760405162461bcd60e51b815260206004820152602f60248201527f54656c65706f727465724d657373656e6765723a207a65726f2061646469746960448201526e1bdb985b0819995948185b5bdd5b9d608a1b60648201526084016104c4565b6001600160a01b0382166109b25760405162461bcd60e51b81526004016104c490612c7b565b6000838152600560205260409020546109dd5760405162461bcd60e51b81526004016104c4906128a7565b6000838152600560205260409020600101546001600160a01b03838116911614610a6f5760405162461bcd60e51b815260206004820152603760248201527f54656c65706f727465724d657373656e6765723a20696e76616c69642066656560448201527f20617373657420636f6e7472616374206164647265737300000000000000000060648201526084016104c4565b6000610a7b8383611981565b600085815260056020526040812060020180549293508392909190610aa1908490612ce5565b909155505060008481526005602052604090819020905185917fc1bfd1f1208927dfbd414041dcb5256e6c9ad90dd61aec3249facbd34ff7b3e191610b03916001019081546001600160a01b0316815260019190910154602082015260400190565b60405180910390a2505060018080556000555050565b60408051306020820152908101849052606081018390526080810182905260009060a0016040516020818303038152906040528051906020012090509392505050565b6000600160005414610b805760405162461bcd60e51b81526004016104c4906125c4565b60026000818155905490866001600160401b03811115610ba257610ba2612607565b604051908082528060200260200182016040528015610be757816020015b6040805180820190915260008082526020820152815260200190600190039081610bc05790505b5090508660005b81811015610d6c5760008a8a83818110610c0a57610c0a612cf8565b90506020020135905060006007600083815260200190815260200160002054905080600003610c8a5760405162461bcd60e51b815260206004820152602660248201527f54656c65706f727465724d657373656e6765723a2072656365697074206e6f7460448201526508199bdd5b9960d21b60648201526084016104c4565b610c958d8783610b19565b8214610d095760405162461bcd60e51b815260206004820152603a60248201527f54656c65706f727465724d657373656e6765723a206d6573736167652049442060448201527f6e6f742066726f6d20736f7572636520626c6f636b636861696e00000000000060648201526084016104c4565b6000828152600860209081526040918290205482518084019093528383526001600160a01b03169082018190528651909190879086908110610d4d57610d4d612cf8565b602002602001018190525050505080610d6590612d0e565b9050610bee565b506040805160c0810182528b815260006020820152610df0918101610d96368b90038b018b612d27565b8152602001600081526020018888808060200260200160405190810160405280939291908181526020018383602002808284376000920182905250938552505060408051928352602080840190915290920152508361167c565b60016000559a9950505050505050505050565b6001805414610e245760405162461bcd60e51b81526004016104c490612c36565b60026001556040516306f8253560e41b815263ffffffff8316600482015260009081906005600160991b0190636f82535090602401600060405180830381865afa158015610e76573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e9e9190810190612da3565b9150915080610f015760405162461bcd60e51b815260206004820152602960248201527f54656c65706f727465724d657373656e6765723a20696e76616c69642077617260448201526870206d65737361676560b81b60648201526084016104c4565b60208201516001600160a01b03163014610f785760405162461bcd60e51b815260206004820152603260248201527f54656c65706f727465724d657373656e6765723a20696e76616c6964206f726960448201527167696e2073656e646572206164647265737360701b60648201526084016104c4565b60008260400151806020019051810190610f929190612f40565b90506000610f9e610431565b90508082604001511461100d5760405162461bcd60e51b815260206004820152603160248201527f54656c65706f727465724d657373656e6765723a20696e76616c6964206465736044820152701d1a5b985d1a5bdb8818da185a5b881251607a1b60648201526084016104c4565b8351825160009161101f918490610b19565b600081815260076020526040902054909150156110945760405162461bcd60e51b815260206004820152602d60248201527f54656c65706f727465724d657373656e6765723a206d65737361676520616c7260448201526c1958591e481c9958d95a5d9959609a1b60648201526084016104c4565b6110a2338460a00151611ae9565b6111005760405162461bcd60e51b815260206004820152602960248201527f54656c65706f727465724d657373656e6765723a20756e617574686f72697a6560448201526832103932b630bcb2b960b91b60648201526084016104c4565b61110e818460000151611b61565b6001600160a01b0386161561114557600081815260086020526040902080546001600160a01b0319166001600160a01b0388161790555b60c08301515160005b81811015611192576111828488600001518760c00151848151811061117557611175612cf8565b6020026020010151611bd3565b61118b81612d0e565b905061114e565b50604080518082018252855181526001600160a01b038916602080830191909152885160009081526004909152919091206111cc91611cfb565b336001600160a01b03168660000151837f292ee90bbaf70b5d4936025e09d56ba08f3e421156b6a568cf3c2840d9343e348a8860405161120d929190613150565b60405180910390a460e0840151511561122f5761122f82876000015186611d57565b505060018055505050505050565b600254600090806112605760405162461bcd60e51b81526004016104c49061257d565b600060035460016112719190612ce5565b905061127e828583610b19565b949350505050565b600081815260076020526040812054151561060f565b60018054146112bd5760405162461bcd60e51b81526004016104c490612c36565b60026001819055546000906112d59084908435610b19565b600081815260066020526040902054909150806113045760405162461bcd60e51b81526004016104c4906128a7565b80836040516020016113169190612b42565b60405160208183030381529060405280519060200120146113495760405162461bcd60e51b81526004016104c490612b55565b600061135b6080850160608601612251565b6001600160a01b03163b116113cf5760405162461bcd60e51b815260206004820152603460248201527f54656c65706f727465724d657373656e6765723a2064657374696e6174696f6e604482015273206164647265737320686173206e6f20636f646560601b60648201526084016104c4565b604051849083907f34795cc6b122b9a0ae684946319f1e14a577b4e8f9b3dda9ac94c21a54d3188c90600090a360008281526006602090815260408083208390558691611420918701908701612251565b61142d60e0870187613174565b60405160240161144094939291906131ba565b60408051601f198184030181529190526020810180516001600160e01b031663643477d560e11b179052905060006114886114816080870160608801612251565b5a84611e8a565b9050806114eb5760405162461bcd60e51b815260206004820152602b60248201527f54656c65706f727465724d657373656e6765723a20726574727920657865637560448201526a1d1a5bdb8819985a5b195960aa1b60648201526084016104c4565b50506001805550505050565b6040516001600160a01b03831660248201526044810182905261155a90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611ea4565b505050565b8054600182015460009161060f916131e5565b6060600061158960056115848561155f565b611f76565b9050806000036115d85760408051600080825260208201909252906115d0565b60408051808201909152600080825260208201528152602001906001900390816115a95790505b509392505050565b6000816001600160401b038111156115f2576115f2612607565b60405190808252806020026020018201604052801561163757816020015b60408051808201909152600080825260208201528152602001906001900390816116105790505b50905060005b828110156115d05761164e85611f8c565b82828151811061166057611660612cf8565b60200260200101819052508061167590612d0e565b905061163d565b600080611687610431565b9050600060036000815461169a90612d0e565b919050819055905060006116b383876000015184610b19565b90506000604051806101000160405280848152602001336001600160a01b031681526020018860000151815260200188602001516001600160a01b0316815260200188606001518152602001886080015181526020018781526020018860a00151815250905060008160405160200161172c91906131f8565b60405160208183030381529060405290506000808960400151602001511115611794576040890151516001600160a01b031661177a5760405162461bcd60e51b81526004016104c490612c7b565b604089015180516020909101516117919190611981565b90505b6040805180820182528a820151516001600160a01b039081168252602080830185905283518085018552865187830120815280820184815260008a815260058452869020915182555180516001830180546001600160a01b03191691909516179093559101516002909101558a51915190919086907f2a211ad4a59ab9d003852404f9c57c690704ee755f3c79d2c2812ad32da99df890611838908890869061320b565b60405180910390a360405163ee5b48eb60e01b81526005600160991b019063ee5b48eb9061186a908690600401612c23565b6020604051808303816000875af1158015611889573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ad9190612564565b50939998505050505050505050565b60408051808201909152600080825260208201526118d98361155f565b82106119315760405162461bcd60e51b815260206004820152602160248201527f5265636569707451756575653a20696e646578206f7574206f6620626f756e646044820152607360f81b60648201526084016104c4565b8260020160008385600001546119479190612ce5565b81526020808201929092526040908101600020815180830190925280548252600101546001600160a01b0316918101919091529392505050565b6040516370a0823160e01b815230600482015260009081906001600160a01b038516906370a0823190602401602060405180830381865afa1580156119ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ee9190612564565b9050611a056001600160a01b038516333086612058565b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa158015611a4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a709190612564565b9050818111611ad65760405162461bcd60e51b815260206004820152602c60248201527f5361666545524332305472616e7366657246726f6d3a2062616c616e6365206e60448201526b1bdd081a5b98dc99585cd95960a21b60648201526084016104c4565b611ae082826131e5565b95945050505050565b60008151600003611afc5750600161060f565b815160005b81811015611b5657846001600160a01b0316848281518110611b2557611b25612cf8565b60200260200101516001600160a01b031603611b465760019250505061060f565b611b4f81612d0e565b9050611b01565b506000949350505050565b80600003611bc15760405162461bcd60e51b815260206004820152602760248201527f54656c65706f727465724d657373656e6765723a207a65726f206d657373616760448201526665206e6f6e636560c81b60648201526084016104c4565b60009182526007602052604090912055565b6000611be484848460000151610b19565b6000818152600560209081526040918290208251808401845281548152835180850190945260018201546001600160a01b031684526002909101548383015290810191909152805191925090611c3b575050505050565b60008281526005602090815260408083208381556001810180546001600160a01b03191690556002018390558382018051830151878401516001600160a01b0390811686526009855283862092515116855292528220805491929091611ca2908490612ce5565b9250508190555082602001516001600160a01b031684837fd13a7935f29af029349bed0a2097455b91fd06190a30478c575db3f31e00bf578460200151604051611cec919061321e565b60405180910390a45050505050565b6001820180548291600285019160009182611d1583612d0e565b90915550815260208082019290925260400160002082518155910151600190910180546001600160a01b0319166001600160a01b039092169190911790555050565b80608001515a1015611db95760405162461bcd60e51b815260206004820152602560248201527f54656c65706f727465724d657373656e6765723a20696e73756666696369656e604482015264742067617360d81b60648201526084016104c4565b80606001516001600160a01b03163b600003611dda5761155a838383612096565b602081015160e0820151604051600092611df892869260240161323e565b60408051601f198184030181529190526020810180516001600160e01b031663643477d560e11b17905260608301516080840151919250600091611e3d919084611e8a565b905080611e5657611e4f858585612096565b5050505050565b604051849086907f34795cc6b122b9a0ae684946319f1e14a577b4e8f9b3dda9ac94c21a54d3188c90600090a35050505050565b60008060008084516020860160008989f195945050505050565b6000611ef9826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661210b9092919063ffffffff16565b80519091501561155a5780806020019051810190611f179190613268565b61155a5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016104c4565b6000818310611f8557816108d3565b5090919050565b604080518082019091526000808252602082015281546001830154819003611ff65760405162461bcd60e51b815260206004820152601960248201527f5265636569707451756575653a20656d7074792071756575650000000000000060448201526064016104c4565b60008181526002840160208181526040808420815180830190925280548252600180820180546001600160a01b03811685870152888852959094529490556001600160a01b031990921690559061204e908390612ce5565b9093555090919050565b6040516001600160a01b03808516602483015283166044820152606481018290526120909085906323b872dd60e01b90608401611523565b50505050565b806040516020016120a791906131f8565b60408051601f1981840301815282825280516020918201206000878152600690925291902055829084907f4619adc1017b82e02eaefac01a43d50d6d8de4460774bc370c3ff0210d40c985906120fe9085906131f8565b60405180910390a3505050565b606061127e848460008585600080866001600160a01b031685876040516121329190613283565b60006040518083038185875af1925050503d806000811461216f576040519150601f19603f3d011682016040523d82523d6000602084013e612174565b606091505b509150915061218587838387612190565b979650505050505050565b606083156121ff5782516000036121f8576001600160a01b0385163b6121f85760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016104c4565b508161127e565b61127e83838151156122145781518083602001fd5b8060405162461bcd60e51b81526004016104c49190612c23565b6001600160a01b038116811461224357600080fd5b50565b80356104fe8161222e565b60006020828403121561226357600080fd5b81356108d38161222e565b60006020828403121561228057600080fd5b5035919050565b828152606081016108d3602083018480516001600160a01b03168252602090810151910152565b6000602082840312156122c057600080fd5b81356001600160401b038111156122d657600080fd5b820160e081850312156108d357600080fd5b600061010082840312156122fb57600080fd5b50919050565b60006020828403121561231357600080fd5b81356001600160401b0381111561232957600080fd5b61127e848285016122e8565b6000806040838503121561234857600080fd5b50508035926020909101359150565b815181526020808301516001600160a01b0316908201526040810161060f565b60008060006060848603121561238c57600080fd5b83359250602084013561239e8161222e565b929592945050506040919091013590565b6000806000606084860312156123c457600080fd5b505081359360208301359350604090920135919050565b60008083601f8401126123ed57600080fd5b5081356001600160401b0381111561240457600080fd5b6020830191508360208260051b850101111561241f57600080fd5b9250929050565b60008060008060008086880360a081121561244057600080fd5b8735965060208801356001600160401b038082111561245e57600080fd5b61246a8b838c016123db565b90985096508691506040603f198401121561248457600080fd5b60408a01955060808a013592508083111561249e57600080fd5b50506124ac89828a016123db565b979a9699509497509295939492505050565b600080604083850312156124d157600080fd5b82356124dc8161222e565b915060208301356124ec8161222e565b809150509250929050565b6000806040838503121561250a57600080fd5b823563ffffffff811681146124dc57600080fd5b6000806040838503121561253157600080fd5b8235915060208301356001600160401b0381111561254e57600080fd5b61255a858286016122e8565b9150509250929050565b60006020828403121561257657600080fd5b5051919050565b60208082526027908201527f54656c65706f727465724d657373656e6765723a207a65726f20626c6f636b636040820152661a185a5b88125160ca1b606082015260800190565b60208082526023908201527f5265656e7472616e63794775617264733a2073656e646572207265656e7472616040820152626e637960e81b606082015260800190565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b038111828210171561263f5761263f612607565b60405290565b60405160c081016001600160401b038111828210171561263f5761263f612607565b60405161010081016001600160401b038111828210171561263f5761263f612607565b604051601f8201601f191681016001600160401b03811182821017156126b2576126b2612607565b604052919050565b6000604082840312156126cc57600080fd5b6126d461261d565b905081356126e18161222e565b808252506020820135602082015292915050565b60006001600160401b0382111561270e5761270e612607565b5060051b60200190565b600082601f83011261272957600080fd5b8135602061273e612739836126f5565b61268a565b82815260059290921b8401810191818101908684111561275d57600080fd5b8286015b848110156127815780356127748161222e565b8352918301918301612761565b509695505050505050565b60006001600160401b038211156127a5576127a5612607565b50601f01601f191660200190565b600082601f8301126127c457600080fd5b81356127d26127398261278c565b8181528460208386010111156127e757600080fd5b816020850160208301376000918101602001919091529392505050565b600060e0823603121561281657600080fd5b61281e612645565b8235815261282e60208401612246565b602082015261284036604085016126ba565b60408201526080830135606082015260a08301356001600160401b038082111561286957600080fd5b61287536838701612718565b608084015260c085013591508082111561288e57600080fd5b5061289b368286016127b3565b60a08301525092915050565b60208082526026908201527f54656c65706f727465724d657373656e6765723a206d657373616765206e6f7460408201526508199bdd5b9960d21b606082015260800190565b6000808335601e1984360301811261290457600080fd5b83016020810192503590506001600160401b0381111561292357600080fd5b8060051b360382131561241f57600080fd5b8183526000602080850194508260005b858110156129735781356129588161222e565b6001600160a01b031687529582019590820190600101612945565b509495945050505050565b6000808335601e1984360301811261299557600080fd5b83016020810192503590506001600160401b038111156129b457600080fd5b8060061b360382131561241f57600080fd5b8183526000602080850194508260005b858110156129735781358752828201356129ef8161222e565b6001600160a01b03168784015260409687019691909101906001016129d6565b6000808335601e19843603018112612a2657600080fd5b83016020810192503590506001600160401b03811115612a4557600080fd5b80360382131561241f57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000610100823584526020830135612a948161222e565b6001600160a01b0316602085015260408381013590850152612ab860608401612246565b6001600160a01b0316606085015260808381013590850152612add60a08401846128ed565b8260a0870152612af08387018284612935565b92505050612b0160c084018461297e565b85830360c0870152612b148382846129c6565b92505050612b2560e0840184612a0f565b85830360e0870152612b38838284612a54565b9695505050505050565b6020815260006108d36020830184612a7d565b60208082526029908201527f54656c65706f727465724d657373656e6765723a20696e76616c6964206d65736040820152680e6c2ceca40d0c2e6d60bb1b606082015260800190565b606081526000612bb16060830185612a7d565b90506108d3602083018480516001600160a01b03168252602090810151910152565b60005b83811015612bee578181015183820152602001612bd6565b50506000910152565b60008151808452612c0f816020860160208601612bd3565b601f01601f19169290920160200192915050565b6020815260006108d36020830184612bf7565b60208082526025908201527f5265656e7472616e63794775617264733a207265636569766572207265656e7460408201526472616e637960d81b606082015260800190565b60208082526034908201527f54656c65706f727465724d657373656e6765723a207a65726f2066656520617360408201527373657420636f6e7472616374206164647265737360601b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8082018082111561060f5761060f612ccf565b634e487b7160e01b600052603260045260246000fd5b600060018201612d2057612d20612ccf565b5060010190565b600060408284031215612d3957600080fd5b6108d383836126ba565b80516104fe8161222e565b600082601f830112612d5f57600080fd5b8151612d6d6127398261278c565b818152846020838601011115612d8257600080fd5b61127e826020830160208701612bd3565b805180151581146104fe57600080fd5b60008060408385031215612db657600080fd5b82516001600160401b0380821115612dcd57600080fd5b9084019060608287031215612de157600080fd5b604051606081018181108382111715612dfc57612dfc612607565b604052825181526020830151612e118161222e565b6020820152604083015182811115612e2857600080fd5b612e3488828601612d4e565b6040830152509350612e4b91505060208401612d93565b90509250929050565b600082601f830112612e6557600080fd5b81516020612e75612739836126f5565b82815260059290921b84018101918181019086841115612e9457600080fd5b8286015b84811015612781578051612eab8161222e565b8352918301918301612e98565b600082601f830112612ec957600080fd5b81516020612ed9612739836126f5565b82815260069290921b84018101918181019086841115612ef857600080fd5b8286015b848110156127815760408189031215612f155760008081fd5b612f1d61261d565b8151815284820151612f2e8161222e565b81860152835291830191604001612efc565b600060208284031215612f5257600080fd5b81516001600160401b0380821115612f6957600080fd5b908301906101008286031215612f7e57600080fd5b612f86612667565b82518152612f9660208401612d43565b602082015260408301516040820152612fb160608401612d43565b60608201526080830151608082015260a083015182811115612fd257600080fd5b612fde87828601612e54565b60a08301525060c083015182811115612ff657600080fd5b61300287828601612eb8565b60c08301525060e08301518281111561301a57600080fd5b61302687828601612d4e565b60e08301525095945050505050565b600081518084526020808501945080840160005b838110156129735781516001600160a01b031687529582019590820190600101613049565b600081518084526020808501945080840160005b83811015612973576130a8878351805182526020908101516001600160a01b0316910152565b6040969096019590820190600101613082565b60006101008251845260018060a01b0360208401511660208501526040830151604085015260608301516130fa60608601826001600160a01b03169052565b506080830151608085015260a08301518160a086015261311c82860182613035565b91505060c083015184820360c0860152613136828261306e565b91505060e083015184820360e0860152611ae08282612bf7565b6001600160a01b038316815260406020820181905260009061127e908301846130bb565b6000808335601e1984360301811261318b57600080fd5b8301803591506001600160401b038211156131a557600080fd5b60200191503681900382131561241f57600080fd5b8481526001600160a01b0384166020820152606060408201819052600090612b389083018486612a54565b8181038181111561060f5761060f612ccf565b6020815260006108d360208301846130bb565b606081526000612bb160608301856130bb565b81516001600160a01b03168152602080830151908201526040810161060f565b8381526001600160a01b0383166020820152606060408201819052600090611ae090830184612bf7565b60006020828403121561327a57600080fd5b6108d382612d93565b60008251613295818460208701612bd3565b919091019291505056fea2646970667358221220586881dd1413fe17197100ceb55646481dae802ef65d37df603c3915f51a4b6364736f6c63430008120033 diff --git a/pkg/vm/deployed_registry_bytecode.txt b/pkg/vm/deployed_registry_bytecode.txt new file mode 100644 index 000000000..7b1a0502b --- /dev/null +++ b/pkg/vm/deployed_registry_bytecode.txt @@ -0,0 +1 @@ +0x608060405234801561001057600080fd5b506004361061009e5760003560e01c8063ac473ac311610066578063ac473ac314610124578063b771b3bc1461012d578063c07f47d41461013b578063d127dc9b14610144578063d820e64f1461016b57600080fd5b80630731775d146100a3578063215abce9146100c857806341f34ed9146100db57806346f9ef49146100f05780634c1f08ce14610103575b600080fd5b6100ab600081565b6040516001600160a01b0390911681526020015b60405180910390f35b6100ab6100d63660046107c5565b610173565b6100ee6100e93660046107de565b610184565b005b6100ab6100fe3660046107c5565b6103f9565b610116610111366004610823565b6104be565b6040519081526020016100bf565b6101166101f481565b6100ab6005600160991b0181565b61011660005481565b6101167f000000000000000000000000000000000000000000000000000000000000000081565b6100ab610566565b600061017e826103f9565b92915050565b6040516306f8253560e41b815263ffffffff8216600482015260009081906005600160991b0190636f82535090602401600060405180830381865afa1580156101d1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526101f991908101906108c5565b91509150806102605760405162461bcd60e51b815260206004820152602860248201527f54656c65706f7274657252656769737472793a20696e76616c69642077617270604482015267206d65737361676560c01b60648201526084015b60405180910390fd5b81517f0000000000000000000000000000000000000000000000000000000000000000146102e45760405162461bcd60e51b815260206004820152602b60248201527f54656c65706f7274657252656769737472793a20696e76616c696420736f757260448201526a18d94818da185a5b88125160aa1b6064820152608401610257565b60208201516001600160a01b0316156103595760405162461bcd60e51b815260206004820152603160248201527f54656c65706f7274657252656769737472793a20696e76616c6964206f726967604482015270696e2073656e646572206164647265737360781b6064820152608401610257565b600080836040015180602001905181019061037491906109cd565b90925090506001600160a01b03811630146103e95760405162461bcd60e51b815260206004820152602f60248201527f54656c65706f7274657252656769737472793a20696e76616c6964206465737460448201526e696e6174696f6e206164647265737360881b6064820152608401610257565b6103f282610578565b5050505050565b60008160000361044b5760405162461bcd60e51b815260206004820181905260248201527f54656c65706f7274657252656769737472793a207a65726f2076657273696f6e6044820152606401610257565b6000828152600160205260409020546001600160a01b03168061017e5760405162461bcd60e51b815260206004820152602560248201527f54656c65706f7274657252656769737472793a2076657273696f6e206e6f7420604482015264199bdd5b9960da1b6064820152608401610257565b60006001600160a01b0382166104e65760405162461bcd60e51b815260040161025790610a49565b6001600160a01b0382166000908152600260205260408120549081900361017e5760405162461bcd60e51b815260206004820152602e60248201527f54656c65706f7274657252656769737472793a2070726f746f636f6c2061646460448201526d1c995cdcc81b9bdd08199bdd5b9960921b6064820152608401610257565b60006105736000546103f9565b905090565b80516000036105c95760405162461bcd60e51b815260206004820181905260248201527f54656c65706f7274657252656769737472793a207a65726f2076657273696f6e6044820152606401610257565b80516000908152600160205260409020546001600160a01b0316156106435760405162461bcd60e51b815260206004820152602a60248201527f54656c65706f7274657252656769737472793a2076657273696f6e20616c72656044820152696164792065786973747360b01b6064820152608401610257565b60208101516001600160a01b031661066d5760405162461bcd60e51b815260040161025790610a49565b60005461067c6101f482610a92565b825111156106e35760405162461bcd60e51b815260206004820152602e60248201527f54656c65706f7274657252656769737472793a2076657273696f6e20696e637260448201526d0cadacadce840e8dede40d0d2ced60931b6064820152608401610257565b602082810180518451600090815260018452604080822080546001600160a01b0319166001600160a01b039485161790559251909116815260029092529020548251111561074c5781516020808401516001600160a01b03166000908152600290915260409020555b602082015182516040516001600160a01b03909216917fa5eed93d951a9603d5f7c0a57de79a299dd3dbd5e51429be209d8053a42ab43a90600090a381518110156107c1578151600081815560405183917f30623e953733f6474dabdfbef1103ce15ab73cdc77c6dfad0f9874d167e8a9b091a35b5050565b6000602082840312156107d757600080fd5b5035919050565b6000602082840312156107f057600080fd5b813563ffffffff8116811461080457600080fd5b9392505050565b6001600160a01b038116811461082057600080fd5b50565b60006020828403121561083557600080fd5b81356108048161080b565b634e487b7160e01b600052604160045260246000fd5b6040516060810167ffffffffffffffff8111828210171561087957610879610840565b60405290565b604051601f8201601f1916810167ffffffffffffffff811182821017156108a8576108a8610840565b604052919050565b805180151581146108c057600080fd5b919050565b600080604083850312156108d857600080fd5b825167ffffffffffffffff808211156108f057600080fd5b908401906060828703121561090457600080fd5b61090c610856565b8251815260208084015161091f8161080b565b8282015260408401518381111561093557600080fd5b80850194505087601f85011261094a57600080fd5b83518381111561095c5761095c610840565b61096e601f8201601f1916830161087f565b9350808452888282870101111561098457600080fd5b60005b818110156109a2578581018301518582018401528201610987565b506000828286010152508260408301528195506109c08188016108b0565b9450505050509250929050565b60008082840360608112156109e157600080fd5b60408112156109ef57600080fd5b506040516040810181811067ffffffffffffffff82111715610a1357610a13610840565b604052835181526020840151610a288161080b565b60208201526040840151909250610a3e8161080b565b809150509250929050565b60208082526029908201527f54656c65706f7274657252656769737472793a207a65726f2070726f746f636f6040820152686c206164647265737360b81b606082015260800190565b8082018082111561017e57634e487b7160e01b600052601160045260246000fdfea2646970667358221220147aa4bf673206f63959dca6bf01bb7ab5e23e6ff9c146a03a27caed9a8296ef64736f6c63430008120033 diff --git a/pkg/vm/icm.go b/pkg/vm/icm.go new file mode 100644 index 000000000..d284ce57a --- /dev/null +++ b/pkg/vm/icm.go @@ -0,0 +1,40 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package vm + +import ( + _ "embed" + "math/big" + "strings" + + "github.com/ava-labs/subnet-evm/core" + "github.com/ethereum/go-ethereum/common" +) + +const ( + messengerContractAddress = "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf" + messengerDeployerAddress = "0x618FEdD9A45a8C456812ecAAE70C671c6249DfaC" +) + +//go:embed deployed_messenger_bytecode.txt +var deployedMessengerBytecode []byte + +func addICMContractToGenesisAllocations( + allocs core.GenesisAlloc, +) { + storage := map[common.Hash]common.Hash{ + common.HexToHash("0x0"): common.HexToHash("0x1"), + common.HexToHash("0x1"): common.HexToHash("0x1"), + } + deployedMessengerBytes := common.FromHex(strings.TrimSpace(string(deployedMessengerBytecode))) + allocs[common.HexToAddress(messengerContractAddress)] = core.GenesisAccount{ + Balance: big.NewInt(0), + Code: deployedMessengerBytes, + Storage: storage, + Nonce: 1, + } + allocs[common.HexToAddress(messengerDeployerAddress)] = core.GenesisAccount{ + Balance: big.NewInt(0), + Nonce: 1, + } +} From dfa8bfa0f778f49ba05364fae7193098d02767ab Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Mon, 16 Sep 2024 14:14:39 -0400 Subject: [PATCH 16/74] add function to deploy ICM registry --- pkg/vm/icm.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pkg/vm/icm.go b/pkg/vm/icm.go index d284ce57a..ba0b0ab16 100644 --- a/pkg/vm/icm.go +++ b/pkg/vm/icm.go @@ -14,11 +14,15 @@ import ( const ( messengerContractAddress = "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf" messengerDeployerAddress = "0x618FEdD9A45a8C456812ecAAE70C671c6249DfaC" + registryContractAddress = "0xF86Cb19Ad8405AEFa7d09C778215D2Cb6eBfB228" ) //go:embed deployed_messenger_bytecode.txt var deployedMessengerBytecode []byte +//go:embed deployed_registry_bytecode.txt +var deployedRegistryBytecode []byte + func addICMContractToGenesisAllocations( allocs core.GenesisAlloc, ) { @@ -38,3 +42,19 @@ func addICMContractToGenesisAllocations( Nonce: 1, } } + +func addICMRegistryContractToGenesisAllocations( + allocs core.GenesisAlloc, +) { + storage := map[common.Hash]common.Hash{ + common.HexToHash("0x0"): common.HexToHash("0x1"), + common.HexToHash("0x1"): common.HexToHash("0x1"), + } + deployedRegistryBytes := common.FromHex(strings.TrimSpace(string(deployedRegistryBytecode))) + allocs[common.HexToAddress(registryContractAddress)] = core.GenesisAccount{ + Balance: big.NewInt(0), + Code: deployedRegistryBytes, + Storage: storage, + Nonce: 1, + } +} From 26ef2fa869dd52fee001f8e8308fd9de15a02d15 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Mon, 16 Sep 2024 15:58:23 -0400 Subject: [PATCH 17/74] creating mapping values --- pkg/vm/create_evm.go | 3 +++ pkg/vm/icm.go | 56 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/pkg/vm/create_evm.go b/pkg/vm/create_evm.go index 5b62a7760..dc2cca3c7 100644 --- a/pkg/vm/create_evm.go +++ b/pkg/vm/create_evm.go @@ -113,6 +113,9 @@ func CreateEVMGenesis( Balance: balance, } addICMContractToGenesisAllocations(params.initialTokenAllocation) + if err := addICMRegistryContractToGenesisAllocations(params.initialTokenAllocation); err != nil { + return nil, err + } } if params.UseExternalGasToken { diff --git a/pkg/vm/icm.go b/pkg/vm/icm.go index ba0b0ab16..e5f03a555 100644 --- a/pkg/vm/icm.go +++ b/pkg/vm/icm.go @@ -4,11 +4,14 @@ package vm import ( _ "embed" + "encoding/hex" + "fmt" "math/big" "strings" "github.com/ava-labs/subnet-evm/core" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" ) const ( @@ -23,13 +26,45 @@ var deployedMessengerBytecode []byte //go:embed deployed_registry_bytecode.txt var deployedRegistryBytecode []byte +func setSimpleStorageValue( + storage map[common.Hash]common.Hash, + slot string, + value string, +) { + storage[common.HexToHash(slot)] = common.HexToHash(value) +} + +func trimHexa(s string) string { + return strings.TrimPrefix(strings.TrimPrefix(s, "0x"), "0X") +} + +func hexFill32(s string) string { + return fmt.Sprintf("%064s", trimHexa(s)) +} + +func setMappingStorageValue( + storage map[common.Hash]common.Hash, + slot string, + key string, + value string, +) error { + slot = hexFill32(slot) + key = hexFill32(key) + storageKey := key + slot + storageKeyBytes, err := hex.DecodeString(storageKey) + if err != nil { + return err + } + storage[crypto.Keccak256Hash(storageKeyBytes)] = common.HexToHash(value) + return nil +} + func addICMContractToGenesisAllocations( allocs core.GenesisAlloc, ) { - storage := map[common.Hash]common.Hash{ - common.HexToHash("0x0"): common.HexToHash("0x1"), - common.HexToHash("0x1"): common.HexToHash("0x1"), - } + storage := map[common.Hash]common.Hash{} + setSimpleStorageValue(storage, "0", "1") + setSimpleStorageValue(storage, "1", "1") deployedMessengerBytes := common.FromHex(strings.TrimSpace(string(deployedMessengerBytecode))) allocs[common.HexToAddress(messengerContractAddress)] = core.GenesisAccount{ Balance: big.NewInt(0), @@ -45,10 +80,14 @@ func addICMContractToGenesisAllocations( func addICMRegistryContractToGenesisAllocations( allocs core.GenesisAlloc, -) { - storage := map[common.Hash]common.Hash{ - common.HexToHash("0x0"): common.HexToHash("0x1"), - common.HexToHash("0x1"): common.HexToHash("0x1"), +) error { + storage := map[common.Hash]common.Hash{} + setSimpleStorageValue(storage, "0", "1") + if err := setMappingStorageValue(storage, "1", "1", messengerContractAddress); err != nil { + return err + } + if err := setMappingStorageValue(storage, "2", messengerContractAddress, "2"); err != nil { + return err } deployedRegistryBytes := common.FromHex(strings.TrimSpace(string(deployedRegistryBytecode))) allocs[common.HexToAddress(registryContractAddress)] = core.GenesisAccount{ @@ -57,4 +96,5 @@ func addICMRegistryContractToGenesisAllocations( Storage: storage, Nonce: 1, } + return nil } From 78c58a6d759af2882885ab563c5621aa95233341 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Mon, 16 Sep 2024 16:34:15 -0400 Subject: [PATCH 18/74] add consts --- pkg/vm/icm.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/pkg/vm/icm.go b/pkg/vm/icm.go index e5f03a555..b425c19ec 100644 --- a/pkg/vm/icm.go +++ b/pkg/vm/icm.go @@ -15,6 +15,7 @@ import ( ) const ( + messengerVersion = "0x1" messengerContractAddress = "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf" messengerDeployerAddress = "0x618FEdD9A45a8C456812ecAAE70C671c6249DfaC" registryContractAddress = "0xF86Cb19Ad8405AEFa7d09C778215D2Cb6eBfB228" @@ -62,9 +63,13 @@ func setMappingStorageValue( func addICMContractToGenesisAllocations( allocs core.GenesisAlloc, ) { + const ( + blockchainIDSlot = "0x0" + messageNonceSlot = "0x1" + ) storage := map[common.Hash]common.Hash{} - setSimpleStorageValue(storage, "0", "1") - setSimpleStorageValue(storage, "1", "1") + setSimpleStorageValue(storage, blockchainIDSlot, "0x1") + setSimpleStorageValue(storage, messageNonceSlot, "0x1") deployedMessengerBytes := common.FromHex(strings.TrimSpace(string(deployedMessengerBytecode))) allocs[common.HexToAddress(messengerContractAddress)] = core.GenesisAccount{ Balance: big.NewInt(0), @@ -81,12 +86,17 @@ func addICMContractToGenesisAllocations( func addICMRegistryContractToGenesisAllocations( allocs core.GenesisAlloc, ) error { + const ( + latestVersionSlot = "0x0" + versionToAddressSlot = "0x1" + addressToVersionSlot = "0x2" + ) storage := map[common.Hash]common.Hash{} - setSimpleStorageValue(storage, "0", "1") - if err := setMappingStorageValue(storage, "1", "1", messengerContractAddress); err != nil { + setSimpleStorageValue(storage, latestVersionSlot, messengerVersion) + if err := setMappingStorageValue(storage, versionToAddressSlot, messengerVersion, messengerContractAddress); err != nil { return err } - if err := setMappingStorageValue(storage, "2", messengerContractAddress, "2"); err != nil { + if err := setMappingStorageValue(storage, addressToVersionSlot, messengerContractAddress, messengerVersion); err != nil { return err } deployedRegistryBytes := common.FromHex(strings.TrimSpace(string(deployedRegistryBytecode))) From d39a8e87d4a2802f335b0ba56e96f9fdcf0094ad Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Mon, 16 Sep 2024 17:23:54 -0400 Subject: [PATCH 19/74] make local deploy to work --- pkg/subnet/local.go | 10 ++++++++-- pkg/vm/create_evm.go | 2 +- pkg/vm/icm.go | 14 +++++++------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/pkg/subnet/local.go b/pkg/subnet/local.go index 18b87fab6..b28d539fd 100644 --- a/pkg/subnet/local.go +++ b/pkg/subnet/local.go @@ -26,6 +26,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/teleporter" "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/ux" + "github.com/ava-labs/avalanche-cli/pkg/vm" "github.com/ava-labs/avalanche-network-runner/client" "github.com/ava-labs/avalanche-network-runner/rpcpb" "github.com/ava-labs/avalanche-network-runner/server" @@ -444,17 +445,22 @@ func (d *LocalDeployer) doDeploy(chain string, genesisPath string, icmSpec ICMSp if err != nil { return nil, err } - _, icmMessengerAddress, icmRegistryAddress, err = icmd.Deploy( + var alreadyDeployed bool + alreadyDeployed, icmMessengerAddress, icmRegistryAddress, err = icmd.Deploy( chain, network.BlockchainEndpoint(blockchainID), blockchainKey.PrivKeyHex(), true, true, - true, + false, ) if err != nil { return nil, err } + if alreadyDeployed { + // included in genesis + icmRegistryAddress = vm.RegistryContractAddress + } if sc.RunRelayer && !icmSpec.SkipRelayerDeploy { if !cchainAlreadyDeployed { if err := teleporter.FundRelayer( diff --git a/pkg/vm/create_evm.go b/pkg/vm/create_evm.go index dc2cca3c7..3a559024c 100644 --- a/pkg/vm/create_evm.go +++ b/pkg/vm/create_evm.go @@ -112,7 +112,7 @@ func CreateEVMGenesis( params.initialTokenAllocation[common.HexToAddress(teleporterInfo.FundedAddress)] = core.GenesisAccount{ Balance: balance, } - addICMContractToGenesisAllocations(params.initialTokenAllocation) + addICMMessengerContractToGenesisAllocations(params.initialTokenAllocation) if err := addICMRegistryContractToGenesisAllocations(params.initialTokenAllocation); err != nil { return nil, err } diff --git a/pkg/vm/icm.go b/pkg/vm/icm.go index b425c19ec..7d89a01e9 100644 --- a/pkg/vm/icm.go +++ b/pkg/vm/icm.go @@ -16,9 +16,9 @@ import ( const ( messengerVersion = "0x1" - messengerContractAddress = "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf" messengerDeployerAddress = "0x618FEdD9A45a8C456812ecAAE70C671c6249DfaC" - registryContractAddress = "0xF86Cb19Ad8405AEFa7d09C778215D2Cb6eBfB228" + MessengerContractAddress = "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf" + RegistryContractAddress = "0xF86Cb19Ad8405AEFa7d09C778215D2Cb6eBfB228" ) //go:embed deployed_messenger_bytecode.txt @@ -60,7 +60,7 @@ func setMappingStorageValue( return nil } -func addICMContractToGenesisAllocations( +func addICMMessengerContractToGenesisAllocations( allocs core.GenesisAlloc, ) { const ( @@ -71,7 +71,7 @@ func addICMContractToGenesisAllocations( setSimpleStorageValue(storage, blockchainIDSlot, "0x1") setSimpleStorageValue(storage, messageNonceSlot, "0x1") deployedMessengerBytes := common.FromHex(strings.TrimSpace(string(deployedMessengerBytecode))) - allocs[common.HexToAddress(messengerContractAddress)] = core.GenesisAccount{ + allocs[common.HexToAddress(MessengerContractAddress)] = core.GenesisAccount{ Balance: big.NewInt(0), Code: deployedMessengerBytes, Storage: storage, @@ -93,14 +93,14 @@ func addICMRegistryContractToGenesisAllocations( ) storage := map[common.Hash]common.Hash{} setSimpleStorageValue(storage, latestVersionSlot, messengerVersion) - if err := setMappingStorageValue(storage, versionToAddressSlot, messengerVersion, messengerContractAddress); err != nil { + if err := setMappingStorageValue(storage, versionToAddressSlot, messengerVersion, MessengerContractAddress); err != nil { return err } - if err := setMappingStorageValue(storage, addressToVersionSlot, messengerContractAddress, messengerVersion); err != nil { + if err := setMappingStorageValue(storage, addressToVersionSlot, MessengerContractAddress, messengerVersion); err != nil { return err } deployedRegistryBytes := common.FromHex(strings.TrimSpace(string(deployedRegistryBytecode))) - allocs[common.HexToAddress(registryContractAddress)] = core.GenesisAccount{ + allocs[common.HexToAddress(RegistryContractAddress)] = core.GenesisAccount{ Balance: big.NewInt(0), Code: deployedRegistryBytes, Storage: storage, From 857f8157ba542c4b6a83c2a8711abd655687d9cc Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Mon, 16 Sep 2024 17:39:41 -0400 Subject: [PATCH 20/74] do not print info for icm support addrs --- cmd/blockchaincmd/describe.go | 7 +++++++ pkg/subnet/local.go | 4 ++-- .../genesis}/deployed_messenger_bytecode.txt | 0 .../genesis}/deployed_registry_bytecode.txt | 0 .../icm.go => teleporter/genesis/genesis.go} | 18 +++++++++--------- pkg/vm/create_evm.go | 5 +++-- 6 files changed, 21 insertions(+), 13 deletions(-) rename pkg/{vm => teleporter/genesis}/deployed_messenger_bytecode.txt (100%) rename pkg/{vm => teleporter/genesis}/deployed_registry_bytecode.txt (100%) rename pkg/{vm/icm.go => teleporter/genesis/genesis.go} (83%) diff --git a/cmd/blockchaincmd/describe.go b/cmd/blockchaincmd/describe.go index 83ac2a5b8..23b0943cf 100644 --- a/cmd/blockchaincmd/describe.go +++ b/cmd/blockchaincmd/describe.go @@ -17,6 +17,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/models" "github.com/ava-labs/avalanche-cli/pkg/networkoptions" "github.com/ava-labs/avalanche-cli/pkg/subnet" + icmgenesis "github.com/ava-labs/avalanche-cli/pkg/teleporter/genesis" "github.com/ava-labs/avalanche-cli/pkg/txutils" "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/ux" @@ -276,6 +277,12 @@ func printAllocations(sc models.Sidecar, genesis core.Genesis) error { t.SetTitle("Initial Token Allocation") t.AppendHeader(table.Row{"Description", "Address and Private Key", "Amount (10^18)", "Amount (wei)"}) for address := range genesis.Alloc { + if len(genesis.Alloc[address].Code) > 0 { + continue + } + if address == common.HexToAddress(icmgenesis.MessengerDeployerAddress) { + continue + } amount := genesis.Alloc[address].Balance formattedAmount := new(big.Int).Div(amount, big.NewInt(params.Ether)) description := "" diff --git a/pkg/subnet/local.go b/pkg/subnet/local.go index b28d539fd..d3b0f6e39 100644 --- a/pkg/subnet/local.go +++ b/pkg/subnet/local.go @@ -24,9 +24,9 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/localnet" "github.com/ava-labs/avalanche-cli/pkg/models" "github.com/ava-labs/avalanche-cli/pkg/teleporter" + icmgenesis "github.com/ava-labs/avalanche-cli/pkg/teleporter/genesis" "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/ux" - "github.com/ava-labs/avalanche-cli/pkg/vm" "github.com/ava-labs/avalanche-network-runner/client" "github.com/ava-labs/avalanche-network-runner/rpcpb" "github.com/ava-labs/avalanche-network-runner/server" @@ -459,7 +459,7 @@ func (d *LocalDeployer) doDeploy(chain string, genesisPath string, icmSpec ICMSp } if alreadyDeployed { // included in genesis - icmRegistryAddress = vm.RegistryContractAddress + icmRegistryAddress = icmgenesis.RegistryContractAddress } if sc.RunRelayer && !icmSpec.SkipRelayerDeploy { if !cchainAlreadyDeployed { diff --git a/pkg/vm/deployed_messenger_bytecode.txt b/pkg/teleporter/genesis/deployed_messenger_bytecode.txt similarity index 100% rename from pkg/vm/deployed_messenger_bytecode.txt rename to pkg/teleporter/genesis/deployed_messenger_bytecode.txt diff --git a/pkg/vm/deployed_registry_bytecode.txt b/pkg/teleporter/genesis/deployed_registry_bytecode.txt similarity index 100% rename from pkg/vm/deployed_registry_bytecode.txt rename to pkg/teleporter/genesis/deployed_registry_bytecode.txt diff --git a/pkg/vm/icm.go b/pkg/teleporter/genesis/genesis.go similarity index 83% rename from pkg/vm/icm.go rename to pkg/teleporter/genesis/genesis.go index 7d89a01e9..67e005549 100644 --- a/pkg/vm/icm.go +++ b/pkg/teleporter/genesis/genesis.go @@ -1,6 +1,6 @@ // Copyright (C) 2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package vm +package genesis import ( _ "embed" @@ -16,9 +16,9 @@ import ( const ( messengerVersion = "0x1" - messengerDeployerAddress = "0x618FEdD9A45a8C456812ecAAE70C671c6249DfaC" - MessengerContractAddress = "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf" + messengerContractAddress = "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf" RegistryContractAddress = "0xF86Cb19Ad8405AEFa7d09C778215D2Cb6eBfB228" + MessengerDeployerAddress = "0x618FEdD9A45a8C456812ecAAE70C671c6249DfaC" ) //go:embed deployed_messenger_bytecode.txt @@ -60,7 +60,7 @@ func setMappingStorageValue( return nil } -func addICMMessengerContractToGenesisAllocations( +func AddICMMessengerContractToAllocations( allocs core.GenesisAlloc, ) { const ( @@ -71,19 +71,19 @@ func addICMMessengerContractToGenesisAllocations( setSimpleStorageValue(storage, blockchainIDSlot, "0x1") setSimpleStorageValue(storage, messageNonceSlot, "0x1") deployedMessengerBytes := common.FromHex(strings.TrimSpace(string(deployedMessengerBytecode))) - allocs[common.HexToAddress(MessengerContractAddress)] = core.GenesisAccount{ + allocs[common.HexToAddress(messengerContractAddress)] = core.GenesisAccount{ Balance: big.NewInt(0), Code: deployedMessengerBytes, Storage: storage, Nonce: 1, } - allocs[common.HexToAddress(messengerDeployerAddress)] = core.GenesisAccount{ + allocs[common.HexToAddress(MessengerDeployerAddress)] = core.GenesisAccount{ Balance: big.NewInt(0), Nonce: 1, } } -func addICMRegistryContractToGenesisAllocations( +func AddICMRegistryContractToAllocations( allocs core.GenesisAlloc, ) error { const ( @@ -93,10 +93,10 @@ func addICMRegistryContractToGenesisAllocations( ) storage := map[common.Hash]common.Hash{} setSimpleStorageValue(storage, latestVersionSlot, messengerVersion) - if err := setMappingStorageValue(storage, versionToAddressSlot, messengerVersion, MessengerContractAddress); err != nil { + if err := setMappingStorageValue(storage, versionToAddressSlot, messengerVersion, messengerContractAddress); err != nil { return err } - if err := setMappingStorageValue(storage, addressToVersionSlot, MessengerContractAddress, messengerVersion); err != nil { + if err := setMappingStorageValue(storage, addressToVersionSlot, messengerContractAddress, messengerVersion); err != nil { return err } deployedRegistryBytes := common.FromHex(strings.TrimSpace(string(deployedRegistryBytecode))) diff --git a/pkg/vm/create_evm.go b/pkg/vm/create_evm.go index 3a559024c..230c19398 100644 --- a/pkg/vm/create_evm.go +++ b/pkg/vm/create_evm.go @@ -14,6 +14,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/binutils" "github.com/ava-labs/avalanche-cli/pkg/models" "github.com/ava-labs/avalanche-cli/pkg/teleporter" + icmgenesis "github.com/ava-labs/avalanche-cli/pkg/teleporter/genesis" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/subnet-evm/core" subnetevmparams "github.com/ava-labs/subnet-evm/params" @@ -112,8 +113,8 @@ func CreateEVMGenesis( params.initialTokenAllocation[common.HexToAddress(teleporterInfo.FundedAddress)] = core.GenesisAccount{ Balance: balance, } - addICMMessengerContractToGenesisAllocations(params.initialTokenAllocation) - if err := addICMRegistryContractToGenesisAllocations(params.initialTokenAllocation); err != nil { + icmgenesis.AddICMMessengerContractToAllocations(params.initialTokenAllocation) + if err := icmgenesis.AddICMRegistryContractToAllocations(params.initialTokenAllocation); err != nil { return nil, err } } From c868707f9573c81646285cfed6c1e60223b4838f Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Mon, 16 Sep 2024 17:54:19 -0400 Subject: [PATCH 21/74] nit --- cmd/blockchaincmd/create.go | 1 - pkg/vm/create_evm.go | 1 - 2 files changed, 2 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 30bcbbe40..07ee0835d 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -276,7 +276,6 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { deployTeleporter = params.UseTeleporter useExternalGasToken = params.UseExternalGasToken genesisBytes, err = vm.CreateEVMGenesis( - app, blockchainName, params, teleporterInfo, diff --git a/pkg/vm/create_evm.go b/pkg/vm/create_evm.go index 230c19398..3be78433e 100644 --- a/pkg/vm/create_evm.go +++ b/pkg/vm/create_evm.go @@ -73,7 +73,6 @@ func CreateEvmSidecar( } func CreateEVMGenesis( - app *application.Avalanche, blockchainName string, params SubnetEVMGenesisParams, teleporterInfo *teleporter.Info, From e87b8f18488b1771cd3a97e8aba43ddddc162ba9 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 16 Sep 2024 18:11:44 -0400 Subject: [PATCH 22/74] 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 23/74] 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 24/74] 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 25/74] 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 26/74] 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 27/74] 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 e2757a74d3b9d5e72254964b0814e2c7a1f7eeee Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Tue, 17 Sep 2024 11:48:09 -0400 Subject: [PATCH 28/74] nit --- pkg/contract/allocations.go | 36 +++++++++++++++++ pkg/subnet/local.go | 67 ++++++++++++++++++------------- pkg/teleporter/genesis/genesis.go | 25 ++++++++++-- 3 files changed, 95 insertions(+), 33 deletions(-) diff --git a/pkg/contract/allocations.go b/pkg/contract/allocations.go index 84b810bc2..f4514628e 100644 --- a/pkg/contract/allocations.go +++ b/pkg/contract/allocations.go @@ -235,3 +235,39 @@ func GetEVMSubnetGenesisNativeMinterAdmin( } return getGenesisNativeMinterAdmin(app, network, genesisData) } + +func ContractAddressIsInBlockchainGenesis( + app *application.Avalanche, + network models.Network, + chainSpec ChainSpec, + contractAddress common.Address, +) (bool, error) { + genesisData, err := GetBlockchainGenesis( + app, + network, + chainSpec, + ) + if err != nil { + return false, err + } + if !utils.ByteSliceIsSubnetEvmGenesis(genesisData) { + return false, fmt.Errorf("checking for contract belonging to genesis is only supported on EVM based vms") + } + return ContractAddressIsInGenesisData(genesisData, contractAddress) +} + +func ContractAddressIsInGenesisData( + genesisData []byte, + contractAddress common.Address, +) (bool, error) { + genesis, err := utils.ByteSliceToSubnetEvmGenesis(genesisData) + if err != nil { + return false, err + } + for address, allocation := range genesis.Alloc { + if address == contractAddress { + return len(allocation.Code) > 0, nil + } + } + return false, nil +} diff --git a/pkg/subnet/local.go b/pkg/subnet/local.go index d3b0f6e39..832853f20 100644 --- a/pkg/subnet/local.go +++ b/pkg/subnet/local.go @@ -101,7 +101,7 @@ type DeployInfo struct { // * it checks the gRPC is running, if not, it starts it // * kicks off the actual deployment func (d *LocalDeployer) DeployToLocalNetwork( - chain string, + blockchainName string, genesisPath string, icmSpec ICMSpec, subnetIDStr string, @@ -109,7 +109,7 @@ func (d *LocalDeployer) DeployToLocalNetwork( if err := d.StartServer(); err != nil { return nil, err } - return d.doDeploy(chain, genesisPath, icmSpec, subnetIDStr) + return d.doDeploy(blockchainName, genesisPath, icmSpec, subnetIDStr) } func (d *LocalDeployer) StartServer() error { @@ -153,7 +153,7 @@ func (d *LocalDeployer) BackendStartedHere() bool { // - deploy a new blockchain for the given VM ID, genesis, and available subnet ID // - waits completion of operation // - show status -func (d *LocalDeployer) doDeploy(chain string, genesisPath string, icmSpec ICMSpec, subnetIDStr string) (*DeployInfo, error) { +func (d *LocalDeployer) doDeploy(blockchainName string, genesisPath string, icmSpec ICMSpec, subnetIDStr string) (*DeployInfo, error) { needsRestart, avalancheGoBinPath, err := d.SetupLocalEnv() if err != nil { return nil, err @@ -176,7 +176,7 @@ func (d *LocalDeployer) doDeploy(chain string, genesisPath string, icmSpec ICMSp defer cancel() // loading sidecar before it's needed so we catch any error early - sc, err := d.app.LoadSidecar(chain) + sc, err := d.app.LoadSidecar(blockchainName) if err != nil { return nil, fmt.Errorf("failed to load sidecar: %w", err) } @@ -194,9 +194,9 @@ func (d *LocalDeployer) doDeploy(chain string, genesisPath string, icmSpec ICMSp } } - chainVMID, err := anrutils.VMID(chain) + chainVMID, err := anrutils.VMID(blockchainName) if err != nil { - return nil, fmt.Errorf("failed to create VM ID from %s: %w", chain, err) + return nil, fmt.Errorf("failed to create VM ID from %s: %w", blockchainName, err) } d.app.Log.Debug("this VM will get ID", zap.String("vm-id", chainVMID.String())) @@ -255,7 +255,7 @@ func (d *LocalDeployer) doDeploy(chain string, genesisPath string, icmSpec ICMSp logRootDir = clusterInfo.GetLogRootDir() if alreadyDeployed(chainVMID, clusterInfo) { - return nil, fmt.Errorf("subnet %s has already been deployed", chain) + return nil, fmt.Errorf("subnet %s has already been deployed", blockchainName) } numBlockchains := len(clusterInfo.CustomChains) @@ -280,11 +280,11 @@ func (d *LocalDeployer) doDeploy(chain string, genesisPath string, icmSpec ICMSp // if a chainConfig has been configured var ( chainConfig string - chainConfigFile = filepath.Join(d.app.GetSubnetDir(), chain, constants.ChainConfigFileName) + chainConfigFile = filepath.Join(d.app.GetSubnetDir(), blockchainName, constants.ChainConfigFileName) perNodeChainConfig string - perNodeChainConfigFile = filepath.Join(d.app.GetSubnetDir(), chain, constants.PerNodeChainConfigFileName) + perNodeChainConfigFile = filepath.Join(d.app.GetSubnetDir(), blockchainName, constants.PerNodeChainConfigFileName) subnetConfig string - subnetConfigFile = filepath.Join(d.app.GetSubnetDir(), chain, constants.SubnetConfigFileName) + subnetConfigFile = filepath.Join(d.app.GetSubnetDir(), blockchainName, constants.SubnetConfigFileName) ) if _, err := os.Stat(chainConfigFile); err == nil { // currently the ANR only accepts the file as a path, not its content @@ -309,14 +309,14 @@ func (d *LocalDeployer) doDeploy(chain string, genesisPath string, icmSpec ICMSp // the given VM ID, genesis, and available subnet ID blockchainSpecs := []*rpcpb.BlockchainSpec{ { - VmName: chain, + VmName: blockchainName, Genesis: genesisPath, SubnetId: &subnetIDStr, SubnetSpec: &rpcpb.SubnetSpec{ SubnetConfig: subnetConfig, }, ChainConfig: chainConfig, - BlockchainAlias: chain, + BlockchainAlias: blockchainName, PerNodeChainConfig: perNodeChainConfig, }, } @@ -426,40 +426,49 @@ func (d *LocalDeployer) doDeploy(chain string, genesisPath string, icmSpec ICMSp } // deploy current blockchain ux.Logger.PrintToUser("") - subnetID, blockchainID, err := utils.GetChainIDs(network.Endpoint, chain) + subnetID, blockchainID, err := utils.GetChainIDs(network.Endpoint, blockchainName) if err != nil { return nil, err } - teleporterKeyName := sc.TeleporterKey - if teleporterKeyName == "" { - genesisData, err := d.app.LoadRawGenesis(chain) + blockchainKeyName := sc.TeleporterKey + if blockchainKeyName == "" { + genesisData, err := d.app.LoadRawGenesis(blockchainName) if err != nil { return nil, err } - teleporterKeyName, _, _, err = GetSubnetAirdropKeyInfo(d.app, network, chain, genesisData) + blockchainKeyName, _, _, err = GetSubnetAirdropKeyInfo(d.app, network, blockchainName, genesisData) if err != nil { return nil, err } } - blockchainKey, err := key.LoadSoft(network.ID, d.app.GetKeyPath(teleporterKeyName)) + blockchainKey, err := key.LoadSoft(network.ID, d.app.GetKeyPath(blockchainKeyName)) if err != nil { return nil, err } - var alreadyDeployed bool - alreadyDeployed, icmMessengerAddress, icmRegistryAddress, err = icmd.Deploy( - chain, - network.BlockchainEndpoint(blockchainID), - blockchainKey.PrivKeyHex(), - true, - true, - false, - ) + genesisData, err := os.ReadFile(genesisPath) + if err != nil { + return nil, err + } + includedInGenesis, err := icmgenesis.GenesisIsICMEnabled(genesisData) if err != nil { return nil, err } - if alreadyDeployed { - // included in genesis + if includedInGenesis { + ux.Logger.PrintToUser("Teleporter Messenger and Registry already included in %s's Genesis", blockchainName) + icmMessengerAddress = icmgenesis.MessengerContractAddress icmRegistryAddress = icmgenesis.RegistryContractAddress + } else { + _, icmMessengerAddress, icmRegistryAddress, err = icmd.Deploy( + blockchainName, + network.BlockchainEndpoint(blockchainID), + blockchainKey.PrivKeyHex(), + true, + true, + false, + ) + if err != nil { + return nil, err + } } if sc.RunRelayer && !icmSpec.SkipRelayerDeploy { if !cchainAlreadyDeployed { diff --git a/pkg/teleporter/genesis/genesis.go b/pkg/teleporter/genesis/genesis.go index 67e005549..173dce242 100644 --- a/pkg/teleporter/genesis/genesis.go +++ b/pkg/teleporter/genesis/genesis.go @@ -9,6 +9,9 @@ import ( "math/big" "strings" + "github.com/ava-labs/avalanche-cli/pkg/application" + "github.com/ava-labs/avalanche-cli/pkg/contract" + "github.com/ava-labs/avalanche-cli/pkg/models" "github.com/ava-labs/subnet-evm/core" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -16,7 +19,7 @@ import ( const ( messengerVersion = "0x1" - messengerContractAddress = "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf" + MessengerContractAddress = "0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf" RegistryContractAddress = "0xF86Cb19Ad8405AEFa7d09C778215D2Cb6eBfB228" MessengerDeployerAddress = "0x618FEdD9A45a8C456812ecAAE70C671c6249DfaC" ) @@ -71,7 +74,7 @@ func AddICMMessengerContractToAllocations( setSimpleStorageValue(storage, blockchainIDSlot, "0x1") setSimpleStorageValue(storage, messageNonceSlot, "0x1") deployedMessengerBytes := common.FromHex(strings.TrimSpace(string(deployedMessengerBytecode))) - allocs[common.HexToAddress(messengerContractAddress)] = core.GenesisAccount{ + allocs[common.HexToAddress(MessengerContractAddress)] = core.GenesisAccount{ Balance: big.NewInt(0), Code: deployedMessengerBytes, Storage: storage, @@ -93,10 +96,10 @@ func AddICMRegistryContractToAllocations( ) storage := map[common.Hash]common.Hash{} setSimpleStorageValue(storage, latestVersionSlot, messengerVersion) - if err := setMappingStorageValue(storage, versionToAddressSlot, messengerVersion, messengerContractAddress); err != nil { + if err := setMappingStorageValue(storage, versionToAddressSlot, messengerVersion, MessengerContractAddress); err != nil { return err } - if err := setMappingStorageValue(storage, addressToVersionSlot, messengerContractAddress, messengerVersion); err != nil { + if err := setMappingStorageValue(storage, addressToVersionSlot, MessengerContractAddress, messengerVersion); err != nil { return err } deployedRegistryBytes := common.FromHex(strings.TrimSpace(string(deployedRegistryBytecode))) @@ -108,3 +111,17 @@ func AddICMRegistryContractToAllocations( } return nil } + +func BlockchainHasICMEnabledGenesis( + app *application.Avalanche, + network models.Network, + chainSpec contract.ChainSpec, +) (bool, error) { + return contract.ContractAddressIsInBlockchainGenesis(app, network, chainSpec, common.HexToAddress(MessengerContractAddress)) +} + +func GenesisIsICMEnabled( + genesisData []byte, +) (bool, error) { + return contract.ContractAddressIsInGenesisData(genesisData, common.HexToAddress(MessengerContractAddress)) +} From 7d829d77525e34a15e546f9e48e7c23a1e3f79f5 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 18 Sep 2024 14:57:47 -0400 Subject: [PATCH 29/74] 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 5cd3e1633e5ea8f536e0869913b1ab10cecc0954 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Wed, 18 Sep 2024 15:32:10 -0400 Subject: [PATCH 30/74] fix wiz stuff --- cmd/blockchaincmd/create.go | 6 ++++++ cmd/nodecmd/wiz.go | 1 + cmd/teleportercmd/deploy.go | 4 +++- pkg/subnet/local.go | 21 ++++++++++++++++++--- pkg/teleporter/genesis/genesis.go | 24 ++++++++++++++++++------ pkg/vm/create_evm.go | 12 +++++++++--- 6 files changed, 55 insertions(+), 13 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index 07ee0835d..b4b325005 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -45,6 +45,8 @@ type CreateFlags struct { useLatestReleasedVMVersion bool useLatestPreReleasedVMVersion bool useExternalGasToken bool + addICMMessengerToGenesis bool + addICMRegistryToGenesis bool } var ( @@ -101,6 +103,8 @@ configuration, pass the -f flag.`, cmd.Flags().BoolVar(&createFlags.useWarp, "warp", true, "generate a vm with warp support (needed for teleporter)") cmd.Flags().BoolVar(&createFlags.useTeleporter, "teleporter", false, "interoperate with other blockchains using teleporter") cmd.Flags().BoolVar(&createFlags.useExternalGasToken, "external-gas-token", false, "use a gas token from another blockchain") + cmd.Flags().BoolVar(&createFlags.addICMMessengerToGenesis, "icm-messenger-at-genesis", false, "setup ICM messenger smart contract on genesis") + cmd.Flags().BoolVar(&createFlags.addICMRegistryToGenesis, "icm-registry-at-genesis", false, "setup ICM registry smart contract on genesis") return cmd } @@ -279,6 +283,8 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { blockchainName, params, teleporterInfo, + createFlags.addICMMessengerToGenesis, + createFlags.addICMRegistryToGenesis, ) if err != nil { return err diff --git a/cmd/nodecmd/wiz.go b/cmd/nodecmd/wiz.go index d50ccf5db..2d42bf278 100644 --- a/cmd/nodecmd/wiz.go +++ b/cmd/nodecmd/wiz.go @@ -394,6 +394,7 @@ func wiz(cmd *cobra.Command, args []string) error { }, DeployMessenger: deployTeleporterMessenger, DeployRegistry: deployTeleporterRegistry, + ForceRegistryDeploy: true, Version: teleporterVersion, MessengerContractAddressPath: teleporterMessengerContractAddressPath, MessengerDeployerAddressPath: teleporterMessengerDeployerAddressPath, diff --git a/cmd/teleportercmd/deploy.go b/cmd/teleportercmd/deploy.go index e9b516860..2671b16a1 100644 --- a/cmd/teleportercmd/deploy.go +++ b/cmd/teleportercmd/deploy.go @@ -25,6 +25,7 @@ type DeployFlags struct { GenesisKey bool DeployMessenger bool DeployRegistry bool + ForceRegistryDeploy bool RPCURL string Version string MessengerContractAddressPath string @@ -62,6 +63,7 @@ func newDeployCmd() *cobra.Command { deployFlags.ChainFlags.AddToCmd(cmd, "deploy ICM", true) cmd.Flags().BoolVar(&deployFlags.DeployMessenger, "deploy-messenger", true, "deploy Teleporter Messenger") cmd.Flags().BoolVar(&deployFlags.DeployRegistry, "deploy-registry", true, "deploy Teleporter Registry") + cmd.Flags().BoolVar(&deployFlags.ForceRegistryDeploy, "force-registry-deploy", false, "deploy Teleporter Registry even if Messenger has already been deployed") cmd.Flags().StringVar(&deployFlags.RPCURL, "rpc-url", "", "use the given RPC URL to connect to the subnet") cmd.Flags().StringVar(&deployFlags.Version, "version", "latest", "version to deploy") cmd.Flags().StringVar(&deployFlags.MessengerContractAddressPath, "messenger-contract-address-path", "", "path to a messenger contract address file") @@ -188,7 +190,7 @@ func CallDeploy(_ []string, flags DeployFlags) error { privateKey, flags.DeployMessenger, flags.DeployRegistry, - false, + flags.ForceRegistryDeploy, ) if err != nil { return err diff --git a/pkg/subnet/local.go b/pkg/subnet/local.go index 832853f20..0e420d3c1 100644 --- a/pkg/subnet/local.go +++ b/pkg/subnet/local.go @@ -449,15 +449,30 @@ func (d *LocalDeployer) doDeploy(blockchainName string, genesisPath string, icmS if err != nil { return nil, err } - includedInGenesis, err := icmgenesis.GenesisIsICMEnabled(genesisData) + messengerAtGenesis, registryAtGenesis, err := icmgenesis.ICMAtGenesis(genesisData) if err != nil { return nil, err } - if includedInGenesis { + switch { + case registryAtGenesis: ux.Logger.PrintToUser("Teleporter Messenger and Registry already included in %s's Genesis", blockchainName) icmMessengerAddress = icmgenesis.MessengerContractAddress icmRegistryAddress = icmgenesis.RegistryContractAddress - } else { + case messengerAtGenesis: + ux.Logger.PrintToUser("Teleporter Messenger already included in %s's Genesis", blockchainName) + icmMessengerAddress = icmgenesis.MessengerContractAddress + _, _, icmRegistryAddress, err = icmd.Deploy( + blockchainName, + network.BlockchainEndpoint(blockchainID), + blockchainKey.PrivKeyHex(), + false, + true, + false, + ) + if err != nil { + return nil, err + } + default: _, icmMessengerAddress, icmRegistryAddress, err = icmd.Deploy( blockchainName, network.BlockchainEndpoint(blockchainID), diff --git a/pkg/teleporter/genesis/genesis.go b/pkg/teleporter/genesis/genesis.go index 173dce242..2ac58abf0 100644 --- a/pkg/teleporter/genesis/genesis.go +++ b/pkg/teleporter/genesis/genesis.go @@ -112,16 +112,28 @@ func AddICMRegistryContractToAllocations( return nil } -func BlockchainHasICMEnabledGenesis( +func ICMAtBlockchainGenesis( app *application.Avalanche, network models.Network, chainSpec contract.ChainSpec, -) (bool, error) { - return contract.ContractAddressIsInBlockchainGenesis(app, network, chainSpec, common.HexToAddress(MessengerContractAddress)) +) (bool, bool, error) { + genesisData, err := contract.GetBlockchainGenesis(app, network, chainSpec) + if err != nil { + return false, false, err + } + return ICMAtGenesis(genesisData) } -func GenesisIsICMEnabled( +func ICMAtGenesis( genesisData []byte, -) (bool, error) { - return contract.ContractAddressIsInGenesisData(genesisData, common.HexToAddress(MessengerContractAddress)) +) (bool, bool, error) { + messengerAtGenesis, err := contract.ContractAddressIsInGenesisData(genesisData, common.HexToAddress(MessengerContractAddress)) + if err != nil { + return false, false, err + } + registryAtGenesis, err := contract.ContractAddressIsInGenesisData(genesisData, common.HexToAddress(RegistryContractAddress)) + if err != nil { + return false, false, err + } + return messengerAtGenesis, registryAtGenesis, nil } diff --git a/pkg/vm/create_evm.go b/pkg/vm/create_evm.go index e7ee10d71..866d26432 100644 --- a/pkg/vm/create_evm.go +++ b/pkg/vm/create_evm.go @@ -76,6 +76,8 @@ func CreateEVMGenesis( blockchainName string, params SubnetEVMGenesisParams, teleporterInfo *teleporter.Info, + addICMMessengerToGenesis bool, + addICMRegistryToGenesis bool, ) ([]byte, error) { ux.Logger.PrintToUser("creating genesis for blockchain %s", blockchainName) @@ -104,9 +106,13 @@ func CreateEVMGenesis( params.initialTokenAllocation[common.HexToAddress(teleporterInfo.FundedAddress)] = core.GenesisAccount{ Balance: balance, } - icmgenesis.AddICMMessengerContractToAllocations(params.initialTokenAllocation) - if err := icmgenesis.AddICMRegistryContractToAllocations(params.initialTokenAllocation); err != nil { - return nil, err + if addICMMessengerToGenesis || addICMRegistryToGenesis { + icmgenesis.AddICMMessengerContractToAllocations(params.initialTokenAllocation) + if addICMRegistryToGenesis { + if err := icmgenesis.AddICMRegistryContractToAllocations(params.initialTokenAllocation); err != nil { + return nil, err + } + } } } From 4cbe6e74c8563d52b3a6056afbbec3892647efca Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 18 Sep 2024 15:33:11 -0400 Subject: [PATCH 31/74] 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 32/74] 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 33/74] 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 34/74] 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 35/74] 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 36/74] 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 fcda44c48dc8388cabf6e834373fcb585ba3c61c Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Wed, 18 Sep 2024 18:46:28 -0400 Subject: [PATCH 37/74] add a function to initialize PoA manager --- pkg/vm/create_evm.go | 6 ++++++ pkg/vm/evm_prompts.go | 1 + 2 files changed, 7 insertions(+) diff --git a/pkg/vm/create_evm.go b/pkg/vm/create_evm.go index 866d26432..8c2b58df4 100644 --- a/pkg/vm/create_evm.go +++ b/pkg/vm/create_evm.go @@ -16,6 +16,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/teleporter" icmgenesis "github.com/ava-labs/avalanche-cli/pkg/teleporter/genesis" "github.com/ava-labs/avalanche-cli/pkg/ux" + "github.com/ava-labs/avalanche-cli/pkg/validatormanager" blockchainSDK "github.com/ava-labs/avalanche-cli/sdk/blockchain" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/utils" @@ -116,6 +117,11 @@ func CreateEVMGenesis( } } + params.SoVUsePoAValidatorManager = true + if params.SoVUsePoAValidatorManager { + validatormanager.AddPoAValidatorManagerContractToAllocations(params.initialTokenAllocation) + } + if params.UseExternalGasToken { params.enableNativeMinterPrecompile = true params.nativeMinterPrecompileAllowList.AdminAddresses = append( diff --git a/pkg/vm/evm_prompts.go b/pkg/vm/evm_prompts.go index c67750186..449b3d944 100644 --- a/pkg/vm/evm_prompts.go +++ b/pkg/vm/evm_prompts.go @@ -90,6 +90,7 @@ type SubnetEVMGenesisParams struct { enableContractDeployerPrecompile bool contractDeployerPrecompileAllowList AllowList enableWarpPrecompile bool + SoVUsePoAValidatorManager bool } func PromptTokenSymbol( From dc0b321cb06735672194cf05bc51dd4d4c9bf7c1 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 19 Sep 2024 09:57:40 -0400 Subject: [PATCH 38/74] add lib folder --- ...eployed_poa_validator_manager_bytecode.txt | 1 + pkg/validatormanager/validatormanager.go | 66 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 pkg/validatormanager/deployed_poa_validator_manager_bytecode.txt create mode 100644 pkg/validatormanager/validatormanager.go diff --git a/pkg/validatormanager/deployed_poa_validator_manager_bytecode.txt b/pkg/validatormanager/deployed_poa_validator_manager_bytecode.txt new file mode 100644 index 000000000..7e19811ae --- /dev/null +++ b/pkg/validatormanager/deployed_poa_validator_manager_bytecode.txt @@ -0,0 +1 @@ +0x608060405234801561000f575f80fd5b506004361061011c575f3560e01c80638994ab49116100a9578063bee0a03f1161006e578063bee0a03f146102b4578063c974d1b6146102c7578063d5f20ff6146102cf578063df93d8de146102ef578063f2fde38b146102f9575f80fd5b80638994ab49146102295780638da5cb5b1461023c57806397fb70d414610280578063a3a65e4814610293578063b771b3bc146102a6575f80fd5b806360305d62116100ef57806360305d62146101ac57806361e2f490146101c957806366435abf146101dc578063715018a6146102075780638280a25a1461020f575f80fd5b80630322ed98146101205780630cdd098514610135578063467ef06f14610186578063580a400614610199575b5f80fd5b61013361012e366004612db8565b61030c565b005b610173610143366004612db8565b5f9081527fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb09602052604090205490565b6040519081526020015b60405180910390f35b610133610194366004612de7565b6104ca565b6101336101a7366004612e2a565b6104d8565b6101b4601481565b60405163ffffffff909116815260200161017d565b6101336101d7366004612e60565b6105e6565b6101ef6101ea366004612db8565b610c1a565b6040516001600160401b03909116815260200161017d565b610133610c2e565b610217603081565b60405160ff909116815260200161017d565b610173610237366004612ec0565b610c41565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b03165b6040516001600160a01b03909116815260200161017d565b61013361028e366004612db8565b610c5d565b6101336102a1366004612de7565b610c71565b6102686005600160991b0181565b6101336102c2366004612db8565b610e29565b610217601481565b6102e26102dd366004612db8565b610f38565b60405161017d9190612f19565b6101ef6202a30081565b610133610307366004612fcf565b61102b565b5f8181525f805160206137fa8339815191526020526040808220815160e0810190925280545f805160206137da83398151915293929190829060ff16600581111561035957610359612f05565b600581111561036a5761036a612f05565b81526001820154602082015260028201546001600160401b038082166040840152600160401b820481166060840152600160801b820481166080840152600160c01b909104811660a08301526003928301541660c090910152909150815160058111156103d9576103d9612f05565b146104435760405162461bcd60e51b815260206004820152602f60248201527f56616c696461746f724d616e616765723a2056616c696461746f72206e6f742060448201526e1c195b991a5b99c81c995b5bdd985b608a1b60648201526084015b60405180910390fd5b6005600160991b016001600160a01b031663ee5b48eb6104688584606001515f611065565b6040518263ffffffff1660e01b81526004016104849190613013565b6020604051808303815f875af11580156104a0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104c49190613045565b50505050565b6104d38161128a565b505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f8115801561051c5750825b90505f826001600160401b031660011480156105375750303b155b905081158015610545575080155b156105635760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561058d57845460ff60401b1916600160401b1785555b6105978787611602565b83156105dd57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b7fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb0a545f805160206137da8339815191529060ff16156106835760405162461bcd60e51b815260206004820152603360248201527f56616c696461746f724d616e616765723a20616c726561647920696e697469616044820152721b1a5e9959081d985b1a59185d1bdc881cd95d606a1b606482015260840161043a565b6005600160991b016001600160a01b0316634213cf786040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106c6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106ea9190613045565b83602001351461074c5760405162461bcd60e51b815260206004820152602760248201527f56616c696461746f724d616e616765723a20696e76616c696420626c6f636b636044820152661a185a5b88125160ca1b606482015260840161043a565b3061075d6060850160408601612fcf565b6001600160a01b0316146107cf5760405162461bcd60e51b815260206004820152603360248201527f56616c696461746f724d616e616765723a20696e76616c69642076616c696461604482015272746f72206d616e61676572206164647265737360681b606482015260840161043a565b5f6107dd606085018561305c565b91505f90508435602086013560146107fb6060890160408a01612fcf565b60408051602081019590955284019290925260e090811b6001600160e01b03199081166060808601919091529290921b6bffffffffffffffffffffffff1916606484015284901b166078820152607c0160405160208183030381529060405290505f805b838163ffffffff161015610b2a575f61087b606089018961305c565b8363ffffffff16818110610891576108916130a1565b90506020028101906108a391906130b5565b6108ac90613165565b80515f81815260098901602052604090205491925090156108df5760405162461bcd60e51b815260040161043a906131fc565b84825f0151836020015184604001516040516020016109019493929190613244565b60408051808303601f190181528282528b35602084015260e086901b6001600160e01b031916838301528151602481850301815260449093019182905296505f9160029161094f9190613293565b602060405180830381855afa15801561096a573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061098d9190613045565b5f83815260098a0160209081526040808320849055805160e0810182526002815287518184015287830180516001600160401b039081168385015260608301869052905181166080830152421660a082015260c0810184905284845260088d01909252909120815181549394509192909190829060ff19166001836005811115610a1957610a19612f05565b0217905550602082810151600183015560408301516002830180546060860151608087015160a08801516001600160401b039586166001600160801b031990941693909317600160401b92861692909202919091176001600160801b0316600160801b918516919091026001600160c01b031617600160c01b9184169190910217905560c0909301516003909201805467ffffffffffffffff191692841692909217909155840151610acc9116866132b8565b83516020808601516040516001600160401b039091168152929750909183917fb815f891730222788b3f8d66249b3a287ce680c3df13866fd9a4f37743ae1014910160405180910390a350505080610b23906132cb565b905061085f565b50600584018190555f610b3c86611620565b90505f610b4c82604001516117fa565b905080600285604051610b5f9190613293565b602060405180830381855afa158015610b7a573d5f803e3d5ffd5b5050506040513d601f19601f82011682018060405250810190610b9d9190613045565b14610c015760405162461bcd60e51b815260206004820152602e60248201527f56616c696461746f724d616e616765723a20696e76616c6964207375626e657460448201526d0818dbdb9d995c9cda5bdb88125160921b606482015260840161043a565b505050600a909201805460ff1916600117905550505050565b5f610c2482610f38565b6080015192915050565b610c36611963565b610c3f5f6119be565b565b5f610c4a611963565b610c548383611a2e565b90505b92915050565b610c65611963565b610c6e81611f93565b50565b5f805160206137da8339815191525f610c8983611620565b90505f80610c9a8360400151612204565b9150915080610cfc5760405162461bcd60e51b815260206004820152602860248201527f56616c696461746f724d616e616765723a20526567697374726174696f6e206e6044820152671bdd081d985b1a5960c21b606482015260840161043a565b5f82815260078501602052604081208054610d16906132ed565b9050118015610d48575060015f83815260088601602052604090205460ff166005811115610d4657610d46612f05565b145b610d645760405162461bcd60e51b815260040161043a9061331f565b5f8281526007850160205260408120610d7c91612d6e565b5f8281526008850160208181526040808420805460ff191660029081178255810180546001600160401b0342818116600160c01b026001600160c01b03909316929092178355600190930154875260098b0185528387208990559588905293835292548151600160801b90910490931683529082019290925283917ff8fd1c90fb9cfa2ca2358fdf5806b086ad43315d92b221c929efc7f105ce7568910160405180910390a25050505050565b5f8181527fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb076020526040812080545f805160206137da833981519152929190610e71906132ed565b9050118015610ea3575060015f83815260088301602052604090205460ff166005811115610ea157610ea1612f05565b145b610ebf5760405162461bcd60e51b815260040161043a9061331f565b5f82815260078201602052604090819020905163ee5b48eb60e01b81526005600160991b019163ee5b48eb91610ef89190600401613366565b6020604051808303815f875af1158015610f14573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104d39190613045565b6040805160e080820183525f8083526020808401829052838501829052606084018290526080840182905260a0840182905260c084018290528582525f805160206137fa83398151915290528390208351918201909352825491925f805160206137da83398151915292829060ff166005811115610fb857610fb8612f05565b6005811115610fc957610fc9612f05565b81526001820154602082015260028201546001600160401b038082166040840152600160401b820481166060840152600160801b820481166080840152600160c01b909104811660a083015260039092015490911660c0909101529392505050565b611033611963565b6001600160a01b03811661105c57604051631e4fbdf760e01b81525f600482015260240161043a565b610c6e816119be565b60408051603680825260608281019093525f91906020820181803683370190505090505f5b60028110156110de5761109e8160016133f0565b6110a9906008613403565b5081515f908390839081106110c0576110c06130a1565b60200101906001600160f81b03191690815f1a90535060010161108a565b505f5b6004811015611149576110f58160036133f0565b611100906008613403565b600263ffffffff16901c60f81b8282600261111b91906132b8565b8151811061112b5761112b6130a1565b60200101906001600160f81b03191690815f1a9053506001016110e1565b505f5b60208110156111ab5761116081601f6133f0565b61116b906008613403565b86901c60f81b8261117d8360066132b8565b8151811061118d5761118d6130a1565b60200101906001600160f81b03191690815f1a90535060010161114c565b505f5b6008811015611216576111c28160076133f0565b6111cd906008613403565b6001600160401b038616901c60f81b826111e88360266132b8565b815181106111f8576111f86130a1565b60200101906001600160f81b03191690815f1a9053506001016111ae565b505f5b60088110156112815761122d8160076133f0565b611238906008613403565b6001600160401b038516901c60f81b8261125383602e6132b8565b81518110611263576112636130a1565b60200101906001600160f81b03191690815f1a905350600101611219565b50949350505050565b6040805160e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c081018290525f805160206137da8339815191525f6112d985611620565b90505f806112ea8360400151612204565b91509150801561134f5760405162461bcd60e51b815260206004820152602a60248201527f56616c696461746f724d616e616765723a20726567697374726174696f6e20736044820152691d1a5b1b081d985b1a5960b21b606482015260840161043a565b5f828152600885016020526040808220815160e081019092528054829060ff16600581111561138057611380612f05565b600581111561139157611391612f05565b81526001820154602082015260028201546001600160401b038082166040840152600160401b820481166060840152600160801b820481166080840152600160c01b909104811660a08301526003928301541660c0909101529091505f908251600581111561140257611402612f05565b1480611420575060018251600581111561141e5761141e612f05565b145b61147f5760405162461bcd60e51b815260206004820152602a60248201527f56616c696461746f724d616e616765723a20696e76616c69642076616c696461604482015269746f722073746174757360b01b606482015260840161043a565b60038251600581111561149457611494612f05565b036114a1575060046114a5565b5060055b6020808301515f908152600988019091526040812055818160058111156114ce576114ce612f05565b908160058111156114e1576114e1612f05565b9052505f84815260088701602052604090208251815484929190829060ff1916600183600581111561151557611515612f05565b02179055506020820151600182015560408201516002820180546060850151608086015160a08701516001600160401b039586166001600160801b031990941693909317600160401b92861692909202919091176001600160801b0316600160801b918516919091026001600160c01b031617600160c01b9184169190910217905560c0909201516003909101805467ffffffffffffffff191691909216179055815160058111156115c9576115c9612f05565b60405185907f1c08e59656f1a18dc2da76826cdc52805c43e897a17c50faefb8ab3c1526cc16905f90a350919791965090945050505050565b61160a6123a5565b611613826123ee565b61161c81612407565b5050565b60408051606080820183525f8083526020830181905292820152905f805160206137da8339815191526040516306f8253560e41b815263ffffffff851660048201529091505f9081906005600160991b0190636f825350906024015f60405180830381865afa158015611695573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526116bc9190810190613429565b915091508061171c5760405162461bcd60e51b815260206004820152602660248201527f56616c696461746f724d616e616765723a20696e76616c69642077617270206d60448201526565737361676560d01b606482015260840161043a565b825482511461177f5760405162461bcd60e51b815260206004820152602960248201527f56616c696461746f724d616e616765723a20696e76616c696420736f757263656044820152680818da185a5b88125160ba1b606482015260840161043a565b60208201516001600160a01b0316156117f25760405162461bcd60e51b815260206004820152602f60248201527f56616c696461746f724d616e616765723a20696e76616c6964206f726967696e60448201526e2073656e646572206164647265737360881b606482015260840161043a565b509392505050565b5f815160261461181c5760405162461bcd60e51b815260040161043a906134fb565b5f805b600281101561186b576118338160016133f0565b61183e906008613403565b61ffff16848281518110611854576118546130a1565b016020015160f81c901b919091179060010161181f565b5061ffff81161561188e5760405162461bcd60e51b815260040161043a90613544565b5f805b60048110156118e9576118a58160036133f0565b6118b0906008613403565b63ffffffff16856118c28360026132b8565b815181106118d2576118d26130a1565b016020015160f81c901b9190911790600101611891565b5063ffffffff81161561190e5760405162461bcd60e51b815260040161043a90613587565b5f805b60208110156112815761192581601f6133f0565b611930906008613403565b8661193c8360066132b8565b8151811061194c5761194c6130a1565b016020015160f81c901b9190911790600101611911565b336119957f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b031614610c3f5760405163118cdaa760e01b815233600482015260240161043a565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b7fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb0a545f9060ff16611ab95760405162461bcd60e51b815260206004820152602f60248201527f56616c696461746f724d616e616765723a2076616c696461746f72207365742060448201526e1b9bdd081a5b9a5d1a585b1a5e9959608a1b606482015260840161043a565b5f805160206137da83398151915242611ad860408601602087016135ce565b6001600160401b031611611b4a5760405162461bcd60e51b815260206004820152603360248201527f56616c696461746f724d616e616765723a20726567697374726174696f6e20656044820152727870697279206e6f7420696e2066757475726560681b606482015260840161043a565b611b5a60408501602086016135ce565b6001600160401b0316611b706202a300426132b8565b11611be35760405162461bcd60e51b815260206004820152603760248201527f56616c696461746f724d616e616765723a20726567697374726174696f6e206560448201527f787069727920746f6f2066617220696e20667574757265000000000000000000606482015260840161043a565b8335611c3b5760405162461bcd60e51b815260206004820152602160248201527f56616c696461746f724d616e616765723a20696e76616c6964206e6f646520496044820152601160fa1b606482015260840161043a565b83355f90815260098201602052604090205415611c6a5760405162461bcd60e51b815260040161043a906131fc565b6030611c7960408601866135e7565b905014611cde5760405162461bcd60e51b815260206004820152602d60248201527f56616c696461746f724d616e616765723a20696e76616c696420626c7350756260448201526c0d8d2c696caf240d8cadccee8d609b1b606482015260840161043a565b611ce8835f612418565b5f80611d826040518060a0016040528085600101548152602001885f01358152602001876001600160401b03168152602001886020016020810190611d2d91906135ce565b6001600160401b03168152602001611d4860408a018a6135e7565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505050915250612626565b5f82815260078601602052604090209193509150611da08282613674565b5060405163ee5b48eb60e01b81525f906005600160991b019063ee5b48eb90611dcd908590600401613013565b6020604051808303815f875af1158015611de9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e0d9190613045565b6040805160e08101909152909150806001815288356020808301919091526001600160401b03891660408084018290525f60608501819052608085019290925260a0840182905260c0909301819052868152600888019091522081518154829060ff19166001836005811115611e8557611e85612f05565b021790555060208281015160018301556040808401516002840180546060870151608088015160a08901516001600160401b039586166001600160801b031990941693909317600160401b92861692909202919091176001600160801b0316600160801b918516919091026001600160c01b031617600160c01b9184169190910217905560c0909401516003909301805467ffffffffffffffff19169390941692909217909255829189359186917f79b81620b81daf2c08cd5bb3dbb79e75d2d7a87f52171fde5aadc8c47823026e918b91611f65918e01908e016135ce565b604080516001600160401b0393841681529290911660208301520160405180910390a4509095945050505050565b5f8181525f805160206137fa8339815191526020526040808220815160e0810190925280545f805160206137da83398151915293929190829060ff166005811115611fe057611fe0612f05565b6005811115611ff157611ff1612f05565b8152600182015460208201526002808301546001600160401b038082166040850152600160401b820481166060850152600160801b820481166080850152600160c01b909104811660a084015260039093015490921660c0909101529091508151600581111561206357612063612f05565b146120bf5760405162461bcd60e51b815260206004820152602660248201527f56616c696461746f724d616e616765723a2076616c696461746f72206e6f742060448201526561637469766560d01b606482015260840161043a565b60038152426001600160401b031660c08201525f83815260088301602052604090208151815483929190829060ff1916600183600581111561210357612103612f05565b02179055506020820151600182015560408201516002820180546060850151608086015160a08701516001600160401b039586166001600160801b031990941693909317600160401b92861692909202919091176001600160801b0316600160801b918516919091026001600160c01b031617600160c01b9184169190910217905560c0909201516003909101805467ffffffffffffffff1916919092161790555f6121af84826129d5565b6080840151604080516001600160401b03909216825242602083015291935083925086917f13d58394cf269d48bcf927959a29a5ffee7c9924dafff8927ecdf3c48ffa7c67910160405180910390a350505050565b5f8082516027146122275760405162461bcd60e51b815260040161043a906134fb565b5f805b60028110156122765761223e8160016133f0565b612249906008613403565b61ffff1685828151811061225f5761225f6130a1565b016020015160f81c901b919091179060010161222a565b5061ffff8116156122995760405162461bcd60e51b815260040161043a90613544565b5f805b60048110156122f4576122b08160036133f0565b6122bb906008613403565b63ffffffff16866122cd8360026132b8565b815181106122dd576122dd6130a1565b016020015160f81c901b919091179060010161229c565b5063ffffffff811660031461231b5760405162461bcd60e51b815260040161043a90613587565b5f805b60208110156123705761233281601f6133f0565b61233d906008613403565b876123498360066132b8565b81518110612359576123596130a1565b016020015160f81c901b919091179060010161231e565b505f86602681518110612385576123856130a1565b016020015191976001600160f81b03199092161515965090945050505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff16610c3f57604051631afcd79f60e31b815260040160405180910390fd5b6123f66123a5565b6123fe612b2c565b610c6e81612b34565b61240f6123a5565b610c6e81612cd6565b5f805160206137da8339815191525f6001600160401b03808416908516111561244c576124458385613733565b9050612459565b6124568484613733565b90505b6040805160808101825260038401548082526004850154602083015260058501549282019290925260068401546001600160401b03166060820152429115806124bb5750600284015481516124b7916001600160401b0316906132b8565b8210155b156124e1576001600160401b038316606082015281815260408101516020820152612500565b82816060018181516124f39190613753565b6001600160401b03169052505b6060810151612510906064613773565b602082015160028601546001600160401b03929092169161253b9190600160401b900460ff16613403565b101561259f5760405162461bcd60e51b815260206004820152602d60248201527f56616c696461746f724d616e616765723a206d6178696d756d20636875726e2060448201526c1c985d1948195e18d959591959609a1b606482015260840161043a565b856001600160401b0316816040018181516125ba91906132b8565b9052506040810180516001600160401b03871691906125da9083906133f0565b905250805160038501556020810151600485015560408101516005850155606001516006909301805467ffffffffffffffff19166001600160401b039094169390931790925550505050565b5f60608260800151516030146126905760405162461bcd60e51b815260206004820152602960248201527f5374616b696e674d657373616765733a20696e76616c6964207369676e6174756044820152680e4ca40d8cadccee8d60bb1b606482015260840161043a565b60408051608680825260c082019092525f916020820181803683370190505090505f5b6002811015612707576126c78160016133f0565b6126d2906008613403565b5081515f908390839081106126e9576126e96130a1565b60200101906001600160f81b03191690815f1a9053506001016126b3565b505f5b600481101561276f5761271e8160036133f0565b612729906008613403565b600160ff919091161c60f81b826127418360026132b8565b81518110612751576127516130a1565b60200101906001600160f81b03191690815f1a90535060010161270a565b505f5b60208110156127cc578451816020811061278e5761278e6130a1565b1a60f81b8261279e8360066132b8565b815181106127ae576127ae6130a1565b60200101906001600160f81b03191690815f1a905350600101612772565b505f5b602081101561282c57846020015181602081106127ee576127ee6130a1565b1a60f81b826127fe8360266132b8565b8151811061280e5761280e6130a1565b60200101906001600160f81b03191690815f1a9053506001016127cf565b505f5b60088110156128a0576128438160076133f0565b61284e906008613403565b60ff1685604001516001600160401b0316901c60f81b8282604661287291906132b8565b81518110612882576128826130a1565b60200101906001600160f81b03191690815f1a90535060010161282f565b505f5b603081101561290b57846080015181815181106128c2576128c26130a1565b01602001516001600160f81b031916826128dd83604e6132b8565b815181106128ed576128ed6130a1565b60200101906001600160f81b03191690815f1a9053506001016128a3565b505f5b600881101561297d576129228160076133f0565b61292d906008613403565b60608601516001600160401b0390811691161c60f81b8261294f83607e6132b8565b8151811061295f5761295f6130a1565b60200101906001600160f81b03191690815f1a90535060010161290e565b5060028160405161298e9190613293565b602060405180830381855afa1580156129a9573d5f803e3d5ffd5b5050506040513d601f19601f820116820180604052508101906129cc9190613045565b94909350915050565b5f8281525f805160206137fa833981519152602052604081206002015481905f805160206137da83398151915290600160801b90046001600160401b0316612a1d8582612418565b5f612a2787612cde565b5f8881526008850160205260408120600201805467ffffffffffffffff60801b1916600160801b6001600160401b038b16021790559091506005600160991b0163ee5b48eb612a778a858b611065565b6040518263ffffffff1660e01b8152600401612a939190613013565b6020604051808303815f875af1158015612aaf573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ad39190613045565b604080516001600160401b038a811682526020820184905282519394508516928b927f07de5ff35a674a8005e661f3333c907ca6333462808762d19dc7b3abb1a8c1df928290030190a3909450925050505b9250929050565b610c3f6123a5565b612b3c6123a5565b80355f805160206137da83398151915290815560208201357fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb01556014612b88608084016060850161379e565b60ff161115612bf55760405162461bcd60e51b815260206004820152603360248201527f56616c696461746f724d616e616765723a206d6178696d756d20636875726e206044820152720e0cae4c6cadce8c2ceca40e8dede40d0d2ced606b1b606482015260840161043a565b5f612c06608084016060850161379e565b60ff1611612c6e5760405162461bcd60e51b815260206004820152602f60248201527f56616c696461746f724d616e616765723a207a65726f206d6178696d756d206360448201526e6875726e2070657263656e7461676560881b606482015260840161043a565b612c7e608083016060840161379e565b60028201805460ff92909216600160401b0260ff60401b19909216919091179055612caf60608301604084016135ce565b600291909101805467ffffffffffffffff19166001600160401b0390921691909117905550565b6110336123a5565b5f8181525f805160206137fa8339815191526020526040812060020180545f805160206137da83398151915291600160401b9091046001600160401b0316906008612d28836137be565b82546101009290920a6001600160401b038181021990931691831602179091555f94855260089290920160205250604090922060020154600160401b9004909116919050565b508054612d7a906132ed565b5f825580601f10612d89575050565b601f0160209004905f5260205f2090810190610c6e91905b80821115612db4575f8155600101612da1565b5090565b5f60208284031215612dc8575f80fd5b5035919050565b803563ffffffff81168114612de2575f80fd5b919050565b5f60208284031215612df7575f80fd5b610c5482612dcf565b5f60808284031215612e10575f80fd5b50919050565b6001600160a01b0381168114610c6e575f80fd5b5f8060a08385031215612e3b575f80fd5b612e458484612e00565b91506080830135612e5581612e16565b809150509250929050565b5f8060408385031215612e71575f80fd5b82356001600160401b03811115612e86575f80fd5b612e9285828601612e00565b925050612ea160208401612dcf565b90509250929050565b80356001600160401b0381168114612de2575f80fd5b5f8060408385031215612ed1575f80fd5b82356001600160401b03811115612ee6575f80fd5b830160608186031215612ef7575f80fd5b9150612ea160208401612eaa565b634e487b7160e01b5f52602160045260245ffd5b815160e082019060068110612f3c57634e487b7160e01b5f52602160045260245ffd5b80835250602083015160208301526001600160401b0360408401511660408301526060830151612f7760608401826001600160401b03169052565b506080830151612f9260808401826001600160401b03169052565b5060a0830151612fad60a08401826001600160401b03169052565b5060c0830151612fc860c08401826001600160401b03169052565b5092915050565b5f60208284031215612fdf575f80fd5b8135612fea81612e16565b9392505050565b5f5b8381101561300b578181015183820152602001612ff3565b50505f910152565b602081525f8251806020840152613031816040850160208701612ff1565b601f01601f19169190910160400192915050565b5f60208284031215613055575f80fd5b5051919050565b5f808335601e19843603018112613071575f80fd5b8301803591506001600160401b0382111561308a575f80fd5b6020019150600581901b3603821315612b25575f80fd5b634e487b7160e01b5f52603260045260245ffd5b5f8235605e198336030181126130c9575f80fd5b9190910192915050565b634e487b7160e01b5f52604160045260245ffd5b604051606081016001600160401b0381118282101715613109576131096130d3565b60405290565b604051601f8201601f191681016001600160401b0381118282101715613137576131376130d3565b604052919050565b5f6001600160401b03821115613157576131576130d3565b50601f01601f191660200190565b5f60608236031215613175575f80fd5b61317d6130e7565b82358152602061318e818501612eaa565b8183015260408401356001600160401b038111156131aa575f80fd5b840136601f8201126131ba575f80fd5b80356131cd6131c88261313f565b61310f565b81815236848385010111156131e0575f80fd5b81848401858301375f9181019093015250604082015292915050565b60208082526028908201527f56616c696461746f724d616e616765723a206e6f646520494420616c72656164604082015267792061637469766560c01b606082015260800190565b5f8551613255818460208a01612ff1565b80830190508581526001600160401b0360c01b8560c01b1660208201528351613285816028840160208801612ff1565b016028019695505050505050565b5f82516130c9818460208701612ff1565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610c5757610c576132a4565b5f63ffffffff8083168181036132e3576132e36132a4565b6001019392505050565b600181811c9082168061330157607f821691505b602082108103612e1057634e487b7160e01b5f52602260045260245ffd5b60208082526027908201527f56616c696461746f724d616e616765723a20696e76616c69642076616c6964616040820152661d1a5bdb88125160ca1b606082015260800190565b5f60208083525f8454613378816132ed565b806020870152604060018084165f811461339957600181146133b5576133e2565b60ff19851660408a0152604084151560051b8a010195506133e2565b895f5260205f205f5b858110156133d95781548b82018601529083019088016133be565b8a016040019650505b509398975050505050505050565b81810381811115610c5757610c576132a4565b8082028115828204841417610c5757610c576132a4565b80518015158114612de2575f80fd5b5f806040838503121561343a575f80fd5b82516001600160401b0380821115613450575f80fd5b9084019060608287031215613463575f80fd5b61346b6130e7565b8251815260208084015161347e81612e16565b82820152604084015183811115613493575f80fd5b80850194505087601f8501126134a7575f80fd5b835192506134b76131c88461313f565b83815288828587010111156134ca575f80fd5b6134d984838301848801612ff1565b806040840152508195506134ee81880161341a565b9450505050509250929050565b60208082526029908201527f56616c696461746f724d657373616765733a20696e76616c6964206d657373616040820152680ceca40d8cadccee8d60bb1b606082015260800190565b60208082526023908201527f56616c696461746f724d657373616765733a20696e76616c696420636f64656360408201526208125160ea1b606082015260800190565b60208082526027908201527f56616c696461746f724d657373616765733a20696e76616c6964206d657373616040820152666765207479706560c81b606082015260800190565b5f602082840312156135de575f80fd5b610c5482612eaa565b5f808335601e198436030181126135fc575f80fd5b8301803591506001600160401b03821115613615575f80fd5b602001915036819003821315612b25575f80fd5b601f8211156104d357805f5260205f20601f840160051c8101602085101561364e5750805b601f840160051c820191505b8181101561366d575f815560010161365a565b5050505050565b81516001600160401b0381111561368d5761368d6130d3565b6136a18161369b84546132ed565b84613629565b602080601f8311600181146136d4575f84156136bd5750858301515b5f19600386901b1c1916600185901b17855561372b565b5f85815260208120601f198616915b82811015613702578886015182559484019460019091019084016136e3565b508582101561371f57878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b6001600160401b03828116828216039080821115612fc857612fc86132a4565b6001600160401b03818116838216019080821115612fc857612fc86132a4565b6001600160401b03818116838216028082169190828114613796576137966132a4565b505092915050565b5f602082840312156137ae575f80fd5b813560ff81168114612fea575f80fd5b5f6001600160401b038083168181036132e3576132e36132a456fee92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb00e92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb08a164736f6c6343000819000a diff --git a/pkg/validatormanager/validatormanager.go b/pkg/validatormanager/validatormanager.go new file mode 100644 index 000000000..0d62904c1 --- /dev/null +++ b/pkg/validatormanager/validatormanager.go @@ -0,0 +1,66 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package validatormanager + +import ( + _ "embed" + "math/big" + "strings" + + "github.com/ava-labs/avalanche-cli/pkg/contract" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/subnet-evm/core" + "github.com/ethereum/go-ethereum/common" +) + +const ( + PoAValidarorMessengerContractAddress = "0x5F584C2D56B4c356e7d82EC6129349393dc5df17" +) + +//go:embed deployed_poa_validator_manager_bytecode.txt +var deployedPoAValidatorManagerBytecode []byte + +func AddPoAValidatorManagerContractToAllocations( + allocs core.GenesisAlloc, +) { + deployedPoaValidatorManagerBytes := common.FromHex(strings.TrimSpace(string(deployedPoAValidatorManagerBytecode))) + allocs[common.HexToAddress(PoAValidarorMessengerContractAddress)] = core.GenesisAccount{ + Balance: big.NewInt(0), + Code: deployedPoaValidatorManagerBytes, + Nonce: 1, + } +} + +func InitializePoAValidatorManager( + rpcURL string, + remoteAddress common.Address, + privateKey string, + subnetID ids.ID, + initialOwner common.Address, +) error { + pChainBlockchainID := ids.Empty + churnPeriodSeconds := uint64(0) + maximumChurnPercentage := uint8(20) + type Params struct { + PChainBlockchainID [32]byte + SubnetID [32]byte + ChurnPeriodSeconds uint64 + MaximumChurnPercentage uint8 + } + params := Params{ + PChainBlockchainID: pChainBlockchainID, + SubnetID: subnetID, + ChurnPeriodSeconds: churnPeriodSeconds, + MaximumChurnPercentage: maximumChurnPercentage, + } + _, _, err := contract.TxToMethod( + rpcURL, + privateKey, + remoteAddress, + nil, + "initialize((bytes32,bytes32,uint64,uint8),address)", + params, + initialOwner, + ) + return err +} From c8c4e5ce98981a3617802dd1ac4240f21ffba894 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 19 Sep 2024 12:56:20 -0400 Subject: [PATCH 39/74] 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 40/74] 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 41/74] 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 42/74] 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 43/74] 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 44/74] 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 45/74] 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 46/74] 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 47/74] 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 48/74] 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 0659211f357a58230d187e24e9f3389a7a12e470 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 19 Sep 2024 16:49:41 -0400 Subject: [PATCH 49/74] address PR comments --- cmd/blockchaincmd/create.go | 5 +---- cmd/blockchaincmd/describe.go | 6 +++--- pkg/contract/allocations.go | 2 +- pkg/teleporter/genesis/genesis.go | 7 ++----- pkg/utils/strings.go | 5 +++++ pkg/vm/create_evm.go | 12 +++++------- 6 files changed, 17 insertions(+), 20 deletions(-) diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index b4b325005..78396bcae 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -45,7 +45,6 @@ type CreateFlags struct { useLatestReleasedVMVersion bool useLatestPreReleasedVMVersion bool useExternalGasToken bool - addICMMessengerToGenesis bool addICMRegistryToGenesis bool } @@ -103,8 +102,7 @@ configuration, pass the -f flag.`, cmd.Flags().BoolVar(&createFlags.useWarp, "warp", true, "generate a vm with warp support (needed for teleporter)") cmd.Flags().BoolVar(&createFlags.useTeleporter, "teleporter", false, "interoperate with other blockchains using teleporter") cmd.Flags().BoolVar(&createFlags.useExternalGasToken, "external-gas-token", false, "use a gas token from another blockchain") - cmd.Flags().BoolVar(&createFlags.addICMMessengerToGenesis, "icm-messenger-at-genesis", false, "setup ICM messenger smart contract on genesis") - cmd.Flags().BoolVar(&createFlags.addICMRegistryToGenesis, "icm-registry-at-genesis", false, "setup ICM registry smart contract on genesis") + cmd.Flags().BoolVar(&createFlags.addICMRegistryToGenesis, "icm-registry-at-genesis", false, "setup ICM registry smart contract on genesis [experimental]") return cmd } @@ -283,7 +281,6 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { blockchainName, params, teleporterInfo, - createFlags.addICMMessengerToGenesis, createFlags.addICMRegistryToGenesis, ) if err != nil { diff --git a/cmd/blockchaincmd/describe.go b/cmd/blockchaincmd/describe.go index 23b0943cf..d11e51265 100644 --- a/cmd/blockchaincmd/describe.go +++ b/cmd/blockchaincmd/describe.go @@ -276,14 +276,14 @@ func printAllocations(sc models.Sidecar, genesis core.Genesis) error { t.Style().Options.SeparateRows = true t.SetTitle("Initial Token Allocation") t.AppendHeader(table.Row{"Description", "Address and Private Key", "Amount (10^18)", "Amount (wei)"}) - for address := range genesis.Alloc { - if len(genesis.Alloc[address].Code) > 0 { + for address, allocation := range genesis.Alloc { + if len(allocation.Code) > 0 { continue } if address == common.HexToAddress(icmgenesis.MessengerDeployerAddress) { continue } - amount := genesis.Alloc[address].Balance + amount := allocation.Balance formattedAmount := new(big.Int).Div(amount, big.NewInt(params.Ether)) description := "" privKey := "" diff --git a/pkg/contract/allocations.go b/pkg/contract/allocations.go index f4514628e..b4f6d3092 100644 --- a/pkg/contract/allocations.go +++ b/pkg/contract/allocations.go @@ -251,7 +251,7 @@ func ContractAddressIsInBlockchainGenesis( return false, err } if !utils.ByteSliceIsSubnetEvmGenesis(genesisData) { - return false, fmt.Errorf("checking for contract belonging to genesis is only supported on EVM based vms") + return false, fmt.Errorf("only EVM based vms support genesis contract checks") } return ContractAddressIsInGenesisData(genesisData, contractAddress) } diff --git a/pkg/teleporter/genesis/genesis.go b/pkg/teleporter/genesis/genesis.go index 2ac58abf0..f6583a319 100644 --- a/pkg/teleporter/genesis/genesis.go +++ b/pkg/teleporter/genesis/genesis.go @@ -12,6 +12,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/application" "github.com/ava-labs/avalanche-cli/pkg/contract" "github.com/ava-labs/avalanche-cli/pkg/models" + "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/subnet-evm/core" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -38,12 +39,8 @@ func setSimpleStorageValue( storage[common.HexToHash(slot)] = common.HexToHash(value) } -func trimHexa(s string) string { - return strings.TrimPrefix(strings.TrimPrefix(s, "0x"), "0X") -} - func hexFill32(s string) string { - return fmt.Sprintf("%064s", trimHexa(s)) + return fmt.Sprintf("%064s", utils.TrimHexa(s)) } func setMappingStorageValue( diff --git a/pkg/utils/strings.go b/pkg/utils/strings.go index a6ff8cb34..ad78d1377 100644 --- a/pkg/utils/strings.go +++ b/pkg/utils/strings.go @@ -71,3 +71,8 @@ func FormatAmount(amount *big.Int, decimals uint8) string { val := new(big.Float).Quo(amountFloat, divisor) return fmt.Sprintf("%.*f", decimals, val) } + +// Removes the leading 0x/0X part of a hexadecimal string representation +func TrimHexa(s string) string { + return strings.TrimPrefix(strings.TrimPrefix(s, "0x"), "0X") +} diff --git a/pkg/vm/create_evm.go b/pkg/vm/create_evm.go index 866d26432..5bddffefb 100644 --- a/pkg/vm/create_evm.go +++ b/pkg/vm/create_evm.go @@ -76,7 +76,6 @@ func CreateEVMGenesis( blockchainName string, params SubnetEVMGenesisParams, teleporterInfo *teleporter.Info, - addICMMessengerToGenesis bool, addICMRegistryToGenesis bool, ) ([]byte, error) { ux.Logger.PrintToUser("creating genesis for blockchain %s", blockchainName) @@ -106,12 +105,11 @@ func CreateEVMGenesis( params.initialTokenAllocation[common.HexToAddress(teleporterInfo.FundedAddress)] = core.GenesisAccount{ Balance: balance, } - if addICMMessengerToGenesis || addICMRegistryToGenesis { - icmgenesis.AddICMMessengerContractToAllocations(params.initialTokenAllocation) - if addICMRegistryToGenesis { - if err := icmgenesis.AddICMRegistryContractToAllocations(params.initialTokenAllocation); err != nil { - return nil, err - } + icmgenesis.AddICMMessengerContractToAllocations(params.initialTokenAllocation) + if addICMRegistryToGenesis { + // experimental + if err := icmgenesis.AddICMRegistryContractToAllocations(params.initialTokenAllocation); err != nil { + return nil, err } } } From 4bc4e5dee1333254e2e64177f3be186b3fd84e55 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 20 Sep 2024 12:25:06 -0400 Subject: [PATCH 50/74] basic PoA manager setting --- cmd/blockchaincmd/upgradecmd/generate.go | 2 +- pkg/vm/allowlist.go | 9 +- pkg/vm/create_evm.go | 2 +- pkg/vm/evm_prompts.go | 169 +++++++++++++---------- pkg/vm/evm_settings.go | 1 + 5 files changed, 109 insertions(+), 74 deletions(-) diff --git a/cmd/blockchaincmd/upgradecmd/generate.go b/cmd/blockchaincmd/upgradecmd/generate.go index b4cd36dbd..da8052416 100644 --- a/cmd/blockchaincmd/upgradecmd/generate.go +++ b/cmd/blockchaincmd/upgradecmd/generate.go @@ -598,7 +598,7 @@ func promptAdminManagerAndEnabledAddresses( sc *models.Sidecar, action string, ) ([]common.Address, []common.Address, []common.Address, bool, error) { - allowList, cancelled, err := vm.GenerateAllowList(app, action, sc.VMVersion) + allowList, cancelled, err := vm.GenerateAllowList(app, vm.AllowList{}, action, sc.VMVersion) if cancelled || err != nil { return nil, nil, nil, cancelled, err } diff --git a/pkg/vm/allowlist.go b/pkg/vm/allowlist.go index 536c7ace1..53851a1bb 100644 --- a/pkg/vm/allowlist.go +++ b/pkg/vm/allowlist.go @@ -98,6 +98,7 @@ func removeAddress( func GenerateAllowList( app *application.Avalanche, + allowList AllowList, action string, evmVersion string, ) (AllowList, bool, error) { @@ -106,8 +107,6 @@ func GenerateAllowList( } managerRoleEnabled := semver.Compare(evmVersion, "v0.6.4") >= 0 - allowList := AllowList{} - promptTemplate := "Configure the addresses that are allowed to %s" prompt := fmt.Sprintf(promptTemplate, action) @@ -122,6 +121,12 @@ func GenerateAllowList( enabledOption := "Enabled" explainOption := "Explain the difference" + if len(allowList.AdminAddresses) != 0 || len(allowList.ManagerAddresses) != 0 || len(allowList.EnabledAddresses) != 0 { + fmt.Println() + fmt.Printf(logging.Bold.Wrap("Addresses automatically allowed to %s\n"), action) + preview(allowList) + } + for { options := []string{addOption, removeOption, previewOption, confirmOption, cancelOption} if len(allowList.AdminAddresses) == 0 && len(allowList.ManagerAddresses) == 0 && len(allowList.EnabledAddresses) == 0 { diff --git a/pkg/vm/create_evm.go b/pkg/vm/create_evm.go index c59f03d4f..0a12152ba 100644 --- a/pkg/vm/create_evm.go +++ b/pkg/vm/create_evm.go @@ -114,7 +114,7 @@ func CreateEVMGenesis( } } - if params.SoVUsePoAValidatorManager { + if params.UsePoAValidatorManager { validatormanager.AddPoAValidatorManagerContractToAllocations(params.initialTokenAllocation) } diff --git a/pkg/vm/evm_prompts.go b/pkg/vm/evm_prompts.go index cde03220e..d3322b0d1 100644 --- a/pkg/vm/evm_prompts.go +++ b/pkg/vm/evm_prompts.go @@ -13,6 +13,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/models" "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/ux" + "github.com/ava-labs/avalanche-cli/pkg/validatormanager" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/plugin/evm" "github.com/ethereum/go-ethereum/common" @@ -52,12 +53,6 @@ const ( confirmAddressAllocationOption = "Confirm and finalize the initial token allocation" ) -var DefaultEwoqAllocation = core.GenesisAlloc{ - PrefundedEwoqAddress: { - Balance: defaultEVMAirdropAmount, - }, -} - type FeeConfig struct { lowThroughput bool mediumThroughput bool @@ -90,7 +85,7 @@ type SubnetEVMGenesisParams struct { enableContractDeployerPrecompile bool contractDeployerPrecompileAllowList AllowList enableWarpPrecompile bool - SoVUsePoAValidatorManager bool + UsePoAValidatorManager bool } func PromptTokenSymbol( @@ -175,8 +170,18 @@ func PromptSubnetEVMGenesisParams( err error params SubnetEVMGenesisParams ) + params.initialTokenAllocation = core.GenesisAlloc{} - params.SoVUsePoAValidatorManager = sc.ValidatorManagement == models.ProofOfAuthority + if sc.ValidatorManagement == models.ProofOfAuthority { + params.UsePoAValidatorManager = true + params.enableNativeMinterPrecompile = true + params.nativeMinterPrecompileAllowList.EnabledAddresses = []common.Address{ + common.HexToAddress(validatormanager.PoAValidarorMessengerContractAddress), + } + params.initialTokenAllocation[common.HexToAddress(sc.ValidatorManagerController)] = core.GenesisAccount{ + Balance: defaultPoAOwnerBalance, + } + } // Chain ID params.chainID = chainID @@ -325,47 +330,57 @@ func displayAllocations(alloc core.GenesisAlloc) { table.Render() } -func createNewKeyAllocation(app *application.Avalanche, subnetName string) (core.GenesisAlloc, error) { +func addNewKeyAllocation(allocations core.GenesisAlloc, app *application.Avalanche, subnetName string) error { keyName := utils.GetDefaultBlockchainAirdropKeyName(subnetName) k, err := app.GetKey(keyName, models.NewLocalNetwork(), true) if err != nil { - return core.GenesisAlloc{}, err + return err } ux.Logger.PrintToUser("prefunding address %s with balance %s", k.C(), defaultEVMAirdropAmount) + allocations[common.HexToAddress(k.C())] = core.GenesisAccount{ + Balance: defaultEVMAirdropAmount, + } + return nil +} - return core.GenesisAlloc{ - common.HexToAddress(k.C()): { - Balance: defaultEVMAirdropAmount, - }, - }, nil +func addEwoqAllocation(allocations core.GenesisAlloc) { + allocations[PrefundedEwoqAddress] = core.GenesisAccount{ + Balance: defaultEVMAirdropAmount, + } } func getNativeGasTokenAllocationConfig( + allocations core.GenesisAlloc, app *application.Avalanche, subnetName string, tokenSymbol string, -) (core.GenesisAlloc, error) { +) error { // Get the type of initial token allocation from the user prompt. allocOption, err := app.Prompt.CaptureList( "How should the initial token allocation be structured?", []string{allocateToNewKeyOption, allocateToEwoqOption, customAllocationOption}, ) if err != nil { - return core.GenesisAlloc{}, err + return err } // If the user chooses to allocate to a new key, generate a new key and allocate the default amount to it. if allocOption == allocateToNewKeyOption { - return createNewKeyAllocation(app, subnetName) + return addNewKeyAllocation(allocations, app, subnetName) } if allocOption == allocateToEwoqOption { ux.Logger.PrintToUser("prefunding address %s with balance %s", PrefundedEwoqAddress, defaultEVMAirdropAmount) - return DefaultEwoqAllocation, nil + addEwoqAllocation(allocations) + return nil } if allocOption == customAllocationOption { - res := core.GenesisAlloc{} + if len(allocations) != 0 { + fmt.Println() + fmt.Printf(logging.Bold.Wrap("Addresses automatically allocated\n")) + displayAllocations(allocations) + } for { // Prompt for the action the user wants to take on the allocation list. action, err := app.Prompt.CaptureList( @@ -379,108 +394,119 @@ func getNativeGasTokenAllocationConfig( }, ) if err != nil { - return core.GenesisAlloc{}, err + return err } switch action { case addAddressAllocationOption: address, err := app.Prompt.CaptureAddress("Address to allocate to") if err != nil { - return core.GenesisAlloc{}, err + return err } // Check if the address already has an allocation entry. - if _, ok := res[address]; ok { + if _, ok := allocations[address]; ok { ux.Logger.PrintToUser("Address already has an allocation entry. Use edit or remove to modify.") continue } balance, err := app.Prompt.CaptureUint64(fmt.Sprintf("Amount to allocate (in %s units)", tokenSymbol)) if err != nil { - return core.GenesisAlloc{}, err + return err } - res[address] = core.GenesisAccount{ + allocations[address] = core.GenesisAccount{ Balance: new(big.Int).Mul(new(big.Int).SetUint64(balance), OneAvax), } case changeAddressAllocationOption: address, err := app.Prompt.CaptureAddress("Address to update the allocation of") if err != nil { - return core.GenesisAlloc{}, err + return err } // Check the address has an existing allocation entry. - if _, ok := res[address]; !ok { + if _, ok := allocations[address]; !ok { ux.Logger.PrintToUser("Address not found in the allocation list") continue } balance, err := app.Prompt.CaptureUint64(fmt.Sprintf("Updated amount to allocate (in %s units)", tokenSymbol)) if err != nil { - return core.GenesisAlloc{}, err + return err } - res[address] = core.GenesisAccount{ + allocations[address] = core.GenesisAccount{ Balance: new(big.Int).Mul(new(big.Int).SetUint64(balance), OneAvax), } case removeAddressAllocationOption: address, err := app.Prompt.CaptureAddress("Address to remove from the allocation list") if err != nil { - return core.GenesisAlloc{}, err + return err } // Check the address has an existing allocation entry. - if _, ok := res[address]; !ok { + if _, ok := allocations[address]; !ok { ux.Logger.PrintToUser("Address not found in the allocation list") continue } - delete(res, address) + delete(allocations, address) case previewAddressAllocationOption: - displayAllocations(res) + displayAllocations(allocations) case confirmAddressAllocationOption: - displayAllocations(res) + displayAllocations(allocations) confirm, err := app.Prompt.CaptureYesNo("Are you sure you want to finalize this allocation list?") if err != nil { - return core.GenesisAlloc{}, err + return err } if confirm { - return res, nil + return nil } default: - return core.GenesisAlloc{}, fmt.Errorf("invalid allocation modification option") + return fmt.Errorf("invalid allocation modification option") } } } - return core.GenesisAlloc{}, fmt.Errorf("invalid allocation option") + return fmt.Errorf("invalid allocation option") } -func getNativeMinterPrecompileConfig(app *application.Avalanche, version string) (AllowList, bool, error) { - option, err := app.Prompt.CaptureList( - "Allow minting of new native tokens?", - []string{fixedSupplyOption, dynamicSupplyOption}, - ) - if err != nil { - return AllowList{}, false, err - } +func getNativeMinterPrecompileConfig( + app *application.Avalanche, + alreadyEnabled bool, + allowList AllowList, + version string, +) (AllowList, bool, error) { + if !alreadyEnabled { + option, err := app.Prompt.CaptureList( + "Allow minting of new native tokens?", + []string{fixedSupplyOption, dynamicSupplyOption}, + ) + if err != nil { + return AllowList{}, false, err + } + if option == fixedSupplyOption { + return AllowList{}, false, nil + } + } else { + confirm, err := app.Prompt.CaptureYesNo("Minting of native tokens automatically enabled. Do you want to configure allow list?") + if err != nil { + return AllowList{}, false, err + } + if !confirm { + return AllowList{}, false, nil + } - if option == fixedSupplyOption { - return AllowList{}, false, nil } - if option == dynamicSupplyOption { - for { - allowList, cancel, err := GenerateAllowList(app, "mint native tokens", version) - if err != nil { - return AllowList{}, false, err - } - if cancel { - continue - } - return allowList, true, nil + for { + allowList, cancel, err := GenerateAllowList(app, allowList, "mint native tokens", version) + if err != nil { + return AllowList{}, false, err } + if cancel { + continue + } + return allowList, true, nil } - - return AllowList{}, false, fmt.Errorf("invalid option") } // prompts for token symbol, initial token allocation, and native minter precompile @@ -505,27 +531,30 @@ func promptNativeGasToken( } if defaultsKind == TestDefaults { - params.initialTokenAllocation, err = createNewKeyAllocation(app, blockchainName) + err = addNewKeyAllocation(params.initialTokenAllocation, app, blockchainName) return params, tokenSymbol, err } if defaultsKind == ProductionDefaults { - params.initialTokenAllocation = DefaultEwoqAllocation + addEwoqAllocation(params.initialTokenAllocation) return params, tokenSymbol, nil } // No defaults case. Prompt for initial token allocation and native minter precompile options. - alloc, err := getNativeGasTokenAllocationConfig(app, blockchainName, tokenSymbol) - if err != nil { + if err := getNativeGasTokenAllocationConfig(params.initialTokenAllocation, app, blockchainName, tokenSymbol); err != nil { return SubnetEVMGenesisParams{}, "", err } - allowList, nativeMinterEnabled, err := getNativeMinterPrecompileConfig(app, version) + allowList, nativeMinterEnabled, err := getNativeMinterPrecompileConfig( + app, + params.enableNativeMinterPrecompile, + params.nativeMinterPrecompileAllowList, + version, + ) if err != nil { return SubnetEVMGenesisParams{}, "", err } - params.initialTokenAllocation = alloc params.enableNativeMinterPrecompile = nativeMinterEnabled params.nativeMinterPrecompileAllowList = allowList return params, tokenSymbol, nil @@ -659,7 +688,7 @@ func promptFeeConfig( switch option { case dontChangeFeeSettingsOption: case changeFeeSettingsOption: - params.feeManagerPrecompileAllowList, cancel, err = GenerateAllowList(app, "adjust the gas fees", version) + params.feeManagerPrecompileAllowList, cancel, err = GenerateAllowList(app, AllowList{}, "adjust the gas fees", version) if err != nil { return SubnetEVMGenesisParams{}, err } @@ -687,7 +716,7 @@ func promptFeeConfig( switch option { case burnFees: case distributeFees: - params.rewardManagerPrecompileAllowList, cancel, err = GenerateAllowList(app, "customize gas fees distribution", version) + params.rewardManagerPrecompileAllowList, cancel, err = GenerateAllowList(app, AllowList{}, "customize gas fees distribution", version) if err != nil { return SubnetEVMGenesisParams{}, err } @@ -782,7 +811,7 @@ func promptPermissioning( } switch option { case approvedCanSubmitTransactionsOption: - params.transactionPrecompileAllowList, cancel, err = GenerateAllowList(app, "issue transactions", version) + params.transactionPrecompileAllowList, cancel, err = GenerateAllowList(app, AllowList{}, "issue transactions", version) if err != nil { return SubnetEVMGenesisParams{}, err } @@ -811,7 +840,7 @@ func promptPermissioning( } switch option { case approvedCanDeployContractsOption: - params.contractDeployerPrecompileAllowList, cancel, err = GenerateAllowList(app, "deploy smart contracts", version) + params.contractDeployerPrecompileAllowList, cancel, err = GenerateAllowList(app, AllowList{}, "deploy smart contracts", version) if err != nil { return SubnetEVMGenesisParams{}, err } diff --git a/pkg/vm/evm_settings.go b/pkg/vm/evm_settings.go index f48bb0b28..b16422a9f 100644 --- a/pkg/vm/evm_settings.go +++ b/pkg/vm/evm_settings.go @@ -25,4 +25,5 @@ var ( OneAvax = new(big.Int).SetUint64(1000000000000000000) defaultEVMAirdropAmount = new(big.Int).Exp(big.NewInt(10), big.NewInt(24), nil) // 10^24 + defaultPoAOwnerBalance = new(big.Int).Mul(OneAvax, big.NewInt(10)) // 10 Native Tokens ) From 3f0eca7d49e125cb3e76f35b6db267b2069daf03 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 20 Sep 2024 12:36:01 -0400 Subject: [PATCH 51/74] lint --- cmd/blockchaincmd/export_test.go | 5 ++++- pkg/vm/evm_prompts.go | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/blockchaincmd/export_test.go b/cmd/blockchaincmd/export_test.go index c83e0552e..75c00e01c 100644 --- a/cmd/blockchaincmd/export_test.go +++ b/cmd/blockchaincmd/export_test.go @@ -12,6 +12,7 @@ import ( "github.com/ava-labs/avalanche-cli/internal/mocks" "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/avalanche-cli/pkg/vm" @@ -40,7 +41,9 @@ func TestExportImportSubnet(t *testing.T) { "../../"+utils.SubnetEvmGenesisPath, ) require.NoError(err) - sc, err := vm.CreateEvmSidecar( + sc := &models.Sidecar{} + err = vm.FillEvmSidecar( + sc, app, testSubnet, vmVersion, diff --git a/pkg/vm/evm_prompts.go b/pkg/vm/evm_prompts.go index d3322b0d1..acca57f6b 100644 --- a/pkg/vm/evm_prompts.go +++ b/pkg/vm/evm_prompts.go @@ -378,7 +378,7 @@ func getNativeGasTokenAllocationConfig( if allocOption == customAllocationOption { if len(allocations) != 0 { fmt.Println() - fmt.Printf(logging.Bold.Wrap("Addresses automatically allocated\n")) + fmt.Println(logging.Bold.Wrap("Addresses automatically allocated")) displayAllocations(allocations) } for { @@ -494,7 +494,6 @@ func getNativeMinterPrecompileConfig( if !confirm { return AllowList{}, false, nil } - } for { From 2a8c14d3f0d97a8b18273293404108dfd8b6c265 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Fri, 20 Sep 2024 12:52:52 -0400 Subject: [PATCH 52/74] 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 53/74] 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 64580c982054b6508a59cc8a006e85a80679cb4a Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Sat, 21 Sep 2024 08:54:07 -0400 Subject: [PATCH 54/74] fix e2e --- tests/e2e/commands/subnet.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/e2e/commands/subnet.go b/tests/e2e/commands/subnet.go index f0e1f7298..8f7798de4 100644 --- a/tests/e2e/commands/subnet.go +++ b/tests/e2e/commands/subnet.go @@ -16,7 +16,10 @@ import ( "github.com/onsi/gomega" ) -const subnetEVMMainnetChainID = 11 +const ( + subnetEVMMainnetChainID = 11 + poaValidatorManagerOwner = "0x2e6FcBb9d4E17eC4cF67eddfa7D32eabC4cdCFc6" +) /* #nosec G204 */ func CreateSubnetEvmConfig(subnetName string, genesisPath string) (string, string) { @@ -44,6 +47,8 @@ func CreateSubnetEvmConfigWithVersion(subnetName string, genesisPath string, ver "--evm", subnetName, "--proof-of-authority", + "--poa-manager-owner", + poaValidatorManagerOwner, "--" + constants.SkipUpdateFlag, "--teleporter=false", "--evm-token", @@ -124,6 +129,8 @@ func CreateCustomVMConfig(subnetName string, genesisPath string, vmPath string) "--genesis", genesisPath, "--proof-of-authority", + "--poa-manager-owner", + poaValidatorManagerOwner, "--custom", subnetName, "--custom-vm-path", From b6f65ca7151f6db7e08f5e7c7ddf3c98dcab6fb7 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 23 Sep 2024 19:34:25 -0400 Subject: [PATCH 55/74] fix merge --- cmd/blockchaincmd/deploy.go | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index 6e68e206e..17f599ec0 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -307,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) @@ -419,7 +415,7 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { deployInfo.BlockchainID, deployInfo.ICMMessengerAddress, deployInfo.ICMRegistryAddress, - bootstrapValidators + bootstrapValidators, ); err != nil { return err } @@ -522,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) From 3962f8196b0f181983e029e00d6cf4ceaf69e37b Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 23 Sep 2024 19:41:46 -0400 Subject: [PATCH 56/74] fix merge --- cmd/blockchaincmd/deploy.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index 9d6e7b8de..f512498f1 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -11,7 +11,6 @@ import ( "strings" "github.com/ava-labs/avalanche-cli/pkg/utils" - "github.com/ethereum/go-ethereum/common" "github.com/ava-labs/avalanche-cli/pkg/binutils" "github.com/ava-labs/avalanche-cli/pkg/cobrautils" @@ -585,7 +584,6 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { return app.UpdateSidecarNetworks(&sidecar, network, subnetID, blockchainID, "", "", bootstrapValidators) } - 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... From 5438ec77814f156e83d51fa2aecbec684ce96772 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Tue, 24 Sep 2024 12:06:00 -0400 Subject: [PATCH 57/74] convert Subnet Tx --- cmd/blockchaincmd/deploy.go | 126 ++++++++++++++++++++++++++++++++++++ pkg/subnet/public.go | 76 ++++++++++++++++++++++ 2 files changed, 202 insertions(+) diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index f512498f1..fa4d798d1 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -6,6 +6,10 @@ import ( "encoding/json" "errors" "fmt" + "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" "path/filepath" "strings" @@ -116,6 +120,25 @@ so you can take your locally tested Subnet and deploy it on Fuji or Mainnet.`, return cmd } +type SubnetValidator struct { + // Must be Ed25519 NodeID + NodeID ids.NodeID `json:"nodeID"` + // Weight of this validator used when sampling + Weight uint64 `json:"weight"` + // When this validator will stop validating the Subnet + EndTime uint64 `json:"endTime"` + // Initial balance for this validator + Balance uint64 `json:"balance"` + // [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 `json:"signer"` + // Leftover $AVAX from the [Balance] will be issued to this + // owner once it is removed from the validator set. + ChangeOwner fx.Owner `json:"changeOwner"` +} + func CallDeploy( cmd *cobra.Command, subnetOnlyParam bool, @@ -575,6 +598,58 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { } } + //type ConvertSubnetTx struct { + // // Metadata, inputs and outputs + // BaseTx + // // ID of the Subnet to transform + // // Restrictions: + // // - Must not be the Primary Network ID + // Subnet ids.ID `json:"subnetID"` + // // BlockchainID where the Subnet manager lives + // ChainID ids.ID `json:"chainID"` + // // Address of the Subnet manager + // Address []byte `json:"address"` + // // Initial pay-as-you-go validators for the Subnet + // Validators []SubnetValidator `json:"validators"` + // // Authorizes this conversion + // SubnetAuth verify.Verifiable `json:"subnetAuthorization"` + // } + + //avaGoBootstrapValidators, err := convertToAvalancheGoSubnetValidator(bootstrapValidators) + //if err != nil { + // return err + //} + // TODO: replace with avalanchego subnetValidators once implemented + isFullySigned, convertSubnetTxID, tx, remainingSubnetAuthKeys, err := deployer.ConvertSubnet( + controlKeys, + subnetAuthKeys, + subnetID, + blockchainID, + //avaGoBootstrapValidators, + ) + if err != nil { + ux.Logger.PrintToUser(logging.Red.Wrap( + fmt.Sprintf("error converting blockchain: %s. fix the issue and try again with a new convert cmd", err), + )) + } + + savePartialTx = !isFullySigned && err == nil + ux.Logger.PrintToUser("ConvertSubnetTx ID: %s", convertSubnetTxID) + + if savePartialTx { + if err := SaveNotFullySignedTx( + "ConvertSubnetTx", + tx, + chain, + subnetAuthKeys, + remainingSubnetAuthKeys, + outputTxPath, + false, + ); err != nil { + return err + } + } + flags := make(map[string]string) flags[constants.MetricsNetwork] = network.Name() metrics.HandleTracking(cmd, constants.MetricsSubnetDeployCommand, app, flags) @@ -584,6 +659,57 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { return app.UpdateSidecarNetworks(&sidecar, network, subnetID, blockchainID, "", "", bootstrapValidators) } +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 convertToAvalancheGoSubnetValidator(subnetValidators []models.SubnetValidator) ([]SubnetValidator, error) { + bootstrapValidators := []SubnetValidator{} + for _, validator := range subnetValidators { + nodeID, err := ids.NodeIDFromString(validator.NodeID) + if err != nil { + return nil, err + } + blsInfo, err := getBLSInfo(validator.BLSPublicKey, validator.BLSProofOfPossession) + if err != nil { + return nil, fmt.Errorf("failure parsing BLS info: %w", err) + } + addrs, err := address.ParseToIDs([]string{validator.ChangeOwnerAddr}) + if err != nil { + return nil, fmt.Errorf("failure parsing change owner address: %w", err) + } + bootstrapValidator := SubnetValidator{ + NodeID: nodeID, + Weight: validator.Weight, + Balance: validator.Balance, + Signer: blsInfo, + ChangeOwner: &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: addrs, + }, + } + bootstrapValidators = append(bootstrapValidators, bootstrapValidator) + } + return bootstrapValidators, nil +} + 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... diff --git a/pkg/subnet/public.go b/pkg/subnet/public.go index e82de963e..e62cd2336 100644 --- a/pkg/subnet/public.go +++ b/pkg/subnet/public.go @@ -360,6 +360,53 @@ func (d *PublicDeployer) DeployBlockchain( return isFullySigned, id, tx, remainingSubnetAuthKeys, nil } +// TODO: update ConvertSubnet once avalanchego implementation is up for ACP77 +func (d *PublicDeployer) ConvertSubnet( + controlKeys []string, + subnetAuthKeysStrs []string, + subnetID ids.ID, + chainID ids.ID, + // validators []blockchaincmd.SubnetValidator, +) (bool, ids.ID, *txs.Tx, []string, error) { + ux.Logger.PrintToUser("Now calling ConvertSubnet Tx...") + + wallet, err := d.loadCacheWallet(subnetID) + if err != nil { + return false, ids.Empty, nil, nil, err + } + + subnetAuthKeys, err := address.ParseToIDs(subnetAuthKeysStrs) + if err != nil { + return false, ids.Empty, nil, nil, fmt.Errorf("failure parsing subnet auth keys: %w", err) + } + + showLedgerSignatureMsg(d.kc.UsesLedger, d.kc.HasOnlyOneKey(), "ConvertSubnet transaction") + + var validatorManagerAddress []byte + //var validators []avalanchego.SubnetValidator + + tx, err := d.createConvertSubnetTx(subnetAuthKeys, subnetID, chainID, validatorManagerAddress, wallet) + if err != nil { + return false, ids.Empty, nil, nil, err + } + + _, remainingSubnetAuthKeys, err := txutils.GetRemainingSigners(tx, controlKeys) + if err != nil { + return false, ids.Empty, nil, nil, err + } + isFullySigned := len(remainingSubnetAuthKeys) == 0 + + id := ids.Empty + if isFullySigned { + id, err = d.Commit(tx, true) + if err != nil { + return false, ids.Empty, nil, nil, err + } + } + + return isFullySigned, id, tx, remainingSubnetAuthKeys, nil +} + func (d *PublicDeployer) Commit( tx *txs.Tx, waitForTxAcceptance bool, @@ -507,6 +554,35 @@ func (d *PublicDeployer) createBlockchainTx( return &tx, nil } +func (d *PublicDeployer) createConvertSubnetTx( + subnetAuthKeys []ids.ShortID, + subnetID ids.ID, + chainID ids.ID, + address []byte, + //validators []avalanchego.SubnetValidator, + wallet primary.Wallet, +) (*txs.Tx, error) { + //fxIDs := make([]ids.ID, 0) + //options := d.getMultisigTxOptions(subnetAuthKeys) + //unsignedTx, err := wallet.P().Builder().NewConvertSubnetTx( + // subnetID, + //chainID, + //address, + //validators, + // fxIDs, + // options..., + //) + //if err != nil { + // return nil, fmt.Errorf("error building tx: %w", err) + //} + //tx := txs.Tx{Unsigned: unsignedTx} + //if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { + // return nil, fmt.Errorf("error signing tx: %w", err) + //} + //return &tx, nil + return nil, nil +} + func (d *PublicDeployer) createTransferSubnetOwnershipTx( subnetAuthKeys []ids.ShortID, subnetID ids.ID, From ca2af0d8487f206dfc05c1d3821d0a7c022a0f81 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Tue, 24 Sep 2024 14:47:32 -0400 Subject: [PATCH 58/74] convert subnet --- cmd/blockchaincmd/convert.go | 878 +++++++++++++++++++++++++++++++++++ cmd/blockchaincmd/deploy.go | 11 +- 2 files changed, 884 insertions(+), 5 deletions(-) create mode 100644 cmd/blockchaincmd/convert.go diff --git a/cmd/blockchaincmd/convert.go b/cmd/blockchaincmd/convert.go new file mode 100644 index 000000000..d4fc0b4fe --- /dev/null +++ b/cmd/blockchaincmd/convert.go @@ -0,0 +1,878 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package blockchaincmd + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "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" + + "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" + "github.com/ava-labs/avalanche-cli/pkg/keychain" + "github.com/ava-labs/avalanche-cli/pkg/localnet" + "github.com/ava-labs/avalanche-cli/pkg/metrics" + "github.com/ava-labs/avalanche-cli/pkg/models" + "github.com/ava-labs/avalanche-cli/pkg/networkoptions" + "github.com/ava-labs/avalanche-cli/pkg/prompts" + "github.com/ava-labs/avalanche-cli/pkg/subnet" + "github.com/ava-labs/avalanche-cli/pkg/txutils" + "github.com/ava-labs/avalanche-cli/pkg/ux" + "github.com/ava-labs/avalanche-cli/pkg/vm" + anrutils "github.com/ava-labs/avalanche-network-runner/utils" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/olekukonko/tablewriter" + "github.com/spf13/cobra" + "go.uber.org/zap" + "golang.org/x/mod/semver" +) + +// avalanche blockchain convert +func newConvertCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "convert [blockchainName]", + Short: "Converts an Avalanche blockchain into a SOV (Subnet Only Validator) blockchain", + Long: `The blockchain convert command converts a non-SOV Avalanche blockchain (which requires +subnet validators to have at least 2000 AVAX staked in the Primary Network) into a SOV (Subnet Only +Validator) blockchain. + +At the end of the call, the Owner Keys . + +Avalanche-CLI only supports deploying an individual Blockchain once per network. Subsequent +attempts to deploy the same Blockchain to the same network (local, Fuji, Mainnet) aren't +allowed. If you'd like to redeploy a Blockchain locally for testing, you must first call +avalanche network clean to reset all deployed chain state. Subsequent local deploys +redeploy the chain with fresh state. You can deploy the same Blockchain to multiple networks, +so you can take your locally tested Subnet and deploy it on Fuji or Mainnet.`, + RunE: deployBlockchain, + PersistentPostRun: handlePostRun, + Args: cobrautils.ExactArgs(1), + } + networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, true, deploySupportedNetworkOptions) + cmd.Flags().StringVar(&userProvidedAvagoVersion, "avalanchego-version", "latest", "use this version of avalanchego (ex: v1.17.12)") + cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [fuji/devnet deploy only]") + cmd.Flags().BoolVarP(&sameControlKey, "same-control-key", "s", false, "use the fee-paying key as control key") + cmd.Flags().Uint32Var(&threshold, "threshold", 0, "required number of control key signatures to make subnet changes") + cmd.Flags().StringSliceVar(&controlKeys, "control-keys", nil, "addresses that may make subnet changes") + cmd.Flags().StringSliceVar(&subnetAuthKeys, "subnet-auth-keys", nil, "control keys that will be used to authenticate chain creation") + cmd.Flags().StringVar(&outputTxPath, "output-tx-path", "", "file path of the blockchain creation tx") + cmd.Flags().BoolVarP(&useEwoq, "ewoq", "e", false, "use ewoq key [fuji/devnet deploy only]") + cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji/devnet)") + cmd.Flags().StringSliceVar(&ledgerAddresses, "ledger-addrs", []string{}, "use the given ledger addresses") + cmd.Flags().StringVarP(&subnetIDStr, "subnet-id", "u", "", "do not create a subnet, deploy the blockchain into the given subnet id") + cmd.Flags().Uint32Var(&mainnetChainID, "mainnet-chain-id", 0, "use different ChainID for mainnet deployment") + cmd.Flags().StringVar(&avagoBinaryPath, "avalanchego-path", "", "use this avalanchego binary path") + cmd.Flags().BoolVar(&subnetOnly, "subnet-only", false, "only create a subnet") + cmd.Flags().BoolVar(&icmSpec.SkipICMDeploy, "skip-local-teleporter", false, "skip automatic teleporter deploy on local networks [to be deprecated]") + cmd.Flags().BoolVar(&icmSpec.SkipICMDeploy, "skip-teleporter-deploy", false, "skip automatic teleporter deploy") + cmd.Flags().BoolVar(&icmSpec.SkipRelayerDeploy, "skip-relayer", false, "skip relayer deploy") + cmd.Flags().StringVar(&icmSpec.ICMVersion, "teleporter-version", "latest", "teleporter version to deploy") + cmd.Flags().StringVar(&icmSpec.RelayerVersion, "relayer-version", "latest", "relayer version to deploy") + cmd.Flags().StringVar(&icmSpec.MessengerContractAddressPath, "teleporter-messenger-contract-address-path", "", "path to an interchain messenger contract address file") + 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(&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 +} + +type SubnetValidator struct { + // Must be Ed25519 NodeID + NodeID ids.NodeID `json:"nodeID"` + // Weight of this validator used when sampling + Weight uint64 `json:"weight"` + // When this validator will stop validating the Subnet + EndTime uint64 `json:"endTime"` + // Initial balance for this validator + Balance uint64 `json:"balance"` + // [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 `json:"signer"` + // Leftover $AVAX from the [Balance] will be issued to this + // owner once it is removed from the validator set. + ChangeOwner fx.Owner `json:"changeOwner"` +} + +func CallDeploy( + cmd *cobra.Command, + subnetOnlyParam bool, + blockchainName string, + networkFlags networkoptions.NetworkFlags, + keyNameParam string, + useLedgerParam bool, + useEwoqParam bool, + sameControlKeyParam bool, +) error { + subnetOnly = subnetOnlyParam + globalNetworkFlags = networkFlags + sameControlKey = sameControlKeyParam + keyName = keyNameParam + useLedger = useLedgerParam + useEwoq = useEwoqParam + return deployBlockchain(cmd, []string{blockchainName}) +} + +func getChainsInSubnet(blockchainName string) ([]string, error) { + subnets, err := os.ReadDir(app.GetSubnetDir()) + if err != nil { + return nil, fmt.Errorf("failed to read baseDir: %w", err) + } + + chains := []string{} + + for _, s := range subnets { + if !s.IsDir() { + continue + } + sidecarFile := filepath.Join(app.GetSubnetDir(), s.Name(), constants.SidecarFileName) + if _, err := os.Stat(sidecarFile); err == nil { + // read in sidecar file + jsonBytes, err := os.ReadFile(sidecarFile) + if err != nil { + return nil, fmt.Errorf("failed reading file %s: %w", sidecarFile, err) + } + + var sc models.Sidecar + err = json.Unmarshal(jsonBytes, &sc) + if err != nil { + return nil, fmt.Errorf("failed unmarshaling file %s: %w", sidecarFile, err) + } + if sc.Subnet == blockchainName { + chains = append(chains, sc.Name) + } + } + } + return chains, nil +} + +func checkSubnetEVMDefaultAddressNotInAlloc(network models.Network, chain string) error { + if network.Kind != models.Local && network.Kind != models.Devnet && os.Getenv(constants.SimulatePublicNetwork) == "" { + genesis, err := app.LoadEvmGenesis(chain) + if err != nil { + return err + } + allocAddressMap := genesis.Alloc + for address := range allocAddressMap { + if address.String() == vm.PrefundedEwoqAddress.String() { + return fmt.Errorf("can't airdrop to default address on public networks, please edit the genesis by calling `avalanche subnet create %s --force`", chain) + } + } + } + return nil +} + +func runDeploy(cmd *cobra.Command, args []string, supportedNetworkOptions []networkoptions.NetworkOption) error { + skipCreatePrompt = true + deploySupportedNetworkOptions = supportedNetworkOptions + return deployBlockchain(cmd, args) +} + +func updateSubnetEVMGenesisChainID(genesisBytes []byte, newChainID uint) ([]byte, error) { + var genesisMap map[string]interface{} + if err := json.Unmarshal(genesisBytes, &genesisMap); err != nil { + return nil, err + } + configI, ok := genesisMap["config"] + if !ok { + return nil, fmt.Errorf("config field not found on genesis") + } + config, ok := configI.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("expected genesis config field to be a map[string]interface, found %T", configI) + } + config["chainId"] = float64(newChainID) + return json.MarshalIndent(genesisMap, "", " ") +} + +// updates sidecar with genesis mainnet id to use +// given either by cmdline flag, original genesis id, or id obtained from the user +func getSubnetEVMMainnetChainID(sc *models.Sidecar, blockchainName string) error { + // get original chain id + evmGenesis, err := app.LoadEvmGenesis(blockchainName) + if err != nil { + return err + } + if evmGenesis.Config == nil { + return fmt.Errorf("invalid subnet evm genesis format: config is nil") + } + if evmGenesis.Config.ChainID == nil { + return fmt.Errorf("invalid subnet evm genesis format: config chain id is nil") + } + originalChainID := evmGenesis.Config.ChainID.Uint64() + // handle cmdline flag if given + if mainnetChainID != 0 { + sc.SubnetEVMMainnetChainID = uint(mainnetChainID) + } + // prompt the user + if sc.SubnetEVMMainnetChainID == 0 { + useSameChainID := "Use same ChainID" + useNewChainID := "Use new ChainID" + listOptions := []string{useNewChainID, useSameChainID} + newChainIDPrompt := "Using the same ChainID for both Fuji and Mainnet could lead to a replay attack. Do you want to use a different ChainID?" + var ( + err error + decision string + ) + decision, err = app.Prompt.CaptureList(newChainIDPrompt, listOptions) + if err != nil { + return err + } + if decision == useSameChainID { + sc.SubnetEVMMainnetChainID = uint(originalChainID) + } else { + ux.Logger.PrintToUser("Enter your subnet's ChainID. It can be any positive integer != %d.", originalChainID) + newChainID, err := app.Prompt.CapturePositiveInt( + "ChainID", + []prompts.Comparator{ + { + Label: "Zero", + Type: prompts.MoreThan, + Value: 0, + }, + { + Label: "Original Chain ID", + Type: prompts.NotEq, + Value: originalChainID, + }, + }, + ) + if err != nil { + return err + } + sc.SubnetEVMMainnetChainID = uint(newChainID) + } + } + return app.UpdateSidecar(sc) +} + +// deployBlockchain is the cobra command run for deploying subnets +func deployBlockchain(cmd *cobra.Command, args []string) error { + blockchainName := args[0] + + if err := CreateBlockchainFirst(cmd, blockchainName, skipCreatePrompt); err != nil { + return err + } + + chains, err := ValidateSubnetNameAndGetChains(args) + if err != nil { + return err + } + + if icmSpec.MessengerContractAddressPath != "" || icmSpec.MessengerDeployerAddressPath != "" || icmSpec.MessengerDeployerTxPath != "" || icmSpec.RegistryBydecodePath != "" { + if icmSpec.MessengerContractAddressPath == "" || icmSpec.MessengerDeployerAddressPath == "" || icmSpec.MessengerDeployerTxPath == "" || icmSpec.RegistryBydecodePath == "" { + return fmt.Errorf("if setting any teleporter asset path, you must set all teleporter asset paths") + } + } + + var bootstrapValidators []models.SubnetValidator + if bootstrapValidatorsJSONFilePath != "" { + bootstrapValidators, err = LoadBootstrapValidator(bootstrapValidatorsJSONFilePath) + if err != nil { + return err + } + } + + chain := chains[0] + + sidecar, err := app.LoadSidecar(chain) + if err != nil { + return fmt.Errorf("failed to load sidecar for later update: %w", err) + } + + if sidecar.ImportedFromAPM { + return errors.New("unable to deploy subnets imported from a repo") + } + + if outputTxPath != "" { + if _, err := os.Stat(outputTxPath); err == nil { + return fmt.Errorf("outputTxPath %q already exists", outputTxPath) + } + } + + network, err := networkoptions.GetNetworkFromCmdLineFlags( + app, + "", + globalNetworkFlags, + true, + false, + deploySupportedNetworkOptions, + "", + ) + if err != nil { + return err + } + + isEVMGenesis, validationErr, err := app.HasSubnetEVMGenesis(chain) + if err != nil { + return err + } + if sidecar.VM == models.SubnetEvm && !isEVMGenesis { + return fmt.Errorf("failed to validate SubnetEVM genesis format: %w", validationErr) + } + + chainGenesis, err := app.LoadRawGenesis(chain) + if err != nil { + return err + } + + if isEVMGenesis { + // is is a subnet evm or a custom vm based on subnet evm + if network.Kind == models.Mainnet { + err = getSubnetEVMMainnetChainID(&sidecar, chain) + if err != nil { + return err + } + chainGenesis, err = updateSubnetEVMGenesisChainID(chainGenesis, sidecar.SubnetEVMMainnetChainID) + if err != nil { + return err + } + } + err = checkSubnetEVMDefaultAddressNotInAlloc(network, chain) + if err != nil { + return err + } + } + + 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 { + app.Log.Debug("Deploy local") + + genesisPath := app.GetGenesisPath(chain) + + // copy vm binary to the expected location, first downloading it if necessary + var vmBin string + switch sidecar.VM { + case models.SubnetEvm: + _, vmBin, err = binutils.SetupSubnetEVM(app, sidecar.VMVersion) + if err != nil { + return fmt.Errorf("failed to install subnet-evm: %w", err) + } + case models.CustomVM: + vmBin = binutils.SetupCustomBin(app, chain) + default: + return fmt.Errorf("unknown vm: %s", sidecar.VM) + } + + // check if selected version matches what is currently running + nc := localnet.NewStatusChecker() + avagoVersion, err := CheckForInvalidDeployAndGetAvagoVersion(nc, sidecar.RPCVersion) + if err != nil { + return err + } + if avagoBinaryPath == "" { + userProvidedAvagoVersion = avagoVersion + } + + deployer := subnet.NewLocalDeployer(app, userProvidedAvagoVersion, avagoBinaryPath, vmBin) + deployInfo, err := deployer.DeployToLocalNetwork(chain, genesisPath, icmSpec, subnetIDStr) + if err != nil { + if deployer.BackendStartedHere() { + if innerErr := binutils.KillgRPCServerProcess(app); innerErr != nil { + app.Log.Warn("tried to kill the gRPC server process but it failed", zap.Error(innerErr)) + } + } + return err + } + flags := make(map[string]string) + flags[constants.MetricsNetwork] = network.Name() + metrics.HandleTracking(cmd, constants.MetricsSubnetDeployCommand, app, flags) + if err := app.UpdateSidecarNetworks( + &sidecar, + network, + deployInfo.SubnetID, + deployInfo.BlockchainID, + deployInfo.ICMMessengerAddress, + deployInfo.ICMRegistryAddress, + bootstrapValidators, + ); err != nil { + return err + } + return PrintSubnetInfo(blockchainName, true) + } + + // from here on we are assuming a public deploy + if subnetOnly && subnetIDStr != "" { + return errMutuallyExlusiveSubnetFlags + } + + createSubnet := true + var subnetID ids.ID + if subnetIDStr != "" { + subnetID, err = ids.FromString(subnetIDStr) + if err != nil { + return err + } + createSubnet = false + } else if !subnetOnly && sidecar.Networks != nil { + model, ok := sidecar.Networks[network.Name()] + if ok { + if model.SubnetID != ids.Empty && model.BlockchainID == ids.Empty { + subnetID = model.SubnetID + createSubnet = false + } + } + } + + fee := uint64(0) + if !subnetOnly { + fee += network.GenesisParams().TxFeeConfig.StaticFeeConfig.CreateBlockchainTxFee + } + if createSubnet { + fee += network.GenesisParams().TxFeeConfig.StaticFeeConfig.CreateSubnetTxFee + } + + kc, err := keychain.GetKeychainFromCmdLineFlags( + app, + constants.PayTxsFeesMsg, + network, + keyName, + useEwoq, + useLedger, + ledgerAddresses, + fee, + ) + if err != nil { + return err + } + + network.HandlePublicNetworkSimulation() + + if createSubnet { + controlKeys, threshold, err = promptOwners( + kc, + controlKeys, + sameControlKey, + threshold, + subnetAuthKeys, + true, + ) + if err != nil { + return err + } + } else { + ux.Logger.PrintToUser(logging.Blue.Wrap( + fmt.Sprintf("Deploying into pre-existent subnet ID %s", subnetID.String()), + )) + var isPermissioned bool + isPermissioned, controlKeys, threshold, err = txutils.GetOwners(network, subnetID) + if err != nil { + return err + } + if !isPermissioned { + return ErrNotPermissionedSubnet + } + } + + // add control keys to the keychain whenever possible + if err := kc.AddAddresses(controlKeys); err != nil { + return err + } + + kcKeys, err := kc.PChainFormattedStrAddresses() + if err != nil { + return err + } + + // get keys for blockchain tx signing + if subnetAuthKeys != nil { + if err := prompts.CheckSubnetAuthKeys(kcKeys, subnetAuthKeys, controlKeys, threshold); err != nil { + return err + } + } else { + subnetAuthKeys, err = prompts.GetSubnetAuthKeys(app.Prompt, kcKeys, controlKeys, threshold) + if err != nil { + return err + } + } + ux.Logger.PrintToUser("Your subnet auth keys for chain creation: %s", subnetAuthKeys) + + // deploy to public network + deployer := subnet.NewPublicDeployer(app, kc, network) + + if createSubnet { + subnetID, err = deployer.DeploySubnet(controlKeys, threshold) + if err != nil { + return err + } + // get the control keys in the same order as the tx + _, controlKeys, threshold, err = txutils.GetOwners(network, subnetID) + if err != nil { + return err + } + } + + var ( + savePartialTx bool + blockchainID ids.ID + tx *txs.Tx + remainingSubnetAuthKeys []string + isFullySigned bool + ) + + if !subnetOnly { + isFullySigned, blockchainID, tx, remainingSubnetAuthKeys, err = deployer.DeployBlockchain( + controlKeys, + subnetAuthKeys, + subnetID, + chain, + chainGenesis, + ) + if err != nil { + ux.Logger.PrintToUser(logging.Red.Wrap( + fmt.Sprintf("error deploying blockchain: %s. fix the issue and try again with a new deploy cmd", err), + )) + } + + savePartialTx = !isFullySigned && err == nil + } + + if err := PrintDeployResults(chain, subnetID, blockchainID); err != nil { + return err + } + + if savePartialTx { + if err := SaveNotFullySignedTx( + "Blockchain Creation", + tx, + chain, + subnetAuthKeys, + remainingSubnetAuthKeys, + outputTxPath, + false, + ); err != nil { + return err + } + } + + // type ConvertSubnetTx struct { + // // Metadata, inputs and outputs + // BaseTx + // // ID of the Subnet to transform + // // Restrictions: + // // - Must not be the Primary Network ID + // Subnet ids.ID `json:"subnetID"` + // // BlockchainID where the Subnet manager lives + // ChainID ids.ID `json:"chainID"` + // // Address of the Subnet manager + // Address []byte `json:"address"` + // // Initial pay-as-you-go validators for the Subnet + // Validators []SubnetValidator `json:"validators"` + // // Authorizes this conversion + // SubnetAuth verify.Verifiable `json:"subnetAuthorization"` + // } + + //avaGoBootstrapValidators, err := convertToAvalancheGoSubnetValidator(bootstrapValidators) + //if err != nil { + // return err + //} + // TODO: replace with avalanchego subnetValidators once implemented + isFullySigned, convertSubnetTxID, tx, remainingSubnetAuthKeys, err := deployer.ConvertSubnet( + controlKeys, + subnetAuthKeys, + subnetID, + blockchainID, + // avaGoBootstrapValidators, + ) + if err != nil { + ux.Logger.PrintToUser(logging.Red.Wrap( + fmt.Sprintf("error converting blockchain: %s. fix the issue and try again with a new convert cmd", err), + )) + } + + savePartialTx = !isFullySigned && err == nil + ux.Logger.PrintToUser("ConvertSubnetTx ID: %s", convertSubnetTxID) + + if savePartialTx { + if err := SaveNotFullySignedTx( + "ConvertSubnetTx", + tx, + chain, + subnetAuthKeys, + remainingSubnetAuthKeys, + outputTxPath, + false, + ); err != nil { + return err + } + } + + flags := make(map[string]string) + flags[constants.MetricsNetwork] = network.Name() + metrics.HandleTracking(cmd, constants.MetricsSubnetDeployCommand, app, flags) + + // update sidecar + // TODO: need to do something for backwards compatibility? + return app.UpdateSidecarNetworks(&sidecar, network, subnetID, blockchainID, "", "", bootstrapValidators) +} + +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 convertToAvalancheGoSubnetValidator(subnetValidators []models.SubnetValidator) ([]SubnetValidator, error) { + bootstrapValidators := []SubnetValidator{} + for _, validator := range subnetValidators { + nodeID, err := ids.NodeIDFromString(validator.NodeID) + if err != nil { + return nil, err + } + blsInfo, err := getBLSInfo(validator.BLSPublicKey, validator.BLSProofOfPossession) + if err != nil { + return nil, fmt.Errorf("failure parsing BLS info: %w", err) + } + addrs, err := address.ParseToIDs([]string{validator.ChangeOwnerAddr}) + if err != nil { + return nil, fmt.Errorf("failure parsing change owner address: %w", err) + } + bootstrapValidator := SubnetValidator{ + NodeID: nodeID, + Weight: validator.Weight, + Balance: validator.Balance, + Signer: blsInfo, + ChangeOwner: &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: addrs, + }, + } + bootstrapValidators = append(bootstrapValidators, bootstrapValidator) + } + return bootstrapValidators, nil +} + +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... + if err := checkInvalidSubnetNames(args[0]); err != nil { + return nil, fmt.Errorf("subnet name %s is invalid: %w", args[0], err) + } + // Check subnet exists + // TODO create a file that lists chains by subnet for fast querying + chains, err := getChainsInSubnet(args[0]) + if err != nil { + return nil, fmt.Errorf("failed to getChainsInSubnet: %w", err) + } + + if len(chains) == 0 { + return nil, errors.New("Invalid subnet " + args[0]) + } + + return chains, nil +} + +func SaveNotFullySignedTx( + txName string, + tx *txs.Tx, + chain string, + subnetAuthKeys []string, + remainingSubnetAuthKeys []string, + outputTxPath string, + forceOverwrite bool, +) error { + signedCount := len(subnetAuthKeys) - len(remainingSubnetAuthKeys) + ux.Logger.PrintToUser("") + if signedCount == len(subnetAuthKeys) { + ux.Logger.PrintToUser("All %d required %s signatures have been signed. "+ + "Saving tx to disk to enable commit.", len(subnetAuthKeys), txName) + } else { + ux.Logger.PrintToUser("%d of %d required %s signatures have been signed. "+ + "Saving tx to disk to enable remaining signing.", signedCount, len(subnetAuthKeys), txName) + } + if outputTxPath == "" { + ux.Logger.PrintToUser("") + var err error + if forceOverwrite { + outputTxPath, err = app.Prompt.CaptureString("Path to export partially signed tx to") + } else { + outputTxPath, err = app.Prompt.CaptureNewFilepath("Path to export partially signed tx to") + } + if err != nil { + return err + } + } + if forceOverwrite { + ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("Overwriting %s", outputTxPath) + } + if err := txutils.SaveToDisk(tx, outputTxPath, forceOverwrite); err != nil { + return err + } + if signedCount == len(subnetAuthKeys) { + PrintReadyToSignMsg(chain, outputTxPath) + } else { + PrintRemainingToSignMsg(chain, remainingSubnetAuthKeys, outputTxPath) + } + return nil +} + +func PrintReadyToSignMsg( + chain string, + outputTxPath string, +) { + ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("Tx is fully signed, and ready to be committed") + ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("Commit command:") + ux.Logger.PrintToUser(" avalanche transaction commit %s --input-tx-filepath %s", chain, outputTxPath) +} + +func PrintRemainingToSignMsg( + chain string, + remainingSubnetAuthKeys []string, + outputTxPath string, +) { + ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("Addresses remaining to sign the tx") + for _, subnetAuthKey := range remainingSubnetAuthKeys { + ux.Logger.PrintToUser(" %s", subnetAuthKey) + } + ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("Connect a ledger with one of the remaining addresses or choose a stored key "+ + "and run the signing command, or send %q to another user for signing.", outputTxPath) + ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("Signing command:") + ux.Logger.PrintToUser(" avalanche transaction sign %s --input-tx-filepath %s", chain, outputTxPath) + ux.Logger.PrintToUser("") +} + +func PrintDeployResults(chain string, subnetID ids.ID, blockchainID ids.ID) error { + vmID, err := anrutils.VMID(chain) + if err != nil { + return fmt.Errorf("failed to create VM ID from %s: %w", chain, err) + } + header := []string{"Deployment results", ""} + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader(header) + table.SetRowLine(true) + table.SetAutoMergeCells(true) + table.Append([]string{"Chain Name", chain}) + table.Append([]string{"Subnet ID", subnetID.String()}) + table.Append([]string{"VM ID", vmID.String()}) + if blockchainID != ids.Empty { + table.Append([]string{"Blockchain ID", blockchainID.String()}) + table.Append([]string{"P-Chain TXID", blockchainID.String()}) + } + table.Render() + return nil +} + +// Determines the appropriate version of avalanchego to run with. Returns an error if +// that version conflicts with the current deployment. +func CheckForInvalidDeployAndGetAvagoVersion( + statusChecker localnet.StatusChecker, + configuredRPCVersion int, +) (string, error) { + // get current network + runningAvagoVersion, runningRPCVersion, networkRunning, err := statusChecker.GetCurrentNetworkVersion() + if err != nil { + return "", err + } + desiredAvagoVersion := userProvidedAvagoVersion + + // RPC Version was made available in the info API in avalanchego version v1.9.2. For prior versions, + // we will need to skip this check. + skipRPCCheck := false + if semver.Compare(runningAvagoVersion, constants.AvalancheGoCompatibilityVersionAdded) == -1 { + skipRPCCheck = true + } + + if networkRunning { + if userProvidedAvagoVersion == "latest" { + if runningRPCVersion != configuredRPCVersion && !skipRPCCheck { + return "", fmt.Errorf( + "the current avalanchego deployment uses rpc version %d but your subnet has version %d and is not compatible", + runningRPCVersion, + configuredRPCVersion, + ) + } + desiredAvagoVersion = runningAvagoVersion + } else if runningAvagoVersion != strings.Split(userProvidedAvagoVersion, "-")[0] { + // user wants a specific version + return "", errors.New("incompatible avalanchego version selected") + } + } else if userProvidedAvagoVersion == "latest" { + // find latest avago version for this rpc version + desiredAvagoVersion, err = vm.GetLatestAvalancheGoByProtocolVersion( + app, configuredRPCVersion, constants.AvalancheGoCompatibilityURL) + if err == vm.ErrNoAvagoVersion { + latestPreReleaseVersion, err := app.Downloader.GetLatestPreReleaseVersion( + constants.AvaLabsOrg, + constants.AvalancheGoRepoName, + ) + if err != nil { + return "", err + } + return latestPreReleaseVersion, nil + } + if err != nil { + return "", err + } + } + 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 { + subnetValidator.NodeID, subnetValidator.BLSPublicKey, subnetValidator.BLSProofOfPossession, err = generateNewNodeAndBLS() + if err != nil { + return nil, err + } + } + } + return subnetValidators, nil +} diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index fa4d798d1..1a1b7ae67 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -6,13 +6,14 @@ import ( "encoding/json" "errors" "fmt" + "os" + "path/filepath" + "strings" + "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" - "path/filepath" - "strings" "github.com/ava-labs/avalanche-cli/pkg/utils" @@ -598,7 +599,7 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { } } - //type ConvertSubnetTx struct { + // type ConvertSubnetTx struct { // // Metadata, inputs and outputs // BaseTx // // ID of the Subnet to transform @@ -625,7 +626,7 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { subnetAuthKeys, subnetID, blockchainID, - //avaGoBootstrapValidators, + // avaGoBootstrapValidators, ) if err != nil { ux.Logger.PrintToUser(logging.Red.Wrap( From 00df2a816c4e82a57df32fd620cc0d67a2bdcf12 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 25 Sep 2024 11:23:24 -0400 Subject: [PATCH 59/74] register validator --- cmd/blockchaincmd/add_validator.go | 162 ++- cmd/blockchaincmd/convert.go | 1176 +++++++-------------- cmd/blockchaincmd/deploy.go | 1 + cmd/blockchaincmd/prompt_genesis_input.go | 26 +- cmd/nodecmd/validate_subnet.go | 2 +- pkg/prompts/prompts.go | 6 +- pkg/prompts/validations.go | 4 +- pkg/subnet/public.go | 25 +- 8 files changed, 558 insertions(+), 844 deletions(-) diff --git a/cmd/blockchaincmd/add_validator.go b/cmd/blockchaincmd/add_validator.go index e6ae29068..f8f35cd88 100644 --- a/cmd/blockchaincmd/add_validator.go +++ b/cmd/blockchaincmd/add_validator.go @@ -40,6 +40,8 @@ var ( useDefaultDuration bool useDefaultWeight bool waitForTxAcceptance bool + publicKey string + pop string errNoSubnetID = errors.New("failed to find the subnet ID for this subnet, has it been deployed/created on this network?") errMutuallyExclusiveDurationOptions = errors.New("--use-default-duration/--use-default-validator-params and --staking-period are mutually exclusive") @@ -70,22 +72,14 @@ Testnet or Mainnet.`, cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [fuji/devnet only]") cmd.Flags().StringVar(&nodeIDStr, "nodeID", "", "set the NodeID of the validator to add") - cmd.Flags().Uint64Var(&weight, "weight", 0, "set the staking weight of the validator to add") - - cmd.Flags().BoolVar(&useDefaultStartTime, "default-start-time", false, "use default start time for subnet validator (5 minutes later for fuji & mainnet, 30 seconds later for devnet)") - cmd.Flags().StringVar(&startTimeStr, "start-time", "", "UTC start time when this validator starts validating, in 'YYYY-MM-DD HH:MM:SS' format") - - cmd.Flags().BoolVar(&useDefaultDuration, "default-duration", false, "set duration so as to validate until primary validator ends its period") - cmd.Flags().DurationVar(&duration, "staking-period", 0, "how long this validator will be staking") - - cmd.Flags().BoolVar(&defaultValidatorParams, "default-validator-params", false, "use default weight/start/duration params for subnet validator") - + cmd.Flags().Uint64Var(&weight, "weight", constants.BootstrapValidatorWeight, "set the staking weight of the validator to add") cmd.Flags().StringSliceVar(&subnetAuthKeys, "subnet-auth-keys", nil, "control keys that will be used to authenticate add validator tx") - cmd.Flags().StringVar(&outputTxPath, "output-tx-path", "", "file path of the add validator tx") cmd.Flags().BoolVarP(&useEwoq, "ewoq", "e", false, "use ewoq key [fuji/devnet only]") cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji/devnet)") cmd.Flags().StringSliceVar(&ledgerAddresses, "ledger-addrs", []string{}, "use the given ledger addresses") - cmd.Flags().BoolVar(&waitForTxAcceptance, "wait-for-tx-acceptance", true, "just issue the add validator tx, without waiting for its acceptance") + cmd.Flags().BoolVar(&nonSOV, "not-sov", false, "set to true if adding validator to a non SOV blockchain") + cmd.Flags().StringVar(&publicKey, "public-key", "", "set the BLS public key of the validator to add") + cmd.Flags().StringVar(&pop, "proof-of-possession", "", "set the BLS proof of possession of the validator to add") return cmd } @@ -122,7 +116,21 @@ func addValidator(_ *cobra.Command, args []string) error { return err } deployer := subnet.NewPublicDeployer(app, kc, network) - return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeIDStr, defaultValidatorParams, waitForTxAcceptance) + if nonSOV { + return CallAddValidatorNonSOV(deployer, network, kc, useLedger, blockchainName, nodeIDStr, defaultValidatorParams, waitForTxAcceptance) + } + return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeIDStr, defaultValidatorParams) +} + +func PromptWeightBootstrapValidator() (uint64, error) { + txt := "What weight would you like to assign to the validator?" + return app.Prompt.CaptureWeight(txt) +} + +func PromptInitialBalance() (uint64, error) { + ux.Logger.PrintToUser("Balance is used to pay for continuous fee to the P-Chain") + txt := "What balance would you like to assign to the bootstrap validator (in AVAX)?" + return app.Prompt.CaptureValidatorBalance(txt) } func CallAddValidator( @@ -133,6 +141,132 @@ func CallAddValidator( blockchainName string, nodeIDStr string, defaultValidatorParamsSetting bool, +) error { + var ( + nodeID ids.NodeID + start time.Time + err error + ) + + useLedger = useLedgerSetting + defaultValidatorParams = defaultValidatorParamsSetting + + _, err = ValidateSubnetNameAndGetChains([]string{blockchainName}) + if err != nil { + return err + } + + sc, err := app.LoadSidecar(blockchainName) + if err != nil { + return err + } + + subnetID := sc.Networks[network.Name()].SubnetID + if subnetID == ids.Empty { + return errNoSubnetID + } + + isPermissioned, controlKeys, threshold, err := txutils.GetOwners(network, subnetID) + if err != nil { + return err + } + if !isPermissioned { + return ErrNotPermissionedSubnet + } + + kcKeys, err := kc.PChainFormattedStrAddresses() + if err != nil { + return err + } + + // get keys for add validator tx signing + if subnetAuthKeys != nil { + if err := prompts.CheckSubnetAuthKeys(kcKeys, subnetAuthKeys, controlKeys, threshold); err != nil { + return err + } + } else { + subnetAuthKeys, err = prompts.GetSubnetAuthKeys(app.Prompt, kcKeys, controlKeys, threshold) + if err != nil { + return err + } + } + ux.Logger.PrintToUser("Your subnet auth keys for add validator tx creation: %s", subnetAuthKeys) + + if nodeIDStr == "" { + nodeID, err = PromptNodeID("add as Subnet validator") + if err != nil { + return err + } + } else { + nodeID, err = ids.NodeIDFromString(nodeIDStr) + if err != nil { + return err + } + } + + publicKey, pop, err = promptProofOfPossession(publicKey == "", pop == "") + if err != nil { + return err + } + + selectedWeight, err := getWeight() + if err != nil { + return err + } + if selectedWeight < constants.MinStakeWeight { + return fmt.Errorf("invalid weight, must be greater than or equal to %d: %d", constants.MinStakeWeight, selectedWeight) + } + + start, selectedDuration, err := getTimeParameters(network, nodeID, true) + if err != nil { + return err + } + + ux.Logger.PrintToUser("NodeID: %s", nodeID.String()) + ux.Logger.PrintToUser("Network: %s", network.Name()) + ux.Logger.PrintToUser("Start time: %s", start.Format(constants.TimeParseLayout)) + ux.Logger.PrintToUser("End time: %s", start.Add(selectedDuration).Format(constants.TimeParseLayout)) + ux.Logger.PrintToUser("Weight: %d", selectedWeight) + ux.Logger.PrintToUser("Inputs complete, issuing transaction to add the provided validator information...") + + //type RegisterSubnetValidatorTx struct { + // // Metadata, inputs and outputs + // BaseTx + // // Balance <= sum($AVAX inputs) - sum($AVAX outputs) - TxFee. + // Balance uint64 `json:"balance"` + // // [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 does uniquely map to a BLS key + // Signer signer.Signer `json:"signer"` + // // Leftover $AVAX from the Subnet Validator's Balance will be issued to + // // this owner after it is removed from the validator set. + // ChangeOwner fx.Owner `json:"changeOwner"` + // // AddressedCall with Payload: + // // - SubnetID + // // - NodeID (must be Ed25519 NodeID) + // // - Weight + // // - BLS public key + // // - Expiry + // Message warp.Message `json:"message"` + //} + + tx, err := deployer.RegisterSubnetValidator() + if err != nil { + return err + } + ux.Logger.GreenCheckmarkToUser("Register Subnet Validator Tx ID: %s", tx.ID()) + return nil +} + +func CallAddValidatorNonSOV( + deployer *subnet.PublicDeployer, + network models.Network, + kc *keychain.Keychain, + useLedgerSetting bool, + blockchainName string, + nodeIDStr string, + defaultValidatorParamsSetting bool, waitForTxAcceptanceSetting bool, ) error { var ( @@ -240,7 +374,7 @@ func CallAddValidator( ux.Logger.PrintToUser("Weight: %d", selectedWeight) ux.Logger.PrintToUser("Inputs complete, issuing transaction to add the provided validator information...") - isFullySigned, tx, remainingSubnetAuthKeys, err := deployer.AddValidator( + isFullySigned, tx, remainingSubnetAuthKeys, err := deployer.AddValidatorNonSOV( waitForTxAcceptance, controlKeys, subnetAuthKeys, diff --git a/cmd/blockchaincmd/convert.go b/cmd/blockchaincmd/convert.go index d4fc0b4fe..8306a2beb 100644 --- a/cmd/blockchaincmd/convert.go +++ b/cmd/blockchaincmd/convert.go @@ -3,41 +3,9 @@ package blockchaincmd import ( - "encoding/json" - "errors" - "fmt" - "os" - "path/filepath" - "strings" - - "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" - - "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" - "github.com/ava-labs/avalanche-cli/pkg/keychain" - "github.com/ava-labs/avalanche-cli/pkg/localnet" - "github.com/ava-labs/avalanche-cli/pkg/metrics" - "github.com/ava-labs/avalanche-cli/pkg/models" "github.com/ava-labs/avalanche-cli/pkg/networkoptions" - "github.com/ava-labs/avalanche-cli/pkg/prompts" - "github.com/ava-labs/avalanche-cli/pkg/subnet" - "github.com/ava-labs/avalanche-cli/pkg/txutils" - "github.com/ava-labs/avalanche-cli/pkg/ux" - "github.com/ava-labs/avalanche-cli/pkg/vm" - anrutils "github.com/ava-labs/avalanche-network-runner/utils" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/logging" - "github.com/ava-labs/avalanchego/vms/platformvm/txs" - "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" - "go.uber.org/zap" - "golang.org/x/mod/semver" ) // avalanche blockchain convert @@ -57,7 +25,7 @@ allowed. If you'd like to redeploy a Blockchain locally for testing, you must fi avalanche network clean to reset all deployed chain state. Subsequent local deploys redeploy the chain with fresh state. You can deploy the same Blockchain to multiple networks, so you can take your locally tested Subnet and deploy it on Fuji or Mainnet.`, - RunE: deployBlockchain, + RunE: convertSubnet, PersistentPostRun: handlePostRun, Args: cobrautils.ExactArgs(1), } @@ -90,789 +58,371 @@ so you can take your locally tested Subnet and deploy it on Fuji or Mainnet.`, return cmd } -type SubnetValidator struct { - // Must be Ed25519 NodeID - NodeID ids.NodeID `json:"nodeID"` - // Weight of this validator used when sampling - Weight uint64 `json:"weight"` - // When this validator will stop validating the Subnet - EndTime uint64 `json:"endTime"` - // Initial balance for this validator - Balance uint64 `json:"balance"` - // [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 `json:"signer"` - // Leftover $AVAX from the [Balance] will be issued to this - // owner once it is removed from the validator set. - ChangeOwner fx.Owner `json:"changeOwner"` -} - -func CallDeploy( - cmd *cobra.Command, - subnetOnlyParam bool, - blockchainName string, - networkFlags networkoptions.NetworkFlags, - keyNameParam string, - useLedgerParam bool, - useEwoqParam bool, - sameControlKeyParam bool, -) error { - subnetOnly = subnetOnlyParam - globalNetworkFlags = networkFlags - sameControlKey = sameControlKeyParam - keyName = keyNameParam - useLedger = useLedgerParam - useEwoq = useEwoqParam - return deployBlockchain(cmd, []string{blockchainName}) -} - -func getChainsInSubnet(blockchainName string) ([]string, error) { - subnets, err := os.ReadDir(app.GetSubnetDir()) - if err != nil { - return nil, fmt.Errorf("failed to read baseDir: %w", err) - } - - chains := []string{} - - for _, s := range subnets { - if !s.IsDir() { - continue - } - sidecarFile := filepath.Join(app.GetSubnetDir(), s.Name(), constants.SidecarFileName) - if _, err := os.Stat(sidecarFile); err == nil { - // read in sidecar file - jsonBytes, err := os.ReadFile(sidecarFile) - if err != nil { - return nil, fmt.Errorf("failed reading file %s: %w", sidecarFile, err) - } - - var sc models.Sidecar - err = json.Unmarshal(jsonBytes, &sc) - if err != nil { - return nil, fmt.Errorf("failed unmarshaling file %s: %w", sidecarFile, err) - } - if sc.Subnet == blockchainName { - chains = append(chains, sc.Name) - } - } - } - return chains, nil -} - -func checkSubnetEVMDefaultAddressNotInAlloc(network models.Network, chain string) error { - if network.Kind != models.Local && network.Kind != models.Devnet && os.Getenv(constants.SimulatePublicNetwork) == "" { - genesis, err := app.LoadEvmGenesis(chain) - if err != nil { - return err - } - allocAddressMap := genesis.Alloc - for address := range allocAddressMap { - if address.String() == vm.PrefundedEwoqAddress.String() { - return fmt.Errorf("can't airdrop to default address on public networks, please edit the genesis by calling `avalanche subnet create %s --force`", chain) - } - } - } - return nil -} - -func runDeploy(cmd *cobra.Command, args []string, supportedNetworkOptions []networkoptions.NetworkOption) error { - skipCreatePrompt = true - deploySupportedNetworkOptions = supportedNetworkOptions - return deployBlockchain(cmd, args) -} - -func updateSubnetEVMGenesisChainID(genesisBytes []byte, newChainID uint) ([]byte, error) { - var genesisMap map[string]interface{} - if err := json.Unmarshal(genesisBytes, &genesisMap); err != nil { - return nil, err - } - configI, ok := genesisMap["config"] - if !ok { - return nil, fmt.Errorf("config field not found on genesis") - } - config, ok := configI.(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("expected genesis config field to be a map[string]interface, found %T", configI) - } - config["chainId"] = float64(newChainID) - return json.MarshalIndent(genesisMap, "", " ") -} - -// updates sidecar with genesis mainnet id to use -// given either by cmdline flag, original genesis id, or id obtained from the user -func getSubnetEVMMainnetChainID(sc *models.Sidecar, blockchainName string) error { - // get original chain id - evmGenesis, err := app.LoadEvmGenesis(blockchainName) - if err != nil { - return err - } - if evmGenesis.Config == nil { - return fmt.Errorf("invalid subnet evm genesis format: config is nil") - } - if evmGenesis.Config.ChainID == nil { - return fmt.Errorf("invalid subnet evm genesis format: config chain id is nil") - } - originalChainID := evmGenesis.Config.ChainID.Uint64() - // handle cmdline flag if given - if mainnetChainID != 0 { - sc.SubnetEVMMainnetChainID = uint(mainnetChainID) - } - // prompt the user - if sc.SubnetEVMMainnetChainID == 0 { - useSameChainID := "Use same ChainID" - useNewChainID := "Use new ChainID" - listOptions := []string{useNewChainID, useSameChainID} - newChainIDPrompt := "Using the same ChainID for both Fuji and Mainnet could lead to a replay attack. Do you want to use a different ChainID?" - var ( - err error - decision string - ) - decision, err = app.Prompt.CaptureList(newChainIDPrompt, listOptions) - if err != nil { - return err - } - if decision == useSameChainID { - sc.SubnetEVMMainnetChainID = uint(originalChainID) - } else { - ux.Logger.PrintToUser("Enter your subnet's ChainID. It can be any positive integer != %d.", originalChainID) - newChainID, err := app.Prompt.CapturePositiveInt( - "ChainID", - []prompts.Comparator{ - { - Label: "Zero", - Type: prompts.MoreThan, - Value: 0, - }, - { - Label: "Original Chain ID", - Type: prompts.NotEq, - Value: originalChainID, - }, - }, - ) - if err != nil { - return err - } - sc.SubnetEVMMainnetChainID = uint(newChainID) - } - } - return app.UpdateSidecar(sc) -} - -// deployBlockchain is the cobra command run for deploying subnets -func deployBlockchain(cmd *cobra.Command, args []string) error { - blockchainName := args[0] - - if err := CreateBlockchainFirst(cmd, blockchainName, skipCreatePrompt); err != nil { - return err - } - - chains, err := ValidateSubnetNameAndGetChains(args) - if err != nil { - return err - } - - if icmSpec.MessengerContractAddressPath != "" || icmSpec.MessengerDeployerAddressPath != "" || icmSpec.MessengerDeployerTxPath != "" || icmSpec.RegistryBydecodePath != "" { - if icmSpec.MessengerContractAddressPath == "" || icmSpec.MessengerDeployerAddressPath == "" || icmSpec.MessengerDeployerTxPath == "" || icmSpec.RegistryBydecodePath == "" { - return fmt.Errorf("if setting any teleporter asset path, you must set all teleporter asset paths") - } - } - - var bootstrapValidators []models.SubnetValidator - if bootstrapValidatorsJSONFilePath != "" { - bootstrapValidators, err = LoadBootstrapValidator(bootstrapValidatorsJSONFilePath) - if err != nil { - return err - } - } - - chain := chains[0] - - sidecar, err := app.LoadSidecar(chain) - if err != nil { - return fmt.Errorf("failed to load sidecar for later update: %w", err) - } - - if sidecar.ImportedFromAPM { - return errors.New("unable to deploy subnets imported from a repo") - } - - if outputTxPath != "" { - if _, err := os.Stat(outputTxPath); err == nil { - return fmt.Errorf("outputTxPath %q already exists", outputTxPath) - } - } - - network, err := networkoptions.GetNetworkFromCmdLineFlags( - app, - "", - globalNetworkFlags, - true, - false, - deploySupportedNetworkOptions, - "", - ) - if err != nil { - return err - } - - isEVMGenesis, validationErr, err := app.HasSubnetEVMGenesis(chain) - if err != nil { - return err - } - if sidecar.VM == models.SubnetEvm && !isEVMGenesis { - return fmt.Errorf("failed to validate SubnetEVM genesis format: %w", validationErr) - } - - chainGenesis, err := app.LoadRawGenesis(chain) - if err != nil { - return err - } - - if isEVMGenesis { - // is is a subnet evm or a custom vm based on subnet evm - if network.Kind == models.Mainnet { - err = getSubnetEVMMainnetChainID(&sidecar, chain) - if err != nil { - return err - } - chainGenesis, err = updateSubnetEVMGenesisChainID(chainGenesis, sidecar.SubnetEVMMainnetChainID) - if err != nil { - return err - } - } - err = checkSubnetEVMDefaultAddressNotInAlloc(network, chain) - if err != nil { - return err - } - } - - 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 { - app.Log.Debug("Deploy local") - - genesisPath := app.GetGenesisPath(chain) - - // copy vm binary to the expected location, first downloading it if necessary - var vmBin string - switch sidecar.VM { - case models.SubnetEvm: - _, vmBin, err = binutils.SetupSubnetEVM(app, sidecar.VMVersion) - if err != nil { - return fmt.Errorf("failed to install subnet-evm: %w", err) - } - case models.CustomVM: - vmBin = binutils.SetupCustomBin(app, chain) - default: - return fmt.Errorf("unknown vm: %s", sidecar.VM) - } - - // check if selected version matches what is currently running - nc := localnet.NewStatusChecker() - avagoVersion, err := CheckForInvalidDeployAndGetAvagoVersion(nc, sidecar.RPCVersion) - if err != nil { - return err - } - if avagoBinaryPath == "" { - userProvidedAvagoVersion = avagoVersion - } - - deployer := subnet.NewLocalDeployer(app, userProvidedAvagoVersion, avagoBinaryPath, vmBin) - deployInfo, err := deployer.DeployToLocalNetwork(chain, genesisPath, icmSpec, subnetIDStr) - if err != nil { - if deployer.BackendStartedHere() { - if innerErr := binutils.KillgRPCServerProcess(app); innerErr != nil { - app.Log.Warn("tried to kill the gRPC server process but it failed", zap.Error(innerErr)) - } - } - return err - } - flags := make(map[string]string) - flags[constants.MetricsNetwork] = network.Name() - metrics.HandleTracking(cmd, constants.MetricsSubnetDeployCommand, app, flags) - if err := app.UpdateSidecarNetworks( - &sidecar, - network, - deployInfo.SubnetID, - deployInfo.BlockchainID, - deployInfo.ICMMessengerAddress, - deployInfo.ICMRegistryAddress, - bootstrapValidators, - ); err != nil { - return err - } - return PrintSubnetInfo(blockchainName, true) - } - - // from here on we are assuming a public deploy - if subnetOnly && subnetIDStr != "" { - return errMutuallyExlusiveSubnetFlags - } - - createSubnet := true - var subnetID ids.ID - if subnetIDStr != "" { - subnetID, err = ids.FromString(subnetIDStr) - if err != nil { - return err - } - createSubnet = false - } else if !subnetOnly && sidecar.Networks != nil { - model, ok := sidecar.Networks[network.Name()] - if ok { - if model.SubnetID != ids.Empty && model.BlockchainID == ids.Empty { - subnetID = model.SubnetID - createSubnet = false - } - } - } - - fee := uint64(0) - if !subnetOnly { - fee += network.GenesisParams().TxFeeConfig.StaticFeeConfig.CreateBlockchainTxFee - } - if createSubnet { - fee += network.GenesisParams().TxFeeConfig.StaticFeeConfig.CreateSubnetTxFee - } - - kc, err := keychain.GetKeychainFromCmdLineFlags( - app, - constants.PayTxsFeesMsg, - network, - keyName, - useEwoq, - useLedger, - ledgerAddresses, - fee, - ) - if err != nil { - return err - } - - network.HandlePublicNetworkSimulation() - - if createSubnet { - controlKeys, threshold, err = promptOwners( - kc, - controlKeys, - sameControlKey, - threshold, - subnetAuthKeys, - true, - ) - if err != nil { - return err - } - } else { - ux.Logger.PrintToUser(logging.Blue.Wrap( - fmt.Sprintf("Deploying into pre-existent subnet ID %s", subnetID.String()), - )) - var isPermissioned bool - isPermissioned, controlKeys, threshold, err = txutils.GetOwners(network, subnetID) - if err != nil { - return err - } - if !isPermissioned { - return ErrNotPermissionedSubnet - } - } - - // add control keys to the keychain whenever possible - if err := kc.AddAddresses(controlKeys); err != nil { - return err - } - - kcKeys, err := kc.PChainFormattedStrAddresses() - if err != nil { - return err - } - - // get keys for blockchain tx signing - if subnetAuthKeys != nil { - if err := prompts.CheckSubnetAuthKeys(kcKeys, subnetAuthKeys, controlKeys, threshold); err != nil { - return err - } - } else { - subnetAuthKeys, err = prompts.GetSubnetAuthKeys(app.Prompt, kcKeys, controlKeys, threshold) - if err != nil { - return err - } - } - ux.Logger.PrintToUser("Your subnet auth keys for chain creation: %s", subnetAuthKeys) - - // deploy to public network - deployer := subnet.NewPublicDeployer(app, kc, network) - - if createSubnet { - subnetID, err = deployer.DeploySubnet(controlKeys, threshold) - if err != nil { - return err - } - // get the control keys in the same order as the tx - _, controlKeys, threshold, err = txutils.GetOwners(network, subnetID) - if err != nil { - return err - } - } - - var ( - savePartialTx bool - blockchainID ids.ID - tx *txs.Tx - remainingSubnetAuthKeys []string - isFullySigned bool - ) - - if !subnetOnly { - isFullySigned, blockchainID, tx, remainingSubnetAuthKeys, err = deployer.DeployBlockchain( - controlKeys, - subnetAuthKeys, - subnetID, - chain, - chainGenesis, - ) - if err != nil { - ux.Logger.PrintToUser(logging.Red.Wrap( - fmt.Sprintf("error deploying blockchain: %s. fix the issue and try again with a new deploy cmd", err), - )) - } - - savePartialTx = !isFullySigned && err == nil - } - - if err := PrintDeployResults(chain, subnetID, blockchainID); err != nil { - return err - } - - if savePartialTx { - if err := SaveNotFullySignedTx( - "Blockchain Creation", - tx, - chain, - subnetAuthKeys, - remainingSubnetAuthKeys, - outputTxPath, - false, - ); err != nil { - return err - } - } - - // type ConvertSubnetTx struct { - // // Metadata, inputs and outputs - // BaseTx - // // ID of the Subnet to transform - // // Restrictions: - // // - Must not be the Primary Network ID - // Subnet ids.ID `json:"subnetID"` - // // BlockchainID where the Subnet manager lives - // ChainID ids.ID `json:"chainID"` - // // Address of the Subnet manager - // Address []byte `json:"address"` - // // Initial pay-as-you-go validators for the Subnet - // Validators []SubnetValidator `json:"validators"` - // // Authorizes this conversion - // SubnetAuth verify.Verifiable `json:"subnetAuthorization"` +// // convertSubnet is the cobra command run for deploying subnets +func convertSubnet(cmd *cobra.Command, args []string) error { + //blockchainName := args[0] + // + //if err := CreateBlockchainFirst(cmd, blockchainName, skipCreatePrompt); err != nil { + // return err + //} + // + //chains, err := ValidateSubnetNameAndGetChains(args) + //if err != nil { + // return err + //} + // + //if icmSpec.MessengerContractAddressPath != "" || icmSpec.MessengerDeployerAddressPath != "" || icmSpec.MessengerDeployerTxPath != "" || icmSpec.RegistryBydecodePath != "" { + // if icmSpec.MessengerContractAddressPath == "" || icmSpec.MessengerDeployerAddressPath == "" || icmSpec.MessengerDeployerTxPath == "" || icmSpec.RegistryBydecodePath == "" { + // return fmt.Errorf("if setting any teleporter asset path, you must set all teleporter asset paths") // } - - //avaGoBootstrapValidators, err := convertToAvalancheGoSubnetValidator(bootstrapValidators) + //} + // + //var bootstrapValidators []models.SubnetValidator + //if bootstrapValidatorsJSONFilePath != "" { + // bootstrapValidators, err = LoadBootstrapValidator(bootstrapValidatorsJSONFilePath) + // if err != nil { + // return err + // } + //} + // + //chain := chains[0] + // + //sidecar, err := app.LoadSidecar(chain) + //if err != nil { + // return fmt.Errorf("failed to load sidecar for later update: %w", err) + //} + // + //if sidecar.ImportedFromAPM { + // return errors.New("unable to deploy subnets imported from a repo") + //} + // + //if outputTxPath != "" { + // if _, err := os.Stat(outputTxPath); err == nil { + // return fmt.Errorf("outputTxPath %q already exists", outputTxPath) + // } + //} + // + //network, err := networkoptions.GetNetworkFromCmdLineFlags( + // app, + // "", + // globalNetworkFlags, + // true, + // false, + // deploySupportedNetworkOptions, + // "", + //) //if err != nil { // return err //} - // TODO: replace with avalanchego subnetValidators once implemented - isFullySigned, convertSubnetTxID, tx, remainingSubnetAuthKeys, err := deployer.ConvertSubnet( - controlKeys, - subnetAuthKeys, - subnetID, - blockchainID, - // avaGoBootstrapValidators, - ) - if err != nil { - ux.Logger.PrintToUser(logging.Red.Wrap( - fmt.Sprintf("error converting blockchain: %s. fix the issue and try again with a new convert cmd", err), - )) - } - - savePartialTx = !isFullySigned && err == nil - ux.Logger.PrintToUser("ConvertSubnetTx ID: %s", convertSubnetTxID) - - if savePartialTx { - if err := SaveNotFullySignedTx( - "ConvertSubnetTx", - tx, - chain, - subnetAuthKeys, - remainingSubnetAuthKeys, - outputTxPath, - false, - ); err != nil { - return err - } - } - - flags := make(map[string]string) - flags[constants.MetricsNetwork] = network.Name() - metrics.HandleTracking(cmd, constants.MetricsSubnetDeployCommand, app, flags) - - // update sidecar - // TODO: need to do something for backwards compatibility? - return app.UpdateSidecarNetworks(&sidecar, network, subnetID, blockchainID, "", "", bootstrapValidators) -} - -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 convertToAvalancheGoSubnetValidator(subnetValidators []models.SubnetValidator) ([]SubnetValidator, error) { - bootstrapValidators := []SubnetValidator{} - for _, validator := range subnetValidators { - nodeID, err := ids.NodeIDFromString(validator.NodeID) - if err != nil { - return nil, err - } - blsInfo, err := getBLSInfo(validator.BLSPublicKey, validator.BLSProofOfPossession) - if err != nil { - return nil, fmt.Errorf("failure parsing BLS info: %w", err) - } - addrs, err := address.ParseToIDs([]string{validator.ChangeOwnerAddr}) - if err != nil { - return nil, fmt.Errorf("failure parsing change owner address: %w", err) - } - bootstrapValidator := SubnetValidator{ - NodeID: nodeID, - Weight: validator.Weight, - Balance: validator.Balance, - Signer: blsInfo, - ChangeOwner: &secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: addrs, - }, - } - bootstrapValidators = append(bootstrapValidators, bootstrapValidator) - } - return bootstrapValidators, nil -} - -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... - if err := checkInvalidSubnetNames(args[0]); err != nil { - return nil, fmt.Errorf("subnet name %s is invalid: %w", args[0], err) - } - // Check subnet exists - // TODO create a file that lists chains by subnet for fast querying - chains, err := getChainsInSubnet(args[0]) - if err != nil { - return nil, fmt.Errorf("failed to getChainsInSubnet: %w", err) - } - - if len(chains) == 0 { - return nil, errors.New("Invalid subnet " + args[0]) - } - - return chains, nil -} - -func SaveNotFullySignedTx( - txName string, - tx *txs.Tx, - chain string, - subnetAuthKeys []string, - remainingSubnetAuthKeys []string, - outputTxPath string, - forceOverwrite bool, -) error { - signedCount := len(subnetAuthKeys) - len(remainingSubnetAuthKeys) - ux.Logger.PrintToUser("") - if signedCount == len(subnetAuthKeys) { - ux.Logger.PrintToUser("All %d required %s signatures have been signed. "+ - "Saving tx to disk to enable commit.", len(subnetAuthKeys), txName) - } else { - ux.Logger.PrintToUser("%d of %d required %s signatures have been signed. "+ - "Saving tx to disk to enable remaining signing.", signedCount, len(subnetAuthKeys), txName) - } - if outputTxPath == "" { - ux.Logger.PrintToUser("") - var err error - if forceOverwrite { - outputTxPath, err = app.Prompt.CaptureString("Path to export partially signed tx to") - } else { - outputTxPath, err = app.Prompt.CaptureNewFilepath("Path to export partially signed tx to") - } - if err != nil { - return err - } - } - if forceOverwrite { - ux.Logger.PrintToUser("") - ux.Logger.PrintToUser("Overwriting %s", outputTxPath) - } - if err := txutils.SaveToDisk(tx, outputTxPath, forceOverwrite); err != nil { - return err - } - if signedCount == len(subnetAuthKeys) { - PrintReadyToSignMsg(chain, outputTxPath) - } else { - PrintRemainingToSignMsg(chain, remainingSubnetAuthKeys, outputTxPath) - } - return nil -} - -func PrintReadyToSignMsg( - chain string, - outputTxPath string, -) { - ux.Logger.PrintToUser("") - ux.Logger.PrintToUser("Tx is fully signed, and ready to be committed") - ux.Logger.PrintToUser("") - ux.Logger.PrintToUser("Commit command:") - ux.Logger.PrintToUser(" avalanche transaction commit %s --input-tx-filepath %s", chain, outputTxPath) -} - -func PrintRemainingToSignMsg( - chain string, - remainingSubnetAuthKeys []string, - outputTxPath string, -) { - ux.Logger.PrintToUser("") - ux.Logger.PrintToUser("Addresses remaining to sign the tx") - for _, subnetAuthKey := range remainingSubnetAuthKeys { - ux.Logger.PrintToUser(" %s", subnetAuthKey) - } - ux.Logger.PrintToUser("") - ux.Logger.PrintToUser("Connect a ledger with one of the remaining addresses or choose a stored key "+ - "and run the signing command, or send %q to another user for signing.", outputTxPath) - ux.Logger.PrintToUser("") - ux.Logger.PrintToUser("Signing command:") - ux.Logger.PrintToUser(" avalanche transaction sign %s --input-tx-filepath %s", chain, outputTxPath) - ux.Logger.PrintToUser("") -} - -func PrintDeployResults(chain string, subnetID ids.ID, blockchainID ids.ID) error { - vmID, err := anrutils.VMID(chain) - if err != nil { - return fmt.Errorf("failed to create VM ID from %s: %w", chain, err) - } - header := []string{"Deployment results", ""} - table := tablewriter.NewWriter(os.Stdout) - table.SetHeader(header) - table.SetRowLine(true) - table.SetAutoMergeCells(true) - table.Append([]string{"Chain Name", chain}) - table.Append([]string{"Subnet ID", subnetID.String()}) - table.Append([]string{"VM ID", vmID.String()}) - if blockchainID != ids.Empty { - table.Append([]string{"Blockchain ID", blockchainID.String()}) - table.Append([]string{"P-Chain TXID", blockchainID.String()}) - } - table.Render() + // + //isEVMGenesis, validationErr, err := app.HasSubnetEVMGenesis(chain) + //if err != nil { + // return err + //} + //if sidecar.VM == models.SubnetEvm && !isEVMGenesis { + // return fmt.Errorf("failed to validate SubnetEVM genesis format: %w", validationErr) + //} + // + //chainGenesis, err := app.LoadRawGenesis(chain) + //if err != nil { + // return err + //} + // + //if isEVMGenesis { + // // is is a subnet evm or a custom vm based on subnet evm + // if network.Kind == models.Mainnet { + // err = getSubnetEVMMainnetChainID(&sidecar, chain) + // if err != nil { + // return err + // } + // chainGenesis, err = updateSubnetEVMGenesisChainID(chainGenesis, sidecar.SubnetEVMMainnetChainID) + // if err != nil { + // return err + // } + // } + // err = checkSubnetEVMDefaultAddressNotInAlloc(network, chain) + // if err != nil { + // return err + // } + //} + // + //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 { + // app.Log.Debug("Deploy local") + // + // genesisPath := app.GetGenesisPath(chain) + // + // // copy vm binary to the expected location, first downloading it if necessary + // var vmBin string + // switch sidecar.VM { + // case models.SubnetEvm: + // _, vmBin, err = binutils.SetupSubnetEVM(app, sidecar.VMVersion) + // if err != nil { + // return fmt.Errorf("failed to install subnet-evm: %w", err) + // } + // case models.CustomVM: + // vmBin = binutils.SetupCustomBin(app, chain) + // default: + // return fmt.Errorf("unknown vm: %s", sidecar.VM) + // } + // + // // check if selected version matches what is currently running + // nc := localnet.NewStatusChecker() + // avagoVersion, err := CheckForInvalidDeployAndGetAvagoVersion(nc, sidecar.RPCVersion) + // if err != nil { + // return err + // } + // if avagoBinaryPath == "" { + // userProvidedAvagoVersion = avagoVersion + // } + // + // deployer := subnet.NewLocalDeployer(app, userProvidedAvagoVersion, avagoBinaryPath, vmBin) + // deployInfo, err := deployer.DeployToLocalNetwork(chain, genesisPath, icmSpec, subnetIDStr) + // if err != nil { + // if deployer.BackendStartedHere() { + // if innerErr := binutils.KillgRPCServerProcess(app); innerErr != nil { + // app.Log.Warn("tried to kill the gRPC server process but it failed", zap.Error(innerErr)) + // } + // } + // return err + // } + // flags := make(map[string]string) + // flags[constants.MetricsNetwork] = network.Name() + // metrics.HandleTracking(cmd, constants.MetricsSubnetDeployCommand, app, flags) + // if err := app.UpdateSidecarNetworks( + // &sidecar, + // network, + // deployInfo.SubnetID, + // deployInfo.BlockchainID, + // deployInfo.ICMMessengerAddress, + // deployInfo.ICMRegistryAddress, + // bootstrapValidators, + // ); err != nil { + // return err + // } + // return PrintSubnetInfo(blockchainName, true) + //} + // + //// from here on we are assuming a public deploy + //if subnetOnly && subnetIDStr != "" { + // return errMutuallyExlusiveSubnetFlags + //} + // + //createSubnet := true + //var subnetID ids.ID + //if subnetIDStr != "" { + // subnetID, err = ids.FromString(subnetIDStr) + // if err != nil { + // return err + // } + // createSubnet = false + //} else if !subnetOnly && sidecar.Networks != nil { + // model, ok := sidecar.Networks[network.Name()] + // if ok { + // if model.SubnetID != ids.Empty && model.BlockchainID == ids.Empty { + // subnetID = model.SubnetID + // createSubnet = false + // } + // } + //} + // + //fee := uint64(0) + //if !subnetOnly { + // fee += network.GenesisParams().TxFeeConfig.StaticFeeConfig.CreateBlockchainTxFee + //} + //if createSubnet { + // fee += network.GenesisParams().TxFeeConfig.StaticFeeConfig.CreateSubnetTxFee + //} + // + //kc, err := keychain.GetKeychainFromCmdLineFlags( + // app, + // constants.PayTxsFeesMsg, + // network, + // keyName, + // useEwoq, + // useLedger, + // ledgerAddresses, + // fee, + //) + //if err != nil { + // return err + //} + // + //network.HandlePublicNetworkSimulation() + // + //if createSubnet { + // controlKeys, threshold, err = promptOwners( + // kc, + // controlKeys, + // sameControlKey, + // threshold, + // subnetAuthKeys, + // true, + // ) + // if err != nil { + // return err + // } + //} else { + // ux.Logger.PrintToUser(logging.Blue.Wrap( + // fmt.Sprintf("Deploying into pre-existent subnet ID %s", subnetID.String()), + // )) + // var isPermissioned bool + // isPermissioned, controlKeys, threshold, err = txutils.GetOwners(network, subnetID) + // if err != nil { + // return err + // } + // if !isPermissioned { + // return ErrNotPermissionedSubnet + // } + //} + // + //// add control keys to the keychain whenever possible + //if err := kc.AddAddresses(controlKeys); err != nil { + // return err + //} + // + //kcKeys, err := kc.PChainFormattedStrAddresses() + //if err != nil { + // return err + //} + // + //// get keys for blockchain tx signing + //if subnetAuthKeys != nil { + // if err := prompts.CheckSubnetAuthKeys(kcKeys, subnetAuthKeys, controlKeys, threshold); err != nil { + // return err + // } + //} else { + // subnetAuthKeys, err = prompts.GetSubnetAuthKeys(app.Prompt, kcKeys, controlKeys, threshold) + // if err != nil { + // return err + // } + //} + //ux.Logger.PrintToUser("Your subnet auth keys for chain creation: %s", subnetAuthKeys) + // + //// deploy to public network + //deployer := subnet.NewPublicDeployer(app, kc, network) + // + //if createSubnet { + // subnetID, err = deployer.DeploySubnet(controlKeys, threshold) + // if err != nil { + // return err + // } + // // get the control keys in the same order as the tx + // _, controlKeys, threshold, err = txutils.GetOwners(network, subnetID) + // if err != nil { + // return err + // } + //} + // + //var ( + // savePartialTx bool + // blockchainID ids.ID + // tx *txs.Tx + // remainingSubnetAuthKeys []string + // isFullySigned bool + //) + // + //if !subnetOnly { + // isFullySigned, blockchainID, tx, remainingSubnetAuthKeys, err = deployer.DeployBlockchain( + // controlKeys, + // subnetAuthKeys, + // subnetID, + // chain, + // chainGenesis, + // ) + // if err != nil { + // ux.Logger.PrintToUser(logging.Red.Wrap( + // fmt.Sprintf("error deploying blockchain: %s. fix the issue and try again with a new deploy cmd", err), + // )) + // } + // + // savePartialTx = !isFullySigned && err == nil + //} + // + //if err := PrintDeployResults(chain, subnetID, blockchainID); err != nil { + // return err + //} + // + //if savePartialTx { + // if err := SaveNotFullySignedTx( + // "Blockchain Creation", + // tx, + // chain, + // subnetAuthKeys, + // remainingSubnetAuthKeys, + // outputTxPath, + // false, + // ); err != nil { + // return err + // } + //} + // + //// type ConvertSubnetTx struct { + //// // Metadata, inputs and outputs + //// BaseTx + //// // ID of the Subnet to transform + //// // Restrictions: + //// // - Must not be the Primary Network ID + //// Subnet ids.ID `json:"subnetID"` + //// // BlockchainID where the Subnet manager lives + //// ChainID ids.ID `json:"chainID"` + //// // Address of the Subnet manager + //// Address []byte `json:"address"` + //// // Initial pay-as-you-go validators for the Subnet + //// Validators []SubnetValidator `json:"validators"` + //// // Authorizes this conversion + //// SubnetAuth verify.Verifiable `json:"subnetAuthorization"` + //// } + // + ////avaGoBootstrapValidators, err := convertToAvalancheGoSubnetValidator(bootstrapValidators) + ////if err != nil { + //// return err + ////} + //// TODO: replace with avalanchego subnetValidators once implemented + //isFullySigned, convertSubnetTxID, tx, remainingSubnetAuthKeys, err := deployer.ConvertSubnet( + // controlKeys, + // subnetAuthKeys, + // subnetID, + // blockchainID, + // // avaGoBootstrapValidators, + //) + //if err != nil { + // ux.Logger.PrintToUser(logging.Red.Wrap( + // fmt.Sprintf("error converting blockchain: %s. fix the issue and try again with a new convert cmd", err), + // )) + //} + // + //savePartialTx = !isFullySigned && err == nil + //ux.Logger.PrintToUser("ConvertSubnetTx ID: %s", convertSubnetTxID) + // + //if savePartialTx { + // if err := SaveNotFullySignedTx( + // "ConvertSubnetTx", + // tx, + // chain, + // subnetAuthKeys, + // remainingSubnetAuthKeys, + // outputTxPath, + // false, + // ); err != nil { + // return err + // } + //} + // + //flags := make(map[string]string) + //flags[constants.MetricsNetwork] = network.Name() + //metrics.HandleTracking(cmd, constants.MetricsSubnetDeployCommand, app, flags) + // + //// update sidecar + //// TODO: need to do something for backwards compatibility? + //return app.UpdateSidecarNetworks(&sidecar, network, subnetID, blockchainID, "", "", bootstrapValidators) return nil } - -// Determines the appropriate version of avalanchego to run with. Returns an error if -// that version conflicts with the current deployment. -func CheckForInvalidDeployAndGetAvagoVersion( - statusChecker localnet.StatusChecker, - configuredRPCVersion int, -) (string, error) { - // get current network - runningAvagoVersion, runningRPCVersion, networkRunning, err := statusChecker.GetCurrentNetworkVersion() - if err != nil { - return "", err - } - desiredAvagoVersion := userProvidedAvagoVersion - - // RPC Version was made available in the info API in avalanchego version v1.9.2. For prior versions, - // we will need to skip this check. - skipRPCCheck := false - if semver.Compare(runningAvagoVersion, constants.AvalancheGoCompatibilityVersionAdded) == -1 { - skipRPCCheck = true - } - - if networkRunning { - if userProvidedAvagoVersion == "latest" { - if runningRPCVersion != configuredRPCVersion && !skipRPCCheck { - return "", fmt.Errorf( - "the current avalanchego deployment uses rpc version %d but your subnet has version %d and is not compatible", - runningRPCVersion, - configuredRPCVersion, - ) - } - desiredAvagoVersion = runningAvagoVersion - } else if runningAvagoVersion != strings.Split(userProvidedAvagoVersion, "-")[0] { - // user wants a specific version - return "", errors.New("incompatible avalanchego version selected") - } - } else if userProvidedAvagoVersion == "latest" { - // find latest avago version for this rpc version - desiredAvagoVersion, err = vm.GetLatestAvalancheGoByProtocolVersion( - app, configuredRPCVersion, constants.AvalancheGoCompatibilityURL) - if err == vm.ErrNoAvagoVersion { - latestPreReleaseVersion, err := app.Downloader.GetLatestPreReleaseVersion( - constants.AvaLabsOrg, - constants.AvalancheGoRepoName, - ) - if err != nil { - return "", err - } - return latestPreReleaseVersion, nil - } - if err != nil { - return "", err - } - } - 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 { - subnetValidator.NodeID, subnetValidator.BLSPublicKey, subnetValidator.BLSProofOfPossession, err = generateNewNodeAndBLS() - if err != nil { - return nil, err - } - } - } - return subnetValidators, nil -} diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index 1a1b7ae67..8643e5605 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -58,6 +58,7 @@ var ( useLedger bool useEwoq bool ledgerAddresses []string + nonSOV bool subnetIDStr string mainnetChainID uint32 skipCreatePrompt bool diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 0ef77f3f7..3c92a4172 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -31,19 +31,25 @@ func getValidatorContractManagerAddr() (string, error) { ) } -func promptProofOfPossession() (string, string, error) { +func promptProofOfPossession(promptPublicKey, promptPop bool) (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 node's BLS public key?" - publicKey, err := app.Prompt.CaptureValidatedString(txt, prompts.ValidateHexa) - if err != nil { - return "", "", err + publicKey := "" + proofOfPossesion := "" + if promptPublicKey { + 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 node's BLS proof of possession?" - proofOfPossesion, err := app.Prompt.CaptureValidatedString(txt, prompts.ValidateHexa) - if err != nil { - return "", "", err + if promptPop { + txt := "What is the node's BLS proof of possession?" + proofOfPossesion, err = app.Prompt.CaptureValidatedString(txt, prompts.ValidateHexa) + if err != nil { + return "", "", err + } } return publicKey, proofOfPossesion, nil } @@ -139,7 +145,7 @@ func promptBootstrapValidators(network models.Network) ([]models.SubnetValidator if err != nil { return nil, err } - publicKey, pop, err = promptProofOfPossession() + publicKey, pop, err = promptProofOfPossession(true, true) if err != nil { return nil, err } diff --git a/cmd/nodecmd/validate_subnet.go b/cmd/nodecmd/validate_subnet.go index ebc952a5e..552115426 100644 --- a/cmd/nodecmd/validate_subnet.go +++ b/cmd/nodecmd/validate_subnet.go @@ -96,7 +96,7 @@ func addNodeAsSubnetValidator( } ux.Logger.PrintToUser("Adding the node as a Subnet Validator...") defer ux.Logger.PrintLineSeparator() - if err := blockchaincmd.CallAddValidator( + if err := blockchaincmd.CallAddValidatorNonSOV( deployer, network, kc, diff --git a/pkg/prompts/prompts.go b/pkg/prompts/prompts.go index fd4b0b84e..b2f267e18 100644 --- a/pkg/prompts/prompts.go +++ b/pkg/prompts/prompts.go @@ -106,7 +106,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) + CaptureValidatorBalance(promptStr string) (uint64, error) CapturePositiveInt(promptStr string, comparators []Comparator) (int, error) CaptureInt(promptStr string) (int, error) CaptureUint32(promptStr string) (uint32, error) @@ -268,10 +268,10 @@ func (*realPrompter) CaptureNodeID(promptStr string) (ids.NodeID, error) { return ids.NodeIDFromString(nodeIDStr) } -func (*realPrompter) CaptureBootstrapInitialBalance(promptStr string) (uint64, error) { +func (*realPrompter) CaptureValidatorBalance(promptStr string) (uint64, error) { prompt := promptui.Prompt{ Label: promptStr, - Validate: validateBootstrapValidatorBalance, + Validate: validateValidatorBalance, } amountStr, err := prompt.Run() diff --git a/pkg/prompts/validations.go b/pkg/prompts/validations.go index 3a510c70f..b296a4264 100644 --- a/pkg/prompts/validations.go +++ b/pkg/prompts/validations.go @@ -129,13 +129,13 @@ func validateWeight(input string) error { return nil } -func validateBootstrapValidatorBalance(input string) error { +func validateValidatorBalance(input string) error { val, err := strconv.ParseUint(input, 10, 64) if err != nil { return err } if val == 0 { - return fmt.Errorf("initial bootstrap validator balance must be greater than 0 AVAX") + return fmt.Errorf("subnet validator balance must be greater than 0 AVAX") } return nil } diff --git a/pkg/subnet/public.go b/pkg/subnet/public.go index e62cd2336..ae1eeb6f2 100644 --- a/pkg/subnet/public.go +++ b/pkg/subnet/public.go @@ -6,6 +6,8 @@ import ( "context" "errors" "fmt" + "github.com/ava-labs/avalanchego/vms/platformvm/fx" + "github.com/ava-labs/avalanchego/vms/platformvm/warp" "time" "github.com/ava-labs/avalanchego/vms/platformvm/signer" @@ -58,7 +60,7 @@ func NewPublicDeployer(app *application.Avalanche, kc *keychain.Keychain, networ // - signs the tx with the wallet as the owner of fee outputs and a possible subnet auth key // - if partially signed, returns the tx so that it can later on be signed by the rest of the subnet auth keys // - if fully signed, issues it -func (d *PublicDeployer) AddValidator( +func (d *PublicDeployer) AddValidatorNonSOV( waitForTxAcceptance bool, controlKeys []string, subnetAuthKeysStrs []string, @@ -111,6 +113,27 @@ func (d *PublicDeployer) AddValidator( return false, tx, remainingSubnetAuthKeys, nil } +func (d *PublicDeployer) RegisterSubnetValidator( + balance uint64, + signer signer.Signer, + changeOwner fx.Owner, + message warp.Message, +) (*txs.Tx, error) { + // create tx + //unsignedTx, err := wallet.P().Builder().NewRegisterSubnetValidatorTx(args...) + //if err != nil { + // return nil, fmt.Errorf("error building tx: %w", err) + //} + //tx := txs.Tx{Unsigned: unsignedTx} + // sign with current wallet that contains EVM address controlling POA Validator Manager + // TODO: change code below + //if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { + // return nil, fmt.Errorf("error signing tx: %w", err) + //} + //return &tx, nil + return nil, nil +} + // change subnet owner for [subnetID] // - creates a transfer subnet ownership tx // - sets the change output owner to be a wallet address (if not, it may go to any other subnet auth address) From 2c3c7c0772ffff99227d693e7349e6f2cc022d95 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 25 Sep 2024 13:56:41 -0400 Subject: [PATCH 60/74] add validator --- cmd/blockchaincmd/add_validator.go | 80 ++++++++++++++---------------- 1 file changed, 36 insertions(+), 44 deletions(-) diff --git a/cmd/blockchaincmd/add_validator.go b/cmd/blockchaincmd/add_validator.go index f8f35cd88..13dcb92be 100644 --- a/cmd/blockchaincmd/add_validator.go +++ b/cmd/blockchaincmd/add_validator.go @@ -7,6 +7,10 @@ import ( "fmt" "time" + "github.com/ava-labs/avalanchego/utils/formatting/address" + warpPlatformVM "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanche-cli/pkg/cobrautils" "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/keychain" @@ -73,7 +77,6 @@ Testnet or Mainnet.`, cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [fuji/devnet only]") cmd.Flags().StringVar(&nodeIDStr, "nodeID", "", "set the NodeID of the validator to add") cmd.Flags().Uint64Var(&weight, "weight", constants.BootstrapValidatorWeight, "set the staking weight of the validator to add") - cmd.Flags().StringSliceVar(&subnetAuthKeys, "subnet-auth-keys", nil, "control keys that will be used to authenticate add validator tx") cmd.Flags().BoolVarP(&useEwoq, "ewoq", "e", false, "use ewoq key [fuji/devnet only]") cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji/devnet)") cmd.Flags().StringSliceVar(&ledgerAddresses, "ledger-addrs", []string{}, "use the given ledger addresses") @@ -112,8 +115,10 @@ func addValidator(_ *cobra.Command, args []string) error { return err } network.HandlePublicNetworkSimulation() - if err := UpdateKeychainWithSubnetControlKeys(kc, network, blockchainName); err != nil { - return err + if nonSOV { + if err := UpdateKeychainWithSubnetControlKeys(kc, network, blockchainName); err != nil { + return err + } } deployer := subnet.NewPublicDeployer(app, kc, network) if nonSOV { @@ -122,12 +127,7 @@ func addValidator(_ *cobra.Command, args []string) error { return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeIDStr, defaultValidatorParams) } -func PromptWeightBootstrapValidator() (uint64, error) { - txt := "What weight would you like to assign to the validator?" - return app.Prompt.CaptureWeight(txt) -} - -func PromptInitialBalance() (uint64, error) { +func promptValidatorBalance() (uint64, error) { ux.Logger.PrintToUser("Balance is used to pay for continuous fee to the P-Chain") txt := "What balance would you like to assign to the bootstrap validator (in AVAX)?" return app.Prompt.CaptureValidatorBalance(txt) @@ -144,7 +144,6 @@ func CallAddValidator( ) error { var ( nodeID ids.NodeID - start time.Time err error ) @@ -166,31 +165,11 @@ func CallAddValidator( return errNoSubnetID } - isPermissioned, controlKeys, threshold, err := txutils.GetOwners(network, subnetID) - if err != nil { - return err - } - if !isPermissioned { - return ErrNotPermissionedSubnet - } - - kcKeys, err := kc.PChainFormattedStrAddresses() - if err != nil { - return err - } - - // get keys for add validator tx signing - if subnetAuthKeys != nil { - if err := prompts.CheckSubnetAuthKeys(kcKeys, subnetAuthKeys, controlKeys, threshold); err != nil { - return err - } - } else { - subnetAuthKeys, err = prompts.GetSubnetAuthKeys(app.Prompt, kcKeys, controlKeys, threshold) - if err != nil { - return err - } - } - ux.Logger.PrintToUser("Your subnet auth keys for add validator tx creation: %s", subnetAuthKeys) + // TODO: implement getting validator manager controller address + //kcKeys, err := kc.PChainFormattedStrAddresses() + //if err != nil { + // return err + //} if nodeIDStr == "" { nodeID, err = PromptNodeID("add as Subnet validator") @@ -209,24 +188,19 @@ func CallAddValidator( return err } - selectedWeight, err := getWeight() + balance, err := promptValidatorBalance() if err != nil { return err } - if selectedWeight < constants.MinStakeWeight { - return fmt.Errorf("invalid weight, must be greater than or equal to %d: %d", constants.MinStakeWeight, selectedWeight) - } - start, selectedDuration, err := getTimeParameters(network, nodeID, true) + changeAddr, err := getKeyForChangeOwner("", network) if err != nil { return err } ux.Logger.PrintToUser("NodeID: %s", nodeID.String()) ux.Logger.PrintToUser("Network: %s", network.Name()) - ux.Logger.PrintToUser("Start time: %s", start.Format(constants.TimeParseLayout)) - ux.Logger.PrintToUser("End time: %s", start.Add(selectedDuration).Format(constants.TimeParseLayout)) - ux.Logger.PrintToUser("Weight: %d", selectedWeight) + ux.Logger.PrintToUser("Weight: %d", weight) ux.Logger.PrintToUser("Inputs complete, issuing transaction to add the provided validator information...") //type RegisterSubnetValidatorTx struct { @@ -251,7 +225,21 @@ func CallAddValidator( // Message warp.Message `json:"message"` //} - tx, err := deployer.RegisterSubnetValidator() + blsInfo, err := getBLSInfo(publicKey, pop) + if err != nil { + return fmt.Errorf("failure parsing BLS info: %w", err) + } + addrs, err := address.ParseToIDs([]string{changeAddr}) + if err != nil { + return fmt.Errorf("failure parsing change owner address: %w", err) + } + changeOwner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: addrs, + } + // TODO: generate warp message + message, err := generateWarpMessage() + tx, err := deployer.RegisterSubnetValidator(balance, blsInfo, changeOwner, message) if err != nil { return err } @@ -259,6 +247,10 @@ func CallAddValidator( return nil } +func generateWarpMessage() (warpPlatformVM.Message, error) { + return warpPlatformVM.Message{}, nil +} + func CallAddValidatorNonSOV( deployer *subnet.PublicDeployer, network models.Network, From 8770e4d1eb9d71af4a4d7a4fd0e6898a499ef943 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 25 Sep 2024 14:23:44 -0400 Subject: [PATCH 61/74] mock --- internal/mocks/prompter.go | 48 +++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/internal/mocks/prompter.go b/internal/mocks/prompter.go index 4d43df3df..d9fa0f805 100644 --- a/internal/mocks/prompter.go +++ b/internal/mocks/prompter.go @@ -76,30 +76,6 @@ 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) @@ -780,6 +756,30 @@ func (_m *Prompter) CaptureValidatedString(promptStr string, validator func(stri return r0, r1 } +// CaptureValidatorBalance provides a mock function with given fields: promptStr +func (_m *Prompter) CaptureValidatorBalance(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 +} + // CaptureVersion provides a mock function with given fields: promptStr func (_m *Prompter) CaptureVersion(promptStr string) (string, error) { ret := _m.Called(promptStr) From f5f2e378e2629f41862fe389078cc37608a82c15 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 25 Sep 2024 19:14:57 -0400 Subject: [PATCH 62/74] remove validator --- cmd/blockchaincmd/add_validator.go | 4 +- cmd/blockchaincmd/remove_validator.go | 55 ++++++++++++++++++++++++--- pkg/subnet/public.go | 27 +++++++++++++ 3 files changed, 78 insertions(+), 8 deletions(-) diff --git a/cmd/blockchaincmd/add_validator.go b/cmd/blockchaincmd/add_validator.go index 13dcb92be..5f3fd0120 100644 --- a/cmd/blockchaincmd/add_validator.go +++ b/cmd/blockchaincmd/add_validator.go @@ -238,7 +238,7 @@ func CallAddValidator( Addrs: addrs, } // TODO: generate warp message - message, err := generateWarpMessage() + message, err := generateWarpMessageAddValidator() tx, err := deployer.RegisterSubnetValidator(balance, blsInfo, changeOwner, message) if err != nil { return err @@ -247,7 +247,7 @@ func CallAddValidator( return nil } -func generateWarpMessage() (warpPlatformVM.Message, error) { +func generateWarpMessageAddValidator() (warpPlatformVM.Message, error) { return warpPlatformVM.Message{}, nil } diff --git a/cmd/blockchaincmd/remove_validator.go b/cmd/blockchaincmd/remove_validator.go index 72ece3c53..0bd12ec09 100644 --- a/cmd/blockchaincmd/remove_validator.go +++ b/cmd/blockchaincmd/remove_validator.go @@ -7,6 +7,8 @@ import ( "fmt" "os" + warpPlatformVM "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/avalanche-cli/pkg/cobrautils" "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/keychain" @@ -49,14 +51,12 @@ these prompts by providing the values with flags.`, cmd.Flags().StringVar(&outputTxPath, "output-tx-path", "", "file path of the removeValidator tx") cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji)") cmd.Flags().StringSliceVar(&ledgerAddresses, "ledger-addrs", []string{}, "use the given ledger addresses") + cmd.Flags().BoolVar(&nonSOV, "not-sov", false, "set to true if removing validator in a non SOV blockchain") return cmd } func removeValidator(_ *cobra.Command, args []string) error { - var ( - nodeID ids.NodeID - err error - ) + var err error network, err := networkoptions.GetNetworkFromCmdLineFlags( app, @@ -129,6 +129,51 @@ func removeValidator(_ *cobra.Command, args []string) error { return errNoSubnetID } + deployer := subnet.NewPublicDeployer(app, kc, network) + if nonSOV { + return removeValidatorNonSOV(deployer, network, subnetID, kc, blockchainName) + } + return removeValidatorSOV(deployer) +} + +// TODO: implement getMinNonce +// getMinNonce gets minNonce associated with the validationID from P-Chain +func getMinNonce(validationID [32]byte) (uint64, error) { + return 0, nil +} + +// TODO: implement getValidationID +// get validation ID for a node from P Chain +func getValidationID() [32]byte { + return [32]byte{} +} + +// TODO: implement generateWarpMessageRemoveValidator +func generateWarpMessageRemoveValidator(validationID [32]byte, nonce, weight uint64) (warpPlatformVM.Message, error) { + return warpPlatformVM.Message{}, nil +} + +func removeValidatorSOV(deployer *subnet.PublicDeployer) error { + validationID := getValidationID() + minNonce, err := getMinNonce(validationID) + if err != nil { + return err + } + message, err := generateWarpMessageRemoveValidator(validationID, minNonce+1, 0) + if err != nil { + return err + } + tx, err := deployer.SetSubnetValidatorWeight(message) + if err != nil { + return err + } + ux.Logger.GreenCheckmarkToUser("Set Subnet Validator Weight to 0 Tx ID: %s", tx.ID()) + return nil +} + +func removeValidatorNonSOV(deployer *subnet.PublicDeployer, network models.Network, subnetID ids.ID, kc *keychain.Keychain, blockchainName string) error { + var nodeID ids.NodeID + isPermissioned, controlKeys, threshold, err := txutils.GetOwners(network, subnetID) if err != nil { return err @@ -186,7 +231,6 @@ func removeValidator(_ *cobra.Command, args []string) error { ux.Logger.PrintToUser("Network: %s", network.Name()) ux.Logger.PrintToUser("Inputs complete, issuing transaction to remove the specified validator...") - deployer := subnet.NewPublicDeployer(app, kc, network) isFullySigned, tx, remainingSubnetAuthKeys, err := deployer.RemoveValidator( controlKeys, subnetAuthKeys, @@ -209,7 +253,6 @@ func removeValidator(_ *cobra.Command, args []string) error { return err } } - return err } diff --git a/pkg/subnet/public.go b/pkg/subnet/public.go index ae1eeb6f2..48770d519 100644 --- a/pkg/subnet/public.go +++ b/pkg/subnet/public.go @@ -113,6 +113,33 @@ func (d *PublicDeployer) AddValidatorNonSOV( return false, tx, remainingSubnetAuthKeys, nil } +// type SetSubnetValidatorWeightTx struct { +// // Metadata, inputs and outputs +// BaseTx +// // AddressedCall with Payload: +// // - ValidationID (SHA256 of the AddressedCall Payload of the RegisterSubnetValidatorTx adding the validator) +// // - Nonce +// // - Weight +// Message warp.Message `json:"message"` +// } +func (d *PublicDeployer) SetSubnetValidatorWeight( + message warp.Message, +) (*txs.Tx, error) { + // create tx + //unsignedTx, err := wallet.P().Builder().NewSetSubnetValidatorWeightTx(args...) + //if err != nil { + // return nil, fmt.Errorf("error building tx: %w", err) + //} + //tx := txs.Tx{Unsigned: unsignedTx} + // sign with current wallet that contains EVM address controlling POA Validator Manager + // TODO: change code below + //if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { + // return nil, fmt.Errorf("error signing tx: %w", err) + //} + //return &tx, nil + return nil, nil +} + func (d *PublicDeployer) RegisterSubnetValidator( balance uint64, signer signer.Signer, From 68eeec1722fea52d3030f565626efac79bff98c2 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Wed, 25 Sep 2024 20:44:29 -0400 Subject: [PATCH 63/74] update weight --- cmd/blockchaincmd/blockchain.go | 2 + cmd/blockchaincmd/change_weight.go | 570 ++++++++++++++++++++++++++ cmd/blockchaincmd/remove_validator.go | 2 +- 3 files changed, 573 insertions(+), 1 deletion(-) create mode 100644 cmd/blockchaincmd/change_weight.go diff --git a/cmd/blockchaincmd/blockchain.go b/cmd/blockchaincmd/blockchain.go index 514f92e52..54989cd6c 100644 --- a/cmd/blockchaincmd/blockchain.go +++ b/cmd/blockchaincmd/blockchain.go @@ -60,5 +60,7 @@ manage your Blockchain configurations and live deployments.`, cmd.AddCommand(newValidatorsCmd()) // subnet changeOwner cmd.AddCommand(newChangeOwnerCmd()) + // subnet changeWeight + cmd.AddCommand(newChangeWeightCmd()) return cmd } diff --git a/cmd/blockchaincmd/change_weight.go b/cmd/blockchaincmd/change_weight.go new file mode 100644 index 000000000..41e71022c --- /dev/null +++ b/cmd/blockchaincmd/change_weight.go @@ -0,0 +1,570 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package blockchaincmd + +import ( + "errors" + "fmt" + "os" + "time" + + "github.com/ava-labs/avalanchego/utils/formatting/address" + warpPlatformVM "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + + "github.com/ava-labs/avalanche-cli/pkg/cobrautils" + "github.com/ava-labs/avalanche-cli/pkg/constants" + "github.com/ava-labs/avalanche-cli/pkg/keychain" + "github.com/ava-labs/avalanche-cli/pkg/models" + "github.com/ava-labs/avalanche-cli/pkg/networkoptions" + "github.com/ava-labs/avalanche-cli/pkg/prompts" + "github.com/ava-labs/avalanche-cli/pkg/subnet" + "github.com/ava-labs/avalanche-cli/pkg/txutils" + "github.com/ava-labs/avalanche-cli/pkg/utils" + "github.com/ava-labs/avalanche-cli/pkg/ux" + "github.com/ava-labs/avalanchego/ids" + avagoconstants "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/vms/platformvm" + "github.com/spf13/cobra" +) + +var () + +// avalanche blockchain addValidator +func newChangeWeightCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "changeWeight [blockchainName] [nodeID]", + Short: "Changes the weight of a Subnet validator", + Long: `The blockchain changeWeight command changes the weight of a Subnet Validator. + +The Subnet has to be a Proof of Authority Subnet-Only Validator Subnet.`, + RunE: updateWeight, + Args: cobrautils.ExactArgs(1), + } + networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, true, addValidatorSupportedNetworkOptions) + + cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [fuji/devnet only]") + cmd.Flags().StringVar(&nodeIDStr, "nodeID", "", "set the NodeID of the validator to add") + cmd.Flags().Uint64Var(&weight, "weight", constants.BootstrapValidatorWeight, "set the staking weight of the validator to add") + cmd.Flags().BoolVarP(&useEwoq, "ewoq", "e", false, "use ewoq key [fuji/devnet only]") + cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji/devnet)") + cmd.Flags().StringSliceVar(&ledgerAddresses, "ledger-addrs", []string{}, "use the given ledger addresses") + cmd.Flags().BoolVar(&nonSOV, "not-sov", false, "set to true if adding validator to a non SOV blockchain") + cmd.Flags().StringVar(&publicKey, "public-key", "", "set the BLS public key of the validator to add") + cmd.Flags().StringVar(&pop, "proof-of-possession", "", "set the BLS proof of possession of the validator to add") + return cmd +} + +func updateWeight(_ *cobra.Command, args []string) error { + blockchainName := args[0] + nodeID := args[1] + var err error + + network, err := networkoptions.GetNetworkFromCmdLineFlags( + app, + "", + globalNetworkFlags, + true, + false, + removeValidatorSupportedNetworkOptions, + "", + ) + if err != nil { + return err + } + + if outputTxPath != "" { + if _, err := os.Stat(outputTxPath); err == nil { + return fmt.Errorf("outputTxPath %q already exists", outputTxPath) + } + } + + if len(ledgerAddresses) > 0 { + useLedger = true + } + + if useLedger && keyName != "" { + return ErrMutuallyExlusiveKeyLedger + } + + switch network.Kind { + case models.Local: + return removeFromLocal(blockchainName) + case models.Fuji: + if !useLedger && keyName == "" { + useLedger, keyName, err = prompts.GetKeyOrLedger(app.Prompt, constants.PayTxsFeesMsg, app.GetKeyDir(), false) + if err != nil { + return err + } + } + case models.Mainnet: + useLedger = true + if keyName != "" { + return ErrStoredKeyOnMainnet + } + default: + return errors.New("unsupported network") + } + + // get keychain accesor + fee := network.GenesisParams().TxFeeConfig.StaticFeeConfig.TxFee + kc, err := keychain.GetKeychain(app, false, useLedger, ledgerAddresses, keyName, network, fee) + if err != nil { + return err + } + + network.HandlePublicNetworkSimulation() + + sc, err := app.LoadSidecar(blockchainName) + if err != nil { + return err + } + + subnetID := sc.Networks[network.Name()].SubnetID + if subnetID == ids.Empty { + return errNoSubnetID + } + + deployer := subnet.NewPublicDeployer(app, kc, network) + if nonSOV { + return removeValidatorNonSOV(deployer, network, subnetID, kc, blockchainName) + } + return removeValidatorSOV(deployer) +} + +func promptValidatorBalance() (uint64, error) { + ux.Logger.PrintToUser("Balance is used to pay for continuous fee to the P-Chain") + txt := "What balance would you like to assign to the bootstrap validator (in AVAX)?" + return app.Prompt.CaptureValidatorBalance(txt) +} + +func CallAddValidator( + deployer *subnet.PublicDeployer, + network models.Network, + kc *keychain.Keychain, + useLedgerSetting bool, + blockchainName string, + nodeIDStr string, + defaultValidatorParamsSetting bool, +) error { + var ( + nodeID ids.NodeID + err error + ) + + useLedger = useLedgerSetting + defaultValidatorParams = defaultValidatorParamsSetting + + _, err = ValidateSubnetNameAndGetChains([]string{blockchainName}) + if err != nil { + return err + } + + sc, err := app.LoadSidecar(blockchainName) + if err != nil { + return err + } + + subnetID := sc.Networks[network.Name()].SubnetID + if subnetID == ids.Empty { + return errNoSubnetID + } + + // TODO: implement getting validator manager controller address + //kcKeys, err := kc.PChainFormattedStrAddresses() + //if err != nil { + // return err + //} + + if nodeIDStr == "" { + nodeID, err = PromptNodeID("add as Subnet validator") + if err != nil { + return err + } + } else { + nodeID, err = ids.NodeIDFromString(nodeIDStr) + if err != nil { + return err + } + } + + publicKey, pop, err = promptProofOfPossession(publicKey == "", pop == "") + if err != nil { + return err + } + + balance, err := promptValidatorBalance() + if err != nil { + return err + } + + changeAddr, err := getKeyForChangeOwner("", network) + if err != nil { + return err + } + + ux.Logger.PrintToUser("NodeID: %s", nodeID.String()) + ux.Logger.PrintToUser("Network: %s", network.Name()) + ux.Logger.PrintToUser("Weight: %d", weight) + ux.Logger.PrintToUser("Inputs complete, issuing transaction to add the provided validator information...") + + //type RegisterSubnetValidatorTx struct { + // // Metadata, inputs and outputs + // BaseTx + // // Balance <= sum($AVAX inputs) - sum($AVAX outputs) - TxFee. + // Balance uint64 `json:"balance"` + // // [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 does uniquely map to a BLS key + // Signer signer.Signer `json:"signer"` + // // Leftover $AVAX from the Subnet Validator's Balance will be issued to + // // this owner after it is removed from the validator set. + // ChangeOwner fx.Owner `json:"changeOwner"` + // // AddressedCall with Payload: + // // - SubnetID + // // - NodeID (must be Ed25519 NodeID) + // // - Weight + // // - BLS public key + // // - Expiry + // Message warp.Message `json:"message"` + //} + + blsInfo, err := getBLSInfo(publicKey, pop) + if err != nil { + return fmt.Errorf("failure parsing BLS info: %w", err) + } + addrs, err := address.ParseToIDs([]string{changeAddr}) + if err != nil { + return fmt.Errorf("failure parsing change owner address: %w", err) + } + changeOwner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: addrs, + } + // TODO: generate warp message + message, err := generateWarpMessageAddValidator() + tx, err := deployer.RegisterSubnetValidator(balance, blsInfo, changeOwner, message) + if err != nil { + return err + } + ux.Logger.GreenCheckmarkToUser("Register Subnet Validator Tx ID: %s", tx.ID()) + return nil +} + +func generateWarpMessageAddValidator() (warpPlatformVM.Message, error) { + return warpPlatformVM.Message{}, nil +} + +func CallAddValidatorNonSOV( + deployer *subnet.PublicDeployer, + network models.Network, + kc *keychain.Keychain, + useLedgerSetting bool, + blockchainName string, + nodeIDStr string, + defaultValidatorParamsSetting bool, + waitForTxAcceptanceSetting bool, +) error { + var ( + nodeID ids.NodeID + start time.Time + err error + ) + + useLedger = useLedgerSetting + defaultValidatorParams = defaultValidatorParamsSetting + waitForTxAcceptance = waitForTxAcceptanceSetting + + if defaultValidatorParams { + useDefaultDuration = true + useDefaultStartTime = true + useDefaultWeight = true + } + + if useDefaultDuration && duration != 0 { + return errMutuallyExclusiveDurationOptions + } + if useDefaultStartTime && startTimeStr != "" { + return errMutuallyExclusiveStartOptions + } + if useDefaultWeight && weight != 0 { + return errMutuallyExclusiveWeightOptions + } + + if outputTxPath != "" { + if utils.FileExists(outputTxPath) { + return fmt.Errorf("outputTxPath %q already exists", outputTxPath) + } + } + + _, err = ValidateSubnetNameAndGetChains([]string{blockchainName}) + if err != nil { + return err + } + + sc, err := app.LoadSidecar(blockchainName) + if err != nil { + return err + } + + subnetID := sc.Networks[network.Name()].SubnetID + if subnetID == ids.Empty { + return errNoSubnetID + } + + isPermissioned, controlKeys, threshold, err := txutils.GetOwners(network, subnetID) + if err != nil { + return err + } + if !isPermissioned { + return ErrNotPermissionedSubnet + } + + kcKeys, err := kc.PChainFormattedStrAddresses() + if err != nil { + return err + } + + // get keys for add validator tx signing + if subnetAuthKeys != nil { + if err := prompts.CheckSubnetAuthKeys(kcKeys, subnetAuthKeys, controlKeys, threshold); err != nil { + return err + } + } else { + subnetAuthKeys, err = prompts.GetSubnetAuthKeys(app.Prompt, kcKeys, controlKeys, threshold) + if err != nil { + return err + } + } + ux.Logger.PrintToUser("Your subnet auth keys for add validator tx creation: %s", subnetAuthKeys) + + if nodeIDStr == "" { + nodeID, err = PromptNodeID("add as validator") + if err != nil { + return err + } + } else { + nodeID, err = ids.NodeIDFromString(nodeIDStr) + if err != nil { + return err + } + } + + selectedWeight, err := getWeight() + if err != nil { + return err + } + if selectedWeight < constants.MinStakeWeight { + return fmt.Errorf("invalid weight, must be greater than or equal to %d: %d", constants.MinStakeWeight, selectedWeight) + } + + start, selectedDuration, err := getTimeParameters(network, nodeID, true) + if err != nil { + return err + } + + ux.Logger.PrintToUser("NodeID: %s", nodeID.String()) + ux.Logger.PrintToUser("Network: %s", network.Name()) + ux.Logger.PrintToUser("Start time: %s", start.Format(constants.TimeParseLayout)) + ux.Logger.PrintToUser("End time: %s", start.Add(selectedDuration).Format(constants.TimeParseLayout)) + ux.Logger.PrintToUser("Weight: %d", selectedWeight) + ux.Logger.PrintToUser("Inputs complete, issuing transaction to add the provided validator information...") + + isFullySigned, tx, remainingSubnetAuthKeys, err := deployer.AddValidatorNonSOV( + waitForTxAcceptance, + controlKeys, + subnetAuthKeys, + subnetID, + nodeID, + selectedWeight, + start, + selectedDuration, + ) + if err != nil { + return err + } + if !isFullySigned { + if err := SaveNotFullySignedTx( + "Add Validator", + tx, + blockchainName, + subnetAuthKeys, + remainingSubnetAuthKeys, + outputTxPath, + false, + ); err != nil { + return err + } + } + + return err +} + +func PromptDuration(start time.Time, network models.Network) (time.Duration, error) { + for { + txt := "How long should this validator be validating? Enter a duration, e.g. 8760h. Valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\"" + var d time.Duration + var err error + if network.Kind == models.Fuji { + d, err = app.Prompt.CaptureFujiDuration(txt) + } else { + d, err = app.Prompt.CaptureMainnetDuration(txt) + } + if err != nil { + return 0, err + } + end := start.Add(d) + confirm := fmt.Sprintf("Your validator will finish staking by %s", end.Format(constants.TimeParseLayout)) + yes, err := app.Prompt.CaptureYesNo(confirm) + if err != nil { + return 0, err + } + if yes { + return d, nil + } + } +} + +func getMaxValidationTime(network models.Network, nodeID ids.NodeID, startTime time.Time) (time.Duration, error) { + ctx, cancel := utils.GetAPIContext() + defer cancel() + platformCli := platformvm.NewClient(network.Endpoint) + vs, err := platformCli.GetCurrentValidators(ctx, avagoconstants.PrimaryNetworkID, nil) + cancel() + if err != nil { + return 0, err + } + for _, v := range vs { + if v.NodeID == nodeID { + return time.Unix(int64(v.EndTime), 0).Sub(startTime), nil + } + } + return 0, errors.New("nodeID not found in validator set: " + nodeID.String()) +} + +func getTimeParameters(network models.Network, nodeID ids.NodeID, isValidator bool) (time.Time, time.Duration, error) { + defaultStakingStartLeadTime := constants.StakingStartLeadTime + if network.Kind == models.Devnet { + defaultStakingStartLeadTime = constants.DevnetStakingStartLeadTime + } + + const custom = "Custom" + + // this sets either the global var startTimeStr or useDefaultStartTime to enable repeated execution with + // state keeping from node cmds + if startTimeStr == "" && !useDefaultStartTime { + if isValidator { + ux.Logger.PrintToUser("When should your validator start validating?\n" + + "If you validator is not ready by this time, subnet downtime can occur.") + } else { + ux.Logger.PrintToUser("When do you want to start delegating?\n") + } + defaultStartOption := "Start in " + ux.FormatDuration(defaultStakingStartLeadTime) + startTimeOptions := []string{defaultStartOption, custom} + startTimeOption, err := app.Prompt.CaptureList("Start time", startTimeOptions) + if err != nil { + return time.Time{}, 0, err + } + switch startTimeOption { + case defaultStartOption: + useDefaultStartTime = true + default: + start, err := promptStart() + if err != nil { + return time.Time{}, 0, err + } + startTimeStr = start.Format(constants.TimeParseLayout) + } + } + + var ( + err error + start time.Time + ) + if startTimeStr != "" { + start, err = time.Parse(constants.TimeParseLayout, startTimeStr) + if err != nil { + return time.Time{}, 0, err + } + if start.Before(time.Now().Add(constants.StakingMinimumLeadTime)) { + return time.Time{}, 0, fmt.Errorf("time should be at least %s in the future ", constants.StakingMinimumLeadTime) + } + } else { + start = time.Now().Add(defaultStakingStartLeadTime) + } + + // this sets either the global var duration or useDefaultDuration to enable repeated execution with + // state keeping from node cmds + if duration == 0 && !useDefaultDuration { + msg := "How long should your validator validate for?" + if !isValidator { + msg = "How long do you want to delegate for?" + } + const defaultDurationOption = "Until primary network validator expires" + durationOptions := []string{defaultDurationOption, custom} + durationOption, err := app.Prompt.CaptureList(msg, durationOptions) + if err != nil { + return time.Time{}, 0, err + } + switch durationOption { + case defaultDurationOption: + useDefaultDuration = true + default: + duration, err = PromptDuration(start, network) + if err != nil { + return time.Time{}, 0, err + } + } + } + + var selectedDuration time.Duration + if useDefaultDuration { + // avoid setting both globals useDefaultDuration and duration + selectedDuration, err = getMaxValidationTime(network, nodeID, start) + if err != nil { + return time.Time{}, 0, err + } + } else { + selectedDuration = duration + } + + return start, selectedDuration, nil +} + +func promptStart() (time.Time, error) { + txt := "When should the validator start validating? Enter a UTC datetime in 'YYYY-MM-DD HH:MM:SS' format" + return app.Prompt.CaptureDate(txt) +} + +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) +} + +func getWeight() (uint64, error) { + // this sets either the global var weight or useDefaultWeight to enable repeated execution with + // state keeping from node cmds + if weight == 0 && !useDefaultWeight { + defaultWeight := fmt.Sprintf("Default (%d)", constants.DefaultStakeWeight) + 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: + useDefaultWeight = true + default: + weight, err = app.Prompt.CaptureWeight(txt) + if err != nil { + return 0, err + } + } + } + if useDefaultWeight { + return constants.DefaultStakeWeight, nil + } + return weight, nil +} diff --git a/cmd/blockchaincmd/remove_validator.go b/cmd/blockchaincmd/remove_validator.go index 0bd12ec09..6f0a57776 100644 --- a/cmd/blockchaincmd/remove_validator.go +++ b/cmd/blockchaincmd/remove_validator.go @@ -34,7 +34,7 @@ var removeValidatorSupportedNetworkOptions = []networkoptions.NetworkOption{ // avalanche blockchain removeValidator func newRemoveValidatorCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "removeValidator [blockchainName]", + Use: "removeValidator [blockchainName] [nodeID]", Short: "Remove a permissioned validator from your blockchain's subnet", Long: `The blockchain removeValidator command stops a whitelisted, subnet network validator from validating your deployed Blockchain. From 0e74db6ac3ee89aad38674d5aaff68e53681931b Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 26 Sep 2024 13:18:09 -0400 Subject: [PATCH 64/74] update weight --- cmd/blockchaincmd/add_validator.go | 32 +- cmd/blockchaincmd/change_weight.go | 465 +++----------------------- cmd/blockchaincmd/deploy.go | 1 + cmd/blockchaincmd/remove_validator.go | 16 +- 4 files changed, 83 insertions(+), 431 deletions(-) diff --git a/cmd/blockchaincmd/add_validator.go b/cmd/blockchaincmd/add_validator.go index 5f3fd0120..894ec1f96 100644 --- a/cmd/blockchaincmd/add_validator.go +++ b/cmd/blockchaincmd/add_validator.go @@ -36,6 +36,7 @@ var ( } nodeIDStr string + balance uint64 weight uint64 startTimeStr string duration time.Duration @@ -46,6 +47,7 @@ var ( waitForTxAcceptance bool publicKey string pop string + changeAddr string errNoSubnetID = errors.New("failed to find the subnet ID for this subnet, has it been deployed/created on this network?") errMutuallyExclusiveDurationOptions = errors.New("--use-default-duration/--use-default-validator-params and --staking-period are mutually exclusive") @@ -83,6 +85,7 @@ Testnet or Mainnet.`, cmd.Flags().BoolVar(&nonSOV, "not-sov", false, "set to true if adding validator to a non SOV blockchain") cmd.Flags().StringVar(&publicKey, "public-key", "", "set the BLS public key of the validator to add") cmd.Flags().StringVar(&pop, "proof-of-possession", "", "set the BLS proof of possession of the validator to add") + cmd.Flags().StringVar(&changeAddr, "change-address", "", "P-Chain address that will receive any leftover AVAX from the validator when it is removed from Subnet") return cmd } @@ -124,7 +127,7 @@ func addValidator(_ *cobra.Command, args []string) error { if nonSOV { return CallAddValidatorNonSOV(deployer, network, kc, useLedger, blockchainName, nodeIDStr, defaultValidatorParams, waitForTxAcceptance) } - return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeIDStr, defaultValidatorParams) + return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeIDStr) } func promptValidatorBalance() (uint64, error) { @@ -140,7 +143,6 @@ func CallAddValidator( useLedgerSetting bool, blockchainName string, nodeIDStr string, - defaultValidatorParamsSetting bool, ) error { var ( nodeID ids.NodeID @@ -148,13 +150,37 @@ func CallAddValidator( ) useLedger = useLedgerSetting - defaultValidatorParams = defaultValidatorParamsSetting _, err = ValidateSubnetNameAndGetChains([]string{blockchainName}) if err != nil { return err } + switch network.Kind { + case models.Devnet: + if useLedger { + return ErrLedgerOnDevnet + } + keyName, err = prompts.CaptureKeyName(app.Prompt, constants.PayTxsFeesMsg, app.GetKeyDir(), false) + if err != nil { + return err + } + case models.Fuji: + if !useLedger && keyName == "" { + useLedger, keyName, err = prompts.GetKeyOrLedger(app.Prompt, constants.PayTxsFeesMsg, app.GetKeyDir(), false) + if err != nil { + return err + } + } + case models.Mainnet: + useLedger = true + if keyName != "" { + return ErrStoredKeyOnMainnet + } + default: + return errors.New("unsupported network") + } + sc, err := app.LoadSidecar(blockchainName) if err != nil { return err diff --git a/cmd/blockchaincmd/change_weight.go b/cmd/blockchaincmd/change_weight.go index 41e71022c..a4d16d7a4 100644 --- a/cmd/blockchaincmd/change_weight.go +++ b/cmd/blockchaincmd/change_weight.go @@ -5,13 +5,6 @@ package blockchaincmd import ( "errors" "fmt" - "os" - "time" - - "github.com/ava-labs/avalanchego/utils/formatting/address" - warpPlatformVM "github.com/ava-labs/avalanchego/vms/platformvm/warp" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" - "github.com/ava-labs/avalanche-cli/pkg/cobrautils" "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/keychain" @@ -19,13 +12,9 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/networkoptions" "github.com/ava-labs/avalanche-cli/pkg/prompts" "github.com/ava-labs/avalanche-cli/pkg/subnet" - "github.com/ava-labs/avalanche-cli/pkg/txutils" - "github.com/ava-labs/avalanche-cli/pkg/utils" - "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanchego/ids" - avagoconstants "github.com/ava-labs/avalanchego/utils/constants" - "github.com/ava-labs/avalanchego/vms/platformvm" "github.com/spf13/cobra" + "os" ) var () @@ -57,9 +46,16 @@ The Subnet has to be a Proof of Authority Subnet-Only Validator Subnet.`, func updateWeight(_ *cobra.Command, args []string) error { blockchainName := args[0] + // TODO: validate node id format nodeID := args[1] var err error + //TODO: add check for non SOV subnet + // return err if non SOV + + // TODO: check for number of validators + // return error if there is only 1 validator + network, err := networkoptions.GetNetworkFromCmdLineFlags( app, "", @@ -86,10 +82,16 @@ func updateWeight(_ *cobra.Command, args []string) error { if useLedger && keyName != "" { return ErrMutuallyExlusiveKeyLedger } - + switch network.Kind { - case models.Local: - return removeFromLocal(blockchainName) + case models.Devnet: + if useLedger { + return ErrLedgerOnDevnet + } + keyName, err = prompts.CaptureKeyName(app.Prompt, constants.PayTxsFeesMsg, app.GetKeyDir(), false) + if err != nil { + return err + } case models.Fuji: if !useLedger && keyName == "" { useLedger, keyName, err = prompts.GetKeyOrLedger(app.Prompt, constants.PayTxsFeesMsg, app.GetKeyDir(), false) @@ -126,445 +128,56 @@ func updateWeight(_ *cobra.Command, args []string) error { } deployer := subnet.NewPublicDeployer(app, kc, network) - if nonSOV { - return removeValidatorNonSOV(deployer, network, subnetID, kc, blockchainName) - } - return removeValidatorSOV(deployer) -} - -func promptValidatorBalance() (uint64, error) { - ux.Logger.PrintToUser("Balance is used to pay for continuous fee to the P-Chain") - txt := "What balance would you like to assign to the bootstrap validator (in AVAX)?" - return app.Prompt.CaptureValidatorBalance(txt) -} - -func CallAddValidator( - deployer *subnet.PublicDeployer, - network models.Network, - kc *keychain.Keychain, - useLedgerSetting bool, - blockchainName string, - nodeIDStr string, - defaultValidatorParamsSetting bool, -) error { - var ( - nodeID ids.NodeID - err error - ) - - useLedger = useLedgerSetting - defaultValidatorParams = defaultValidatorParamsSetting - - _, err = ValidateSubnetNameAndGetChains([]string{blockchainName}) - if err != nil { - return err - } - sc, err := app.LoadSidecar(blockchainName) + // first remove the validator from subnet + err = removeValidatorSOV(deployer) if err != nil { return err } - subnetID := sc.Networks[network.Name()].SubnetID - if subnetID == ids.Empty { - return errNoSubnetID - } + // TODO: we need to wait for the balance from the removed validator to arrive in changeAddr + // set arbitrary time.sleep here? - // TODO: implement getting validator manager controller address - //kcKeys, err := kc.PChainFormattedStrAddresses() - //if err != nil { - // return err - //} - - if nodeIDStr == "" { - nodeID, err = PromptNodeID("add as Subnet validator") - if err != nil { - return err - } - } else { - nodeID, err = ids.NodeIDFromString(nodeIDStr) - if err != nil { - return err - } - } - - publicKey, pop, err = promptProofOfPossession(publicKey == "", pop == "") + weight, err = promptWeightSubnetValidator() if err != nil { return err } - balance, err := promptValidatorBalance() + balance, err = getValidatorBalanceFromPChain() if err != nil { return err } - changeAddr, err := getKeyForChangeOwner("", network) + publicKey, pop, err = getBLSInfoFromPChain() if err != nil { return err } - ux.Logger.PrintToUser("NodeID: %s", nodeID.String()) - ux.Logger.PrintToUser("Network: %s", network.Name()) - ux.Logger.PrintToUser("Weight: %d", weight) - ux.Logger.PrintToUser("Inputs complete, issuing transaction to add the provided validator information...") - - //type RegisterSubnetValidatorTx struct { - // // Metadata, inputs and outputs - // BaseTx - // // Balance <= sum($AVAX inputs) - sum($AVAX outputs) - TxFee. - // Balance uint64 `json:"balance"` - // // [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 does uniquely map to a BLS key - // Signer signer.Signer `json:"signer"` - // // Leftover $AVAX from the Subnet Validator's Balance will be issued to - // // this owner after it is removed from the validator set. - // ChangeOwner fx.Owner `json:"changeOwner"` - // // AddressedCall with Payload: - // // - SubnetID - // // - NodeID (must be Ed25519 NodeID) - // // - Weight - // // - BLS public key - // // - Expiry - // Message warp.Message `json:"message"` - //} - - blsInfo, err := getBLSInfo(publicKey, pop) - if err != nil { - return fmt.Errorf("failure parsing BLS info: %w", err) - } - addrs, err := address.ParseToIDs([]string{changeAddr}) + changeAddr, err = getChangeAddrFromPChain() if err != nil { return fmt.Errorf("failure parsing change owner address: %w", err) } - changeOwner := &secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: addrs, - } - // TODO: generate warp message - message, err := generateWarpMessageAddValidator() - tx, err := deployer.RegisterSubnetValidator(balance, blsInfo, changeOwner, message) - if err != nil { - return err - } - ux.Logger.GreenCheckmarkToUser("Register Subnet Validator Tx ID: %s", tx.ID()) - return nil -} - -func generateWarpMessageAddValidator() (warpPlatformVM.Message, error) { - return warpPlatformVM.Message{}, nil -} - -func CallAddValidatorNonSOV( - deployer *subnet.PublicDeployer, - network models.Network, - kc *keychain.Keychain, - useLedgerSetting bool, - blockchainName string, - nodeIDStr string, - defaultValidatorParamsSetting bool, - waitForTxAcceptanceSetting bool, -) error { - var ( - nodeID ids.NodeID - start time.Time - err error - ) - - useLedger = useLedgerSetting - defaultValidatorParams = defaultValidatorParamsSetting - waitForTxAcceptance = waitForTxAcceptanceSetting - - if defaultValidatorParams { - useDefaultDuration = true - useDefaultStartTime = true - useDefaultWeight = true - } - - if useDefaultDuration && duration != 0 { - return errMutuallyExclusiveDurationOptions - } - if useDefaultStartTime && startTimeStr != "" { - return errMutuallyExclusiveStartOptions - } - if useDefaultWeight && weight != 0 { - return errMutuallyExclusiveWeightOptions - } - - if outputTxPath != "" { - if utils.FileExists(outputTxPath) { - return fmt.Errorf("outputTxPath %q already exists", outputTxPath) - } - } - - _, err = ValidateSubnetNameAndGetChains([]string{blockchainName}) - if err != nil { - return err - } - - sc, err := app.LoadSidecar(blockchainName) - if err != nil { - return err - } - - subnetID := sc.Networks[network.Name()].SubnetID - if subnetID == ids.Empty { - return errNoSubnetID - } - - isPermissioned, controlKeys, threshold, err := txutils.GetOwners(network, subnetID) - if err != nil { - return err - } - if !isPermissioned { - return ErrNotPermissionedSubnet - } - - kcKeys, err := kc.PChainFormattedStrAddresses() - if err != nil { - return err - } - // get keys for add validator tx signing - if subnetAuthKeys != nil { - if err := prompts.CheckSubnetAuthKeys(kcKeys, subnetAuthKeys, controlKeys, threshold); err != nil { - return err - } - } else { - subnetAuthKeys, err = prompts.GetSubnetAuthKeys(app.Prompt, kcKeys, controlKeys, threshold) - if err != nil { - return err - } - } - ux.Logger.PrintToUser("Your subnet auth keys for add validator tx creation: %s", subnetAuthKeys) - - if nodeIDStr == "" { - nodeID, err = PromptNodeID("add as validator") - if err != nil { - return err - } - } else { - nodeID, err = ids.NodeIDFromString(nodeIDStr) - if err != nil { - return err - } - } - - selectedWeight, err := getWeight() - if err != nil { - return err - } - if selectedWeight < constants.MinStakeWeight { - return fmt.Errorf("invalid weight, must be greater than or equal to %d: %d", constants.MinStakeWeight, selectedWeight) - } - - start, selectedDuration, err := getTimeParameters(network, nodeID, true) - if err != nil { - return err - } - - ux.Logger.PrintToUser("NodeID: %s", nodeID.String()) - ux.Logger.PrintToUser("Network: %s", network.Name()) - ux.Logger.PrintToUser("Start time: %s", start.Format(constants.TimeParseLayout)) - ux.Logger.PrintToUser("End time: %s", start.Add(selectedDuration).Format(constants.TimeParseLayout)) - ux.Logger.PrintToUser("Weight: %d", selectedWeight) - ux.Logger.PrintToUser("Inputs complete, issuing transaction to add the provided validator information...") - - isFullySigned, tx, remainingSubnetAuthKeys, err := deployer.AddValidatorNonSOV( - waitForTxAcceptance, - controlKeys, - subnetAuthKeys, - subnetID, - nodeID, - selectedWeight, - start, - selectedDuration, - ) - if err != nil { - return err - } - if !isFullySigned { - if err := SaveNotFullySignedTx( - "Add Validator", - tx, - blockchainName, - subnetAuthKeys, - remainingSubnetAuthKeys, - outputTxPath, - false, - ); err != nil { - return err - } - } - - return err -} - -func PromptDuration(start time.Time, network models.Network) (time.Duration, error) { - for { - txt := "How long should this validator be validating? Enter a duration, e.g. 8760h. Valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\"" - var d time.Duration - var err error - if network.Kind == models.Fuji { - d, err = app.Prompt.CaptureFujiDuration(txt) - } else { - d, err = app.Prompt.CaptureMainnetDuration(txt) - } - if err != nil { - return 0, err - } - end := start.Add(d) - confirm := fmt.Sprintf("Your validator will finish staking by %s", end.Format(constants.TimeParseLayout)) - yes, err := app.Prompt.CaptureYesNo(confirm) - if err != nil { - return 0, err - } - if yes { - return d, nil - } - } + // add back validator to subnet with updated weight + return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeID) } -func getMaxValidationTime(network models.Network, nodeID ids.NodeID, startTime time.Time) (time.Duration, error) { - ctx, cancel := utils.GetAPIContext() - defer cancel() - platformCli := platformvm.NewClient(network.Endpoint) - vs, err := platformCli.GetCurrentValidators(ctx, avagoconstants.PrimaryNetworkID, nil) - cancel() - if err != nil { - return 0, err - } - for _, v := range vs { - if v.NodeID == nodeID { - return time.Unix(int64(v.EndTime), 0).Sub(startTime), nil - } - } - return 0, errors.New("nodeID not found in validator set: " + nodeID.String()) -} - -func getTimeParameters(network models.Network, nodeID ids.NodeID, isValidator bool) (time.Time, time.Duration, error) { - defaultStakingStartLeadTime := constants.StakingStartLeadTime - if network.Kind == models.Devnet { - defaultStakingStartLeadTime = constants.DevnetStakingStartLeadTime - } - - const custom = "Custom" - - // this sets either the global var startTimeStr or useDefaultStartTime to enable repeated execution with - // state keeping from node cmds - if startTimeStr == "" && !useDefaultStartTime { - if isValidator { - ux.Logger.PrintToUser("When should your validator start validating?\n" + - "If you validator is not ready by this time, subnet downtime can occur.") - } else { - ux.Logger.PrintToUser("When do you want to start delegating?\n") - } - defaultStartOption := "Start in " + ux.FormatDuration(defaultStakingStartLeadTime) - startTimeOptions := []string{defaultStartOption, custom} - startTimeOption, err := app.Prompt.CaptureList("Start time", startTimeOptions) - if err != nil { - return time.Time{}, 0, err - } - switch startTimeOption { - case defaultStartOption: - useDefaultStartTime = true - default: - start, err := promptStart() - if err != nil { - return time.Time{}, 0, err - } - startTimeStr = start.Format(constants.TimeParseLayout) - } - } - - var ( - err error - start time.Time - ) - if startTimeStr != "" { - start, err = time.Parse(constants.TimeParseLayout, startTimeStr) - if err != nil { - return time.Time{}, 0, err - } - if start.Before(time.Now().Add(constants.StakingMinimumLeadTime)) { - return time.Time{}, 0, fmt.Errorf("time should be at least %s in the future ", constants.StakingMinimumLeadTime) - } - } else { - start = time.Now().Add(defaultStakingStartLeadTime) - } - - // this sets either the global var duration or useDefaultDuration to enable repeated execution with - // state keeping from node cmds - if duration == 0 && !useDefaultDuration { - msg := "How long should your validator validate for?" - if !isValidator { - msg = "How long do you want to delegate for?" - } - const defaultDurationOption = "Until primary network validator expires" - durationOptions := []string{defaultDurationOption, custom} - durationOption, err := app.Prompt.CaptureList(msg, durationOptions) - if err != nil { - return time.Time{}, 0, err - } - switch durationOption { - case defaultDurationOption: - useDefaultDuration = true - default: - duration, err = PromptDuration(start, network) - if err != nil { - return time.Time{}, 0, err - } - } - } - - var selectedDuration time.Duration - if useDefaultDuration { - // avoid setting both globals useDefaultDuration and duration - selectedDuration, err = getMaxValidationTime(network, nodeID, start) - if err != nil { - return time.Time{}, 0, err - } - } else { - selectedDuration = duration - } - - return start, selectedDuration, nil +func promptWeightSubnetValidator() (uint64, error) { + txt := "What weight would you like to assign to the validator?" + return app.Prompt.CaptureWeight(txt) } -func promptStart() (time.Time, error) { - txt := "When should the validator start validating? Enter a UTC datetime in 'YYYY-MM-DD HH:MM:SS' format" - return app.Prompt.CaptureDate(txt) +// getValidatorBalanceFromPChain gets remaining balance of validator from p chain +func getValidatorBalanceFromPChain() (uint64, error) { + return 0, nil } -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) +// getBLSInfoFromPChain gets BLS public key and pop from info api +func getBLSInfoFromPChain() (string, string, error) { + return "", "", nil } -func getWeight() (uint64, error) { - // this sets either the global var weight or useDefaultWeight to enable repeated execution with - // state keeping from node cmds - if weight == 0 && !useDefaultWeight { - defaultWeight := fmt.Sprintf("Default (%d)", constants.DefaultStakeWeight) - 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: - useDefaultWeight = true - default: - weight, err = app.Prompt.CaptureWeight(txt) - if err != nil { - return 0, err - } - } - } - if useDefaultWeight { - return constants.DefaultStakeWeight, nil - } - return weight, nil +// getChangeAddrFromPChain gets validator change addr from info api +func getChangeAddrFromPChain() (string, error) { + return "", nil } diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index 8643e5605..504ebc301 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -71,6 +71,7 @@ var ( 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") + ErrLedgerOnDevnet = errors.New("key --ledger is not available for devnet operations") errMutuallyExlusiveSubnetFlags = errors.New("--subnet-only and --subnet-id are mutually exclusive") ) diff --git a/cmd/blockchaincmd/remove_validator.go b/cmd/blockchaincmd/remove_validator.go index 6f0a57776..d8aa93054 100644 --- a/cmd/blockchaincmd/remove_validator.go +++ b/cmd/blockchaincmd/remove_validator.go @@ -93,7 +93,17 @@ func removeValidator(_ *cobra.Command, args []string) error { switch network.Kind { case models.Local: - return removeFromLocal(blockchainName) + if nonSOV { + return removeFromLocalNonSOV(blockchainName) + } + case models.Devnet: + if useLedger { + return ErrLedgerOnDevnet + } + keyName, err = prompts.CaptureKeyName(app.Prompt, constants.PayTxsFeesMsg, app.GetKeyDir(), false) + if err != nil { + return err + } case models.Fuji: if !useLedger && keyName == "" { useLedger, keyName, err = prompts.GetKeyOrLedger(app.Prompt, constants.PayTxsFeesMsg, app.GetKeyDir(), false) @@ -154,6 +164,8 @@ func generateWarpMessageRemoveValidator(validationID [32]byte, nonce, weight uin } func removeValidatorSOV(deployer *subnet.PublicDeployer) error { + // TODO: check for number of validators + // return error if there is only 1 validator validationID := getValidationID() minNonce, err := getMinNonce(validationID) if err != nil { @@ -256,7 +268,7 @@ func removeValidatorNonSOV(deployer *subnet.PublicDeployer, network models.Netwo return err } -func removeFromLocal(blockchainName string) error { +func removeFromLocalNonSOV(blockchainName string) error { sc, err := app.LoadSidecar(blockchainName) if err != nil { return err From 0793c894d5ff6f160ac2c7a8d13a381e8245def1 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 26 Sep 2024 14:35:27 -0400 Subject: [PATCH 65/74] add flags add validator --- cmd/blockchaincmd/add_validator.go | 30 ++++++++++++++++----------- cmd/blockchaincmd/change_weight.go | 11 +++++----- cmd/blockchaincmd/deploy.go | 1 - cmd/blockchaincmd/remove_validator.go | 11 +++++----- 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/cmd/blockchaincmd/add_validator.go b/cmd/blockchaincmd/add_validator.go index 894ec1f96..2aaf6694c 100644 --- a/cmd/blockchaincmd/add_validator.go +++ b/cmd/blockchaincmd/add_validator.go @@ -79,6 +79,7 @@ Testnet or Mainnet.`, cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [fuji/devnet only]") cmd.Flags().StringVar(&nodeIDStr, "nodeID", "", "set the NodeID of the validator to add") cmd.Flags().Uint64Var(&weight, "weight", constants.BootstrapValidatorWeight, "set the staking weight of the validator to add") + cmd.Flags().Uint64Var(&balance, "balance", 0, "set the AVAX balance of the validator that will be used for continuous fee to P-Chain") cmd.Flags().BoolVarP(&useEwoq, "ewoq", "e", false, "use ewoq key [fuji/devnet only]") cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji/devnet)") cmd.Flags().StringSliceVar(&ledgerAddresses, "ledger-addrs", []string{}, "use the given ledger addresses") @@ -158,12 +159,11 @@ func CallAddValidator( switch network.Kind { case models.Devnet: - if useLedger { - return ErrLedgerOnDevnet - } - keyName, err = prompts.CaptureKeyName(app.Prompt, constants.PayTxsFeesMsg, app.GetKeyDir(), false) - if err != nil { - return err + if !useLedger && keyName == "" { + useLedger, keyName, err = prompts.GetKeyOrLedger(app.Prompt, constants.PayTxsFeesMsg, app.GetKeyDir(), false) + if err != nil { + return err + } } case models.Fuji: if !useLedger && keyName == "" { @@ -214,19 +214,25 @@ func CallAddValidator( return err } - balance, err := promptValidatorBalance() - if err != nil { - return err + if balance == 0 { + balance, err = promptValidatorBalance() + if err != nil { + return err + } } - changeAddr, err := getKeyForChangeOwner("", network) - if err != nil { - return err + if changeAddr == "" { + changeAddr, err = getKeyForChangeOwner("", network) + if err != nil { + return err + } } ux.Logger.PrintToUser("NodeID: %s", nodeID.String()) ux.Logger.PrintToUser("Network: %s", network.Name()) ux.Logger.PrintToUser("Weight: %d", weight) + ux.Logger.PrintToUser("Balance: %d", balance) + ux.Logger.PrintToUser("Change Address: %s", changeAddr) ux.Logger.PrintToUser("Inputs complete, issuing transaction to add the provided validator information...") //type RegisterSubnetValidatorTx struct { diff --git a/cmd/blockchaincmd/change_weight.go b/cmd/blockchaincmd/change_weight.go index a4d16d7a4..789e2d04f 100644 --- a/cmd/blockchaincmd/change_weight.go +++ b/cmd/blockchaincmd/change_weight.go @@ -85,12 +85,11 @@ func updateWeight(_ *cobra.Command, args []string) error { switch network.Kind { case models.Devnet: - if useLedger { - return ErrLedgerOnDevnet - } - keyName, err = prompts.CaptureKeyName(app.Prompt, constants.PayTxsFeesMsg, app.GetKeyDir(), false) - if err != nil { - return err + if !useLedger && keyName == "" { + useLedger, keyName, err = prompts.GetKeyOrLedger(app.Prompt, constants.PayTxsFeesMsg, app.GetKeyDir(), false) + if err != nil { + return err + } } case models.Fuji: if !useLedger && keyName == "" { diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index 504ebc301..8643e5605 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -71,7 +71,6 @@ var ( 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") - ErrLedgerOnDevnet = errors.New("key --ledger is not available for devnet operations") errMutuallyExlusiveSubnetFlags = errors.New("--subnet-only and --subnet-id are mutually exclusive") ) diff --git a/cmd/blockchaincmd/remove_validator.go b/cmd/blockchaincmd/remove_validator.go index d8aa93054..8413cce7c 100644 --- a/cmd/blockchaincmd/remove_validator.go +++ b/cmd/blockchaincmd/remove_validator.go @@ -97,12 +97,11 @@ func removeValidator(_ *cobra.Command, args []string) error { return removeFromLocalNonSOV(blockchainName) } case models.Devnet: - if useLedger { - return ErrLedgerOnDevnet - } - keyName, err = prompts.CaptureKeyName(app.Prompt, constants.PayTxsFeesMsg, app.GetKeyDir(), false) - if err != nil { - return err + if !useLedger && keyName == "" { + useLedger, keyName, err = prompts.GetKeyOrLedger(app.Prompt, constants.PayTxsFeesMsg, app.GetKeyDir(), false) + if err != nil { + return err + } } case models.Fuji: if !useLedger && keyName == "" { From 3dff1e09f58edf43a3ac8e95f5ec706634afa7fd Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 26 Sep 2024 16:25:38 -0400 Subject: [PATCH 66/74] remove validator update --- cmd/blockchaincmd/add_validator.go | 2 +- cmd/blockchaincmd/remove_validator.go | 88 +++++++++++++++++---------- 2 files changed, 56 insertions(+), 34 deletions(-) diff --git a/cmd/blockchaincmd/add_validator.go b/cmd/blockchaincmd/add_validator.go index 2aaf6694c..2e5f25541 100644 --- a/cmd/blockchaincmd/add_validator.go +++ b/cmd/blockchaincmd/add_validator.go @@ -83,7 +83,7 @@ Testnet or Mainnet.`, cmd.Flags().BoolVarP(&useEwoq, "ewoq", "e", false, "use ewoq key [fuji/devnet only]") cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji/devnet)") cmd.Flags().StringSliceVar(&ledgerAddresses, "ledger-addrs", []string{}, "use the given ledger addresses") - cmd.Flags().BoolVar(&nonSOV, "not-sov", false, "set to true if adding validator to a non SOV blockchain") + cmd.Flags().BoolVar(&nonSOV, "not-sov", false, "set to true if adding validator to a non-SOV blockchain") cmd.Flags().StringVar(&publicKey, "public-key", "", "set the BLS public key of the validator to add") cmd.Flags().StringVar(&pop, "proof-of-possession", "", "set the BLS proof of possession of the validator to add") cmd.Flags().StringVar(&changeAddr, "change-address", "", "P-Chain address that will receive any leftover AVAX from the validator when it is removed from Subnet") diff --git a/cmd/blockchaincmd/remove_validator.go b/cmd/blockchaincmd/remove_validator.go index 8413cce7c..22f095431 100644 --- a/cmd/blockchaincmd/remove_validator.go +++ b/cmd/blockchaincmd/remove_validator.go @@ -47,11 +47,11 @@ these prompts by providing the values with flags.`, networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, false, removeValidatorSupportedNetworkOptions) cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [fuji deploy only]") cmd.Flags().StringVar(&nodeIDStr, "nodeID", "", "set the NodeID of the validator to remove") - cmd.Flags().StringSliceVar(&subnetAuthKeys, "subnet-auth-keys", nil, "control keys that will be used to authenticate the removeValidator tx") - cmd.Flags().StringVar(&outputTxPath, "output-tx-path", "", "file path of the removeValidator tx") + cmd.Flags().StringSliceVar(&subnetAuthKeys, "subnet-auth-keys", nil, "(for non-SOV blockchain only) control keys that will be used to authenticate the removeValidator tx") + cmd.Flags().StringVar(&outputTxPath, "output-tx-path", "", "(for non-SOV blockchain only) file path of the removeValidator tx") cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji)") cmd.Flags().StringSliceVar(&ledgerAddresses, "ledger-addrs", []string{}, "use the given ledger addresses") - cmd.Flags().BoolVar(&nonSOV, "not-sov", false, "set to true if removing validator in a non SOV blockchain") + cmd.Flags().BoolVar(&nonSOV, "not-sov", false, "set to true if removing validator in a non-SOV blockchain") return cmd } @@ -71,12 +71,36 @@ func removeValidator(_ *cobra.Command, args []string) error { return err } + if nonSOV { + if outputTxPath != "" { + return errors.New("--output-tx-path flag cannot be used for non-SOV (Subnet-Only Validators) blockchains") + } + + if len(subnetAuthKeys) > 0 { + return errors.New("--subnetAuthKeys flag cannot be used for non-SOV (Subnet-Only Validators) blockchains") + } + } + if outputTxPath != "" { if _, err := os.Stat(outputTxPath); err == nil { return fmt.Errorf("outputTxPath %q already exists", outputTxPath) } } + var nodeID ids.NodeID + if nodeIDStr == "" { + nodeID, err = PromptNodeID("remove as validator") + if err != nil { + return err + } + nodeIDStr = nodeID.String() + } else { + nodeID, err = ids.NodeIDFromString(nodeIDStr) + if err != nil { + return err + } + } + if len(ledgerAddresses) > 0 { useLedger = true } @@ -138,11 +162,21 @@ func removeValidator(_ *cobra.Command, args []string) error { return errNoSubnetID } + // check that this guy actually is a validator on the subnet + isValidator, err := subnet.IsSubnetValidator(subnetID, nodeID, network) + if err != nil { + // just warn the user, don't fail + ux.Logger.PrintToUser("failed to check if node is a validator on the subnet: %s", err) + } else if !isValidator { + // this is actually an error + return fmt.Errorf("node %s is not a validator on subnet %s", nodeID, subnetID) + } + deployer := subnet.NewPublicDeployer(app, kc, network) if nonSOV { return removeValidatorNonSOV(deployer, network, subnetID, kc, blockchainName) } - return removeValidatorSOV(deployer) + return removeValidatorSOV(deployer, network, subnetID) } // TODO: implement getMinNonce @@ -153,7 +187,7 @@ func getMinNonce(validationID [32]byte) (uint64, error) { // TODO: implement getValidationID // get validation ID for a node from P Chain -func getValidationID() [32]byte { +func getValidationID(nodeID string) [32]byte { return [32]byte{} } @@ -162,14 +196,21 @@ func generateWarpMessageRemoveValidator(validationID [32]byte, nonce, weight uin return warpPlatformVM.Message{}, nil } -func removeValidatorSOV(deployer *subnet.PublicDeployer) error { - // TODO: check for number of validators - // return error if there is only 1 validator - validationID := getValidationID() +func removeValidatorSOV(deployer *subnet.PublicDeployer, network models.Network, subnetID ids.ID) error { + validators, err := subnet.GetPublicSubnetValidators(subnetID, network) + if err != nil { + return err + } + if len(validators) == 1 { + return fmt.Errorf("cannot remove the only validator left in blockchain") + } + + validationID := getValidationID(nodeIDStr) minNonce, err := getMinNonce(validationID) if err != nil { return err } + // automatically increase nonce by 1 message, err := generateWarpMessageRemoveValidator(validationID, minNonce+1, 0) if err != nil { return err @@ -183,8 +224,6 @@ func removeValidatorSOV(deployer *subnet.PublicDeployer) error { } func removeValidatorNonSOV(deployer *subnet.PublicDeployer, network models.Network, subnetID ids.ID, kc *keychain.Keychain, blockchainName string) error { - var nodeID ids.NodeID - isPermissioned, controlKeys, threshold, err := txutils.GetOwners(network, subnetID) if err != nil { return err @@ -216,32 +255,15 @@ func removeValidatorNonSOV(deployer *subnet.PublicDeployer, network models.Netwo } ux.Logger.PrintToUser("Your subnet auth keys for remove validator tx creation: %s", subnetAuthKeys) - if nodeIDStr == "" { - nodeID, err = PromptNodeID("remove as validator") - if err != nil { - return err - } - } else { - nodeID, err = ids.NodeIDFromString(nodeIDStr) - if err != nil { - return err - } - } + ux.Logger.PrintToUser("NodeID: %s", nodeIDStr) + ux.Logger.PrintToUser("Network: %s", network.Name()) + ux.Logger.PrintToUser("Inputs complete, issuing transaction to remove the specified validator...") - // check that this guy actually is a validator on the subnet - isValidator, err := subnet.IsSubnetValidator(subnetID, nodeID, network) + nodeID, err := ids.NodeIDFromString(nodeIDStr) if err != nil { - // just warn the user, don't fail - ux.Logger.PrintToUser("failed to check if node is a validator on the subnet: %s", err) - } else if !isValidator { - // this is actually an error - return fmt.Errorf("node %s is not a validator on subnet %s", nodeID, subnetID) + return err } - ux.Logger.PrintToUser("NodeID: %s", nodeID.String()) - ux.Logger.PrintToUser("Network: %s", network.Name()) - ux.Logger.PrintToUser("Inputs complete, issuing transaction to remove the specified validator...") - isFullySigned, tx, remainingSubnetAuthKeys, err := deployer.RemoveValidator( controlKeys, subnetAuthKeys, From b5b7ee26a8860a098911b4ab91bea59a2aff3195 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 26 Sep 2024 17:11:52 -0400 Subject: [PATCH 67/74] refactor addvalidator --- cmd/blockchaincmd/add_validator.go | 96 +++++++++------------------ cmd/blockchaincmd/change_weight.go | 10 +-- cmd/blockchaincmd/remove_validator.go | 2 +- cmd/nodecmd/validate_subnet.go | 1 + 4 files changed, 39 insertions(+), 70 deletions(-) diff --git a/cmd/blockchaincmd/add_validator.go b/cmd/blockchaincmd/add_validator.go index 2e5f25541..d45ae6df4 100644 --- a/cmd/blockchaincmd/add_validator.go +++ b/cmd/blockchaincmd/add_validator.go @@ -59,7 +59,7 @@ var ( // avalanche blockchain addValidator func newAddValidatorCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "addValidator [blockchainName]", + Use: "addValidator [blockchainName] [nodeID]", Short: "Allow a validator to validate your blockchain's subnet", Long: `The blockchain addValidator command whitelists a primary network validator to validate the subnet of the provided deployed Blockchain. @@ -72,12 +72,11 @@ these prompts by providing the values with flags. This command currently only works on Blockchains deployed to either the Fuji Testnet or Mainnet.`, RunE: addValidator, - Args: cobrautils.ExactArgs(1), + Args: cobrautils.ExactArgs(2), } networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, true, addValidatorSupportedNetworkOptions) cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [fuji/devnet only]") - cmd.Flags().StringVar(&nodeIDStr, "nodeID", "", "set the NodeID of the validator to add") cmd.Flags().Uint64Var(&weight, "weight", constants.BootstrapValidatorWeight, "set the staking weight of the validator to add") cmd.Flags().Uint64Var(&balance, "balance", 0, "set the AVAX balance of the validator that will be used for continuous fee to P-Chain") cmd.Flags().BoolVarP(&useEwoq, "ewoq", "e", false, "use ewoq key [fuji/devnet only]") @@ -92,6 +91,12 @@ Testnet or Mainnet.`, func addValidator(_ *cobra.Command, args []string) error { blockchainName := args[0] + _, err := ids.NodeIDFromString(args[1]) + if err != nil { + return err + } + nodeIDStr = args[1] + network, err := networkoptions.GetNetworkFromCmdLineFlags( app, "", @@ -124,11 +129,20 @@ func addValidator(_ *cobra.Command, args []string) error { return err } } + sc, err := app.LoadSidecar(blockchainName) + if err != nil { + return err + } + + subnetID := sc.Networks[network.Name()].SubnetID + if subnetID == ids.Empty { + return errNoSubnetID + } deployer := subnet.NewPublicDeployer(app, kc, network) if nonSOV { - return CallAddValidatorNonSOV(deployer, network, kc, useLedger, blockchainName, nodeIDStr, defaultValidatorParams, waitForTxAcceptance) + return CallAddValidatorNonSOV(deployer, network, kc, useLedger, blockchainName, nodeIDStr, defaultValidatorParams, waitForTxAcceptance, subnetID) } - return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeIDStr) + return CallAddValidator(deployer, network, kc, useLedger, blockchainName) } func promptValidatorBalance() (uint64, error) { @@ -143,16 +157,12 @@ func CallAddValidator( kc *keychain.Keychain, useLedgerSetting bool, blockchainName string, - nodeIDStr string, + nodeIDstr string, + subnetID ids.ID, ) error { - var ( - nodeID ids.NodeID - err error - ) - useLedger = useLedgerSetting - _, err = ValidateSubnetNameAndGetChains([]string{blockchainName}) + _, err := ValidateSubnetNameAndGetChains([]string{blockchainName}) if err != nil { return err } @@ -181,34 +191,12 @@ func CallAddValidator( return errors.New("unsupported network") } - sc, err := app.LoadSidecar(blockchainName) - if err != nil { - return err - } - - subnetID := sc.Networks[network.Name()].SubnetID - if subnetID == ids.Empty { - return errNoSubnetID - } - // TODO: implement getting validator manager controller address //kcKeys, err := kc.PChainFormattedStrAddresses() //if err != nil { // return err //} - if nodeIDStr == "" { - nodeID, err = PromptNodeID("add as Subnet validator") - if err != nil { - return err - } - } else { - nodeID, err = ids.NodeIDFromString(nodeIDStr) - if err != nil { - return err - } - } - publicKey, pop, err = promptProofOfPossession(publicKey == "", pop == "") if err != nil { return err @@ -228,7 +216,7 @@ func CallAddValidator( } } - ux.Logger.PrintToUser("NodeID: %s", nodeID.String()) + ux.Logger.PrintToUser("NodeID: %s", nodeIDStr) ux.Logger.PrintToUser("Network: %s", network.Name()) ux.Logger.PrintToUser("Weight: %d", weight) ux.Logger.PrintToUser("Balance: %d", balance) @@ -270,7 +258,7 @@ func CallAddValidator( Addrs: addrs, } // TODO: generate warp message - message, err := generateWarpMessageAddValidator() + message, err := generateWarpMessageAddValidator(subnetID) tx, err := deployer.RegisterSubnetValidator(balance, blsInfo, changeOwner, message) if err != nil { return err @@ -279,7 +267,7 @@ func CallAddValidator( return nil } -func generateWarpMessageAddValidator() (warpPlatformVM.Message, error) { +func generateWarpMessageAddValidator(SubnetID ids.ID, NodeID ids.NodeID, weight uint64, blsPublicKey string, expiry uint64) (warpPlatformVM.Message, error) { return warpPlatformVM.Message{}, nil } @@ -290,15 +278,15 @@ func CallAddValidatorNonSOV( useLedgerSetting bool, blockchainName string, nodeIDStr string, + subnetID ids.ID, defaultValidatorParamsSetting bool, waitForTxAcceptanceSetting bool, ) error { - var ( - nodeID ids.NodeID - start time.Time - err error - ) - + var start time.Time + nodeID, err := ids.NodeIDFromString(nodeIDStr) + if err != nil { + return err + } useLedger = useLedgerSetting defaultValidatorParams = defaultValidatorParamsSetting waitForTxAcceptance = waitForTxAcceptanceSetting @@ -330,16 +318,6 @@ func CallAddValidatorNonSOV( return err } - sc, err := app.LoadSidecar(blockchainName) - if err != nil { - return err - } - - subnetID := sc.Networks[network.Name()].SubnetID - if subnetID == ids.Empty { - return errNoSubnetID - } - isPermissioned, controlKeys, threshold, err := txutils.GetOwners(network, subnetID) if err != nil { return err @@ -366,18 +344,6 @@ func CallAddValidatorNonSOV( } ux.Logger.PrintToUser("Your subnet auth keys for add validator tx creation: %s", subnetAuthKeys) - if nodeIDStr == "" { - nodeID, err = PromptNodeID("add as validator") - if err != nil { - return err - } - } else { - nodeID, err = ids.NodeIDFromString(nodeIDStr) - if err != nil { - return err - } - } - selectedWeight, err := getWeight() if err != nil { return err diff --git a/cmd/blockchaincmd/change_weight.go b/cmd/blockchaincmd/change_weight.go index 789e2d04f..350e65b51 100644 --- a/cmd/blockchaincmd/change_weight.go +++ b/cmd/blockchaincmd/change_weight.go @@ -34,13 +34,10 @@ The Subnet has to be a Proof of Authority Subnet-Only Validator Subnet.`, cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [fuji/devnet only]") cmd.Flags().StringVar(&nodeIDStr, "nodeID", "", "set the NodeID of the validator to add") - cmd.Flags().Uint64Var(&weight, "weight", constants.BootstrapValidatorWeight, "set the staking weight of the validator to add") + cmd.Flags().Uint64Var(&weight, "weight", constants.BootstrapValidatorWeight, "set the new staking weight of the validator") cmd.Flags().BoolVarP(&useEwoq, "ewoq", "e", false, "use ewoq key [fuji/devnet only]") cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji/devnet)") cmd.Flags().StringSliceVar(&ledgerAddresses, "ledger-addrs", []string{}, "use the given ledger addresses") - cmd.Flags().BoolVar(&nonSOV, "not-sov", false, "set to true if adding validator to a non SOV blockchain") - cmd.Flags().StringVar(&publicKey, "public-key", "", "set the BLS public key of the validator to add") - cmd.Flags().StringVar(&pop, "proof-of-possession", "", "set the BLS proof of possession of the validator to add") return cmd } @@ -161,6 +158,11 @@ func updateWeight(_ *cobra.Command, args []string) error { return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeID) } +// TODO: implement checkIfSubnetIsSOV +// checkIfSubnetIsSOV returns true if Subnet is SOV from P Chain +func checkIfSubnetIsSOV() (bool, error) { + return false, nil +} func promptWeightSubnetValidator() (uint64, error) { txt := "What weight would you like to assign to the validator?" return app.Prompt.CaptureWeight(txt) diff --git a/cmd/blockchaincmd/remove_validator.go b/cmd/blockchaincmd/remove_validator.go index 22f095431..1c262db19 100644 --- a/cmd/blockchaincmd/remove_validator.go +++ b/cmd/blockchaincmd/remove_validator.go @@ -42,7 +42,7 @@ validating your deployed Blockchain. To remove the validator from the Subnet's allow list, provide the validator's unique NodeID. You can bypass these prompts by providing the values with flags.`, RunE: removeValidator, - Args: cobrautils.ExactArgs(1), + Args: cobrautils.ExactArgs(2), } networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, false, removeValidatorSupportedNetworkOptions) cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [fuji deploy only]") diff --git a/cmd/nodecmd/validate_subnet.go b/cmd/nodecmd/validate_subnet.go index 552115426..461e2e4e9 100644 --- a/cmd/nodecmd/validate_subnet.go +++ b/cmd/nodecmd/validate_subnet.go @@ -103,6 +103,7 @@ func addNodeAsSubnetValidator( useLedger, subnetName, nodeID, + subnetID, defaultValidatorParams, waitForTxAcceptance, ); err != nil { From d8e39178a960a79b57945a9f9a54a2a9ed8cca85 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 26 Sep 2024 17:37:53 -0400 Subject: [PATCH 68/74] use node id as arg in addvalidator --- cmd/blockchaincmd/add_validator.go | 49 ++++++++++++++++++++---------- cmd/nodecmd/validate_subnet.go | 1 - pkg/constants/constants.go | 11 ++++--- 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/cmd/blockchaincmd/add_validator.go b/cmd/blockchaincmd/add_validator.go index d45ae6df4..cde448993 100644 --- a/cmd/blockchaincmd/add_validator.go +++ b/cmd/blockchaincmd/add_validator.go @@ -129,20 +129,11 @@ func addValidator(_ *cobra.Command, args []string) error { return err } } - sc, err := app.LoadSidecar(blockchainName) - if err != nil { - return err - } - - subnetID := sc.Networks[network.Name()].SubnetID - if subnetID == ids.Empty { - return errNoSubnetID - } deployer := subnet.NewPublicDeployer(app, kc, network) if nonSOV { - return CallAddValidatorNonSOV(deployer, network, kc, useLedger, blockchainName, nodeIDStr, defaultValidatorParams, waitForTxAcceptance, subnetID) + return CallAddValidatorNonSOV(deployer, network, kc, useLedger, blockchainName, nodeIDStr, defaultValidatorParams, waitForTxAcceptance) } - return CallAddValidator(deployer, network, kc, useLedger, blockchainName) + return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeIDStr) } func promptValidatorBalance() (uint64, error) { @@ -157,8 +148,7 @@ func CallAddValidator( kc *keychain.Keychain, useLedgerSetting bool, blockchainName string, - nodeIDstr string, - subnetID ids.ID, + nodeIDStrFormat string, ) error { useLedger = useLedgerSetting @@ -191,6 +181,16 @@ func CallAddValidator( return errors.New("unsupported network") } + sc, err := app.LoadSidecar(blockchainName) + if err != nil { + return err + } + + subnetID := sc.Networks[network.Name()].SubnetID + if subnetID == ids.Empty { + return errNoSubnetID + } + // TODO: implement getting validator manager controller address //kcKeys, err := kc.PChainFormattedStrAddresses() //if err != nil { @@ -216,7 +216,7 @@ func CallAddValidator( } } - ux.Logger.PrintToUser("NodeID: %s", nodeIDStr) + ux.Logger.PrintToUser("NodeID: %s", nodeIDStrFormat) ux.Logger.PrintToUser("Network: %s", network.Name()) ux.Logger.PrintToUser("Weight: %d", weight) ux.Logger.PrintToUser("Balance: %d", balance) @@ -257,8 +257,16 @@ func CallAddValidator( Threshold: 1, Addrs: addrs, } + nodeID, err := ids.NodeIDFromString(nodeIDStrFormat) + if err != nil { + return err + } // TODO: generate warp message - message, err := generateWarpMessageAddValidator(subnetID) + // expiry is set to 48 hours from time of transaction + message, err := generateWarpMessageAddValidator(subnetID, nodeID, weight, publicKey, uint64(time.Now().Add(constants.DefaultValidationIDExpiryDuration).Unix())) + if err != nil { + return err + } tx, err := deployer.RegisterSubnetValidator(balance, blsInfo, changeOwner, message) if err != nil { return err @@ -278,7 +286,6 @@ func CallAddValidatorNonSOV( useLedgerSetting bool, blockchainName string, nodeIDStr string, - subnetID ids.ID, defaultValidatorParamsSetting bool, waitForTxAcceptanceSetting bool, ) error { @@ -318,6 +325,16 @@ func CallAddValidatorNonSOV( return err } + sc, err := app.LoadSidecar(blockchainName) + if err != nil { + return err + } + + subnetID := sc.Networks[network.Name()].SubnetID + if subnetID == ids.Empty { + return errNoSubnetID + } + isPermissioned, controlKeys, threshold, err := txutils.GetOwners(network, subnetID) if err != nil { return err diff --git a/cmd/nodecmd/validate_subnet.go b/cmd/nodecmd/validate_subnet.go index 461e2e4e9..552115426 100644 --- a/cmd/nodecmd/validate_subnet.go +++ b/cmd/nodecmd/validate_subnet.go @@ -103,7 +103,6 @@ func addNodeAsSubnetValidator( useLedger, subnetName, nodeID, - subnetID, defaultValidatorParams, waitForTxAcceptance, ); err != nil { diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 8486b6f62..6e2d3b62a 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -156,11 +156,12 @@ const ( // 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" + BootstrapValidatorWeight = 1000000 + DefaultStakeWeight = 20 + AVAXSymbol = "AVAX" + DefaultFujiStakeDuration = "48h" + DefaultMainnetStakeDuration = "336h" + DefaultValidationIDExpiryDuration = 48 * time.Hour // The absolute minimum is 25 seconds, but set to 1 minute to allow for // time to go through the command DevnetStakingStartLeadTime = 30 * time.Second From 048cb92209de1e4637ae72aeed06f4c4fbde1af5 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 26 Sep 2024 18:00:38 -0400 Subject: [PATCH 69/74] fix change weight arg --- cmd/blockchaincmd/change_weight.go | 33 +++++++++++----- cmd/blockchaincmd/remove_validator.go | 55 +++++++++------------------ 2 files changed, 41 insertions(+), 47 deletions(-) diff --git a/cmd/blockchaincmd/change_weight.go b/cmd/blockchaincmd/change_weight.go index 350e65b51..3e73a95ef 100644 --- a/cmd/blockchaincmd/change_weight.go +++ b/cmd/blockchaincmd/change_weight.go @@ -12,6 +12,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/networkoptions" "github.com/ava-labs/avalanche-cli/pkg/prompts" "github.com/ava-labs/avalanche-cli/pkg/subnet" + "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanchego/ids" "github.com/spf13/cobra" "os" @@ -28,12 +29,11 @@ func newChangeWeightCmd() *cobra.Command { The Subnet has to be a Proof of Authority Subnet-Only Validator Subnet.`, RunE: updateWeight, - Args: cobrautils.ExactArgs(1), + Args: cobrautils.ExactArgs(2), } networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, true, addValidatorSupportedNetworkOptions) cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [fuji/devnet only]") - cmd.Flags().StringVar(&nodeIDStr, "nodeID", "", "set the NodeID of the validator to add") cmd.Flags().Uint64Var(&weight, "weight", constants.BootstrapValidatorWeight, "set the new staking weight of the validator") cmd.Flags().BoolVarP(&useEwoq, "ewoq", "e", false, "use ewoq key [fuji/devnet only]") cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji/devnet)") @@ -43,16 +43,15 @@ The Subnet has to be a Proof of Authority Subnet-Only Validator Subnet.`, func updateWeight(_ *cobra.Command, args []string) error { blockchainName := args[0] - // TODO: validate node id format - nodeID := args[1] - var err error + _, err := ids.NodeIDFromString(args[1]) + if err != nil { + return err + } + nodeIDStr = args[1] //TODO: add check for non SOV subnet // return err if non SOV - // TODO: check for number of validators - // return error if there is only 1 validator - network, err := networkoptions.GetNetworkFromCmdLineFlags( app, "", @@ -123,10 +122,24 @@ func updateWeight(_ *cobra.Command, args []string) error { return errNoSubnetID } + nodeID, err := ids.NodeIDFromString(nodeIDStr) + if err != nil { + return err + } + + isValidator, err := subnet.IsSubnetValidator(subnetID, nodeID, network) + if err != nil { + // just warn the user, don't fail + ux.Logger.PrintToUser("failed to check if node is a validator on the subnet: %s", err) + } else if !isValidator { + // this is actually an error + return fmt.Errorf("node %s is not a validator on subnet %s", nodeID, subnetID) + } + deployer := subnet.NewPublicDeployer(app, kc, network) // first remove the validator from subnet - err = removeValidatorSOV(deployer) + err = removeValidatorSOV(deployer, network, subnetID, nodeID) if err != nil { return err } @@ -155,7 +168,7 @@ func updateWeight(_ *cobra.Command, args []string) error { } // add back validator to subnet with updated weight - return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeID) + return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeIDStr) } // TODO: implement checkIfSubnetIsSOV diff --git a/cmd/blockchaincmd/remove_validator.go b/cmd/blockchaincmd/remove_validator.go index 1c262db19..df6c077b7 100644 --- a/cmd/blockchaincmd/remove_validator.go +++ b/cmd/blockchaincmd/remove_validator.go @@ -46,7 +46,6 @@ these prompts by providing the values with flags.`, } networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, false, removeValidatorSupportedNetworkOptions) cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [fuji deploy only]") - cmd.Flags().StringVar(&nodeIDStr, "nodeID", "", "set the NodeID of the validator to remove") cmd.Flags().StringSliceVar(&subnetAuthKeys, "subnet-auth-keys", nil, "(for non-SOV blockchain only) control keys that will be used to authenticate the removeValidator tx") cmd.Flags().StringVar(&outputTxPath, "output-tx-path", "", "(for non-SOV blockchain only) file path of the removeValidator tx") cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji)") @@ -56,7 +55,12 @@ these prompts by providing the values with flags.`, } func removeValidator(_ *cobra.Command, args []string) error { - var err error + blockchainName := args[0] + _, err := ids.NodeIDFromString(args[1]) + if err != nil { + return err + } + nodeIDStr = args[1] network, err := networkoptions.GetNetworkFromCmdLineFlags( app, @@ -87,20 +91,6 @@ func removeValidator(_ *cobra.Command, args []string) error { } } - var nodeID ids.NodeID - if nodeIDStr == "" { - nodeID, err = PromptNodeID("remove as validator") - if err != nil { - return err - } - nodeIDStr = nodeID.String() - } else { - nodeID, err = ids.NodeIDFromString(nodeIDStr) - if err != nil { - return err - } - } - if len(ledgerAddresses) > 0 { useLedger = true } @@ -109,11 +99,10 @@ func removeValidator(_ *cobra.Command, args []string) error { return ErrMutuallyExlusiveKeyLedger } - chains, err := ValidateSubnetNameAndGetChains(args) + _, err = ValidateSubnetNameAndGetChains([]string{blockchainName}) if err != nil { return err } - blockchainName := chains[0] switch network.Kind { case models.Local: @@ -162,6 +151,10 @@ func removeValidator(_ *cobra.Command, args []string) error { return errNoSubnetID } + nodeID, err := ids.NodeIDFromString(nodeIDStr) + if err != nil { + return err + } // check that this guy actually is a validator on the subnet isValidator, err := subnet.IsSubnetValidator(subnetID, nodeID, network) if err != nil { @@ -174,9 +167,9 @@ func removeValidator(_ *cobra.Command, args []string) error { deployer := subnet.NewPublicDeployer(app, kc, network) if nonSOV { - return removeValidatorNonSOV(deployer, network, subnetID, kc, blockchainName) + return removeValidatorNonSOV(deployer, network, subnetID, kc, blockchainName, nodeID) } - return removeValidatorSOV(deployer, network, subnetID) + return removeValidatorSOV(deployer, network, subnetID, nodeID) } // TODO: implement getMinNonce @@ -187,7 +180,7 @@ func getMinNonce(validationID [32]byte) (uint64, error) { // TODO: implement getValidationID // get validation ID for a node from P Chain -func getValidationID(nodeID string) [32]byte { +func getValidationID(nodeID ids.NodeID) [32]byte { return [32]byte{} } @@ -196,7 +189,7 @@ func generateWarpMessageRemoveValidator(validationID [32]byte, nonce, weight uin return warpPlatformVM.Message{}, nil } -func removeValidatorSOV(deployer *subnet.PublicDeployer, network models.Network, subnetID ids.ID) error { +func removeValidatorSOV(deployer *subnet.PublicDeployer, network models.Network, subnetID ids.ID, nodeID ids.NodeID) error { validators, err := subnet.GetPublicSubnetValidators(subnetID, network) if err != nil { return err @@ -205,7 +198,7 @@ func removeValidatorSOV(deployer *subnet.PublicDeployer, network models.Network, return fmt.Errorf("cannot remove the only validator left in blockchain") } - validationID := getValidationID(nodeIDStr) + validationID := getValidationID(nodeID) minNonce, err := getMinNonce(validationID) if err != nil { return err @@ -223,7 +216,7 @@ func removeValidatorSOV(deployer *subnet.PublicDeployer, network models.Network, return nil } -func removeValidatorNonSOV(deployer *subnet.PublicDeployer, network models.Network, subnetID ids.ID, kc *keychain.Keychain, blockchainName string) error { +func removeValidatorNonSOV(deployer *subnet.PublicDeployer, network models.Network, subnetID ids.ID, kc *keychain.Keychain, blockchainName string, nodeID ids.NodeID) error { isPermissioned, controlKeys, threshold, err := txutils.GetOwners(network, subnetID) if err != nil { return err @@ -255,15 +248,10 @@ func removeValidatorNonSOV(deployer *subnet.PublicDeployer, network models.Netwo } ux.Logger.PrintToUser("Your subnet auth keys for remove validator tx creation: %s", subnetAuthKeys) - ux.Logger.PrintToUser("NodeID: %s", nodeIDStr) + ux.Logger.PrintToUser("NodeID: %s", nodeID.String()) ux.Logger.PrintToUser("Network: %s", network.Name()) ux.Logger.PrintToUser("Inputs complete, issuing transaction to remove the specified validator...") - nodeID, err := ids.NodeIDFromString(nodeIDStr) - if err != nil { - return err - } - isFullySigned, tx, remainingSubnetAuthKeys, err := deployer.RemoveValidator( controlKeys, subnetAuthKeys, @@ -312,13 +300,6 @@ func removeFromLocalNonSOV(blockchainName string) error { validatorList[i] = v.NodeID.String() } - if nodeIDStr == "" { - nodeIDStr, err = app.Prompt.CaptureList("Choose a validator to remove", validatorList) - if err != nil { - return err - } - } - // Convert NodeID string to NodeID type nodeID, err := ids.NodeIDFromString(nodeIDStr) if err != nil { From b14481bf1fb2829a40b7e7791e3c7ceb381d49b3 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 26 Sep 2024 18:25:23 -0400 Subject: [PATCH 70/74] resolve merge conflict --- pkg/vm/create_custom.go | 4 ++-- pkg/vm/create_evm.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/vm/create_custom.go b/pkg/vm/create_custom.go index 3d0d66bdf..358820778 100644 --- a/pkg/vm/create_custom.go +++ b/pkg/vm/create_custom.go @@ -26,7 +26,7 @@ func CreateCustomSidecar( customVMBuildScript string, vmPath string, tokenSymbol string, -) error { +) (*models.Sidecar, error) { ux.Logger.PrintToUser("creating custom VM subnet %s", subnetName) if sc == nil { @@ -83,7 +83,7 @@ func CreateCustomSidecar( sc.RPCVersion = rpcVersion - return nil + return sc, nil } func LoadCustomGenesis(app *application.Avalanche, genesisPath string) ([]byte, error) { diff --git a/pkg/vm/create_evm.go b/pkg/vm/create_evm.go index 67336d76d..68e0305c9 100644 --- a/pkg/vm/create_evm.go +++ b/pkg/vm/create_evm.go @@ -38,7 +38,7 @@ func CreateEvmSidecar( subnetEVMVersion string, tokenSymbol string, getRPCVersionFromBinary bool, -) error { +) (*models.Sidecar, error) { var ( err error rpcVersion int @@ -51,16 +51,16 @@ func CreateEvmSidecar( if getRPCVersionFromBinary { _, vmBin, err := binutils.SetupSubnetEVM(app, subnetEVMVersion) if err != nil { - return fmt.Errorf("failed to install subnet-evm: %w", err) + return nil, fmt.Errorf("failed to install subnet-evm: %w", err) } rpcVersion, err = GetVMBinaryProtocolVersion(vmBin) if err != nil { - return fmt.Errorf("unable to get RPC version: %w", err) + return nil, fmt.Errorf("unable to get RPC version: %w", err) } } else { rpcVersion, err = GetRPCProtocolVersion(app, models.SubnetEvm, subnetEVMVersion) if err != nil { - return err + return nil, err } } From b77683b178db4cd4a5745c9372924e365f1f23df Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 26 Sep 2024 18:27:07 -0400 Subject: [PATCH 71/74] fix lint --- pkg/subnet/public.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/subnet/public.go b/pkg/subnet/public.go index 48770d519..6b75bff40 100644 --- a/pkg/subnet/public.go +++ b/pkg/subnet/public.go @@ -6,9 +6,10 @@ import ( "context" "errors" "fmt" + "time" + "github.com/ava-labs/avalanchego/vms/platformvm/fx" "github.com/ava-labs/avalanchego/vms/platformvm/warp" - "time" "github.com/ava-labs/avalanchego/vms/platformvm/signer" @@ -433,7 +434,7 @@ func (d *PublicDeployer) ConvertSubnet( showLedgerSignatureMsg(d.kc.UsesLedger, d.kc.HasOnlyOneKey(), "ConvertSubnet transaction") var validatorManagerAddress []byte - //var validators []avalanchego.SubnetValidator + // var validators []avalanchego.SubnetValidator tx, err := d.createConvertSubnetTx(subnetAuthKeys, subnetID, chainID, validatorManagerAddress, wallet) if err != nil { @@ -609,7 +610,7 @@ func (d *PublicDeployer) createConvertSubnetTx( subnetID ids.ID, chainID ids.ID, address []byte, - //validators []avalanchego.SubnetValidator, + // validators []avalanchego.SubnetValidator, wallet primary.Wallet, ) (*txs.Tx, error) { //fxIDs := make([]ids.ID, 0) From 031128cf1aaf0877ca71571348f02e42a1250cf4 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Tue, 8 Oct 2024 12:47:41 -0400 Subject: [PATCH 72/74] address comments --- cmd/blockchaincmd/add_validator.go | 26 +++++++++----- cmd/blockchaincmd/change_weight.go | 32 +++++++++-------- cmd/blockchaincmd/deploy.go | 2 +- cmd/blockchaincmd/export_test.go | 1 - cmd/blockchaincmd/remove_validator.go | 28 ++++++++++----- pkg/prompts/prompts.go | 2 +- pkg/prompts/validations.go | 6 ++-- pkg/subnet/public.go | 51 --------------------------- 8 files changed, 58 insertions(+), 90 deletions(-) diff --git a/cmd/blockchaincmd/add_validator.go b/cmd/blockchaincmd/add_validator.go index cde448993..7dbc15a68 100644 --- a/cmd/blockchaincmd/add_validator.go +++ b/cmd/blockchaincmd/add_validator.go @@ -59,7 +59,7 @@ var ( // avalanche blockchain addValidator func newAddValidatorCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "addValidator [blockchainName] [nodeID]", + Use: "addValidator [blockchainName]", Short: "Allow a validator to validate your blockchain's subnet", Long: `The blockchain addValidator command whitelists a primary network validator to validate the subnet of the provided deployed Blockchain. @@ -72,7 +72,7 @@ these prompts by providing the values with flags. This command currently only works on Blockchains deployed to either the Fuji Testnet or Mainnet.`, RunE: addValidator, - Args: cobrautils.ExactArgs(2), + Args: cobrautils.ExactArgs(1), } networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, true, addValidatorSupportedNetworkOptions) @@ -82,20 +82,20 @@ Testnet or Mainnet.`, cmd.Flags().BoolVarP(&useEwoq, "ewoq", "e", false, "use ewoq key [fuji/devnet only]") cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji/devnet)") cmd.Flags().StringSliceVar(&ledgerAddresses, "ledger-addrs", []string{}, "use the given ledger addresses") - cmd.Flags().BoolVar(&nonSOV, "not-sov", false, "set to true if adding validator to a non-SOV blockchain") - cmd.Flags().StringVar(&publicKey, "public-key", "", "set the BLS public key of the validator to add") - cmd.Flags().StringVar(&pop, "proof-of-possession", "", "set the BLS proof of possession of the validator to add") + cmd.Flags().BoolVar(&sovereign, "sovereign", true, "set to false if adding validator to a non-sovereign blockchain") + cmd.Flags().StringVar(&nodeIDStr, "node-id", "", "node-id of the validator to add") + cmd.Flags().StringVar(&publicKey, "bls-public-key", "", "set the BLS public key of the validator to add") + cmd.Flags().StringVar(&pop, "bls-proof-of-possession", "", "set the BLS proof of possession of the validator to add") cmd.Flags().StringVar(&changeAddr, "change-address", "", "P-Chain address that will receive any leftover AVAX from the validator when it is removed from Subnet") return cmd } func addValidator(_ *cobra.Command, args []string) error { blockchainName := args[0] - _, err := ids.NodeIDFromString(args[1]) + err := prompts.ValidateNodeID(nodeIDStr) if err != nil { return err } - nodeIDStr = args[1] network, err := networkoptions.GetNetworkFromCmdLineFlags( app, @@ -124,13 +124,13 @@ func addValidator(_ *cobra.Command, args []string) error { return err } network.HandlePublicNetworkSimulation() - if nonSOV { + if !sovereign { if err := UpdateKeychainWithSubnetControlKeys(kc, network, blockchainName); err != nil { return err } } deployer := subnet.NewPublicDeployer(app, kc, network) - if nonSOV { + if !sovereign { return CallAddValidatorNonSOV(deployer, network, kc, useLedger, blockchainName, nodeIDStr, defaultValidatorParams, waitForTxAcceptance) } return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeIDStr) @@ -197,6 +197,14 @@ func CallAddValidator( // return err //} + if nodeIDStr == "" { + nodeID, err := PromptNodeID("add as a blockchain validator") + if err != nil { + return err + } + nodeIDStr = nodeID.String() + } + publicKey, pop, err = promptProofOfPossession(publicKey == "", pop == "") if err != nil { return err diff --git a/cmd/blockchaincmd/change_weight.go b/cmd/blockchaincmd/change_weight.go index 3e73a95ef..7d0f88c37 100644 --- a/cmd/blockchaincmd/change_weight.go +++ b/cmd/blockchaincmd/change_weight.go @@ -18,24 +18,23 @@ import ( "os" ) -var () - // avalanche blockchain addValidator func newChangeWeightCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "changeWeight [blockchainName] [nodeID]", + Use: "changeWeight [blockchainName]", Short: "Changes the weight of a Subnet validator", Long: `The blockchain changeWeight command changes the weight of a Subnet Validator. The Subnet has to be a Proof of Authority Subnet-Only Validator Subnet.`, RunE: updateWeight, - Args: cobrautils.ExactArgs(2), + Args: cobrautils.ExactArgs(1), } networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, true, addValidatorSupportedNetworkOptions) cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [fuji/devnet only]") cmd.Flags().Uint64Var(&weight, "weight", constants.BootstrapValidatorWeight, "set the new staking weight of the validator") cmd.Flags().BoolVarP(&useEwoq, "ewoq", "e", false, "use ewoq key [fuji/devnet only]") + cmd.Flags().StringVar(&nodeIDStr, "node-id", "", "node-id of the validator") cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji/devnet)") cmd.Flags().StringSliceVar(&ledgerAddresses, "ledger-addrs", []string{}, "use the given ledger addresses") return cmd @@ -43,11 +42,10 @@ The Subnet has to be a Proof of Authority Subnet-Only Validator Subnet.`, func updateWeight(_ *cobra.Command, args []string) error { blockchainName := args[0] - _, err := ids.NodeIDFromString(args[1]) + err := prompts.ValidateNodeID(nodeIDStr) if err != nil { return err } - nodeIDStr = args[1] //TODO: add check for non SOV subnet // return err if non SOV @@ -122,9 +120,17 @@ func updateWeight(_ *cobra.Command, args []string) error { return errNoSubnetID } - nodeID, err := ids.NodeIDFromString(nodeIDStr) - if err != nil { - return err + var nodeID ids.NodeID + if nodeIDStr == "" { + nodeID, err = PromptNodeID("add as a blockchain validator") + if err != nil { + return err + } + } else { + nodeID, err = ids.NodeIDFromString(nodeIDStr) + if err != nil { + return err + } } isValidator, err := subnet.IsSubnetValidator(subnetID, nodeID, network) @@ -147,7 +153,7 @@ func updateWeight(_ *cobra.Command, args []string) error { // TODO: we need to wait for the balance from the removed validator to arrive in changeAddr // set arbitrary time.sleep here? - weight, err = promptWeightSubnetValidator() + weight, err = app.Prompt.CaptureWeight("What weight would you like to assign to the validator?") if err != nil { return err } @@ -168,7 +174,7 @@ func updateWeight(_ *cobra.Command, args []string) error { } // add back validator to subnet with updated weight - return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeIDStr) + return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeID.String()) } // TODO: implement checkIfSubnetIsSOV @@ -176,10 +182,6 @@ func updateWeight(_ *cobra.Command, args []string) error { func checkIfSubnetIsSOV() (bool, error) { return false, nil } -func promptWeightSubnetValidator() (uint64, error) { - txt := "What weight would you like to assign to the validator?" - return app.Prompt.CaptureWeight(txt) -} // getValidatorBalanceFromPChain gets remaining balance of validator from p chain func getValidatorBalanceFromPChain() (uint64, error) { diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index 8643e5605..dd3396525 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -58,7 +58,7 @@ var ( useLedger bool useEwoq bool ledgerAddresses []string - nonSOV bool + sovereign bool subnetIDStr string mainnetChainID uint32 skipCreatePrompt bool diff --git a/cmd/blockchaincmd/export_test.go b/cmd/blockchaincmd/export_test.go index b0dfd7cd2..84a3a1188 100644 --- a/cmd/blockchaincmd/export_test.go +++ b/cmd/blockchaincmd/export_test.go @@ -12,7 +12,6 @@ import ( "github.com/ava-labs/avalanche-cli/internal/mocks" "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/avalanche-cli/pkg/vm" diff --git a/cmd/blockchaincmd/remove_validator.go b/cmd/blockchaincmd/remove_validator.go index df6c077b7..f10225b58 100644 --- a/cmd/blockchaincmd/remove_validator.go +++ b/cmd/blockchaincmd/remove_validator.go @@ -34,7 +34,7 @@ var removeValidatorSupportedNetworkOptions = []networkoptions.NetworkOption{ // avalanche blockchain removeValidator func newRemoveValidatorCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "removeValidator [blockchainName] [nodeID]", + Use: "removeValidator [blockchainName]", Short: "Remove a permissioned validator from your blockchain's subnet", Long: `The blockchain removeValidator command stops a whitelisted, subnet network validator from validating your deployed Blockchain. @@ -42,7 +42,7 @@ validating your deployed Blockchain. To remove the validator from the Subnet's allow list, provide the validator's unique NodeID. You can bypass these prompts by providing the values with flags.`, RunE: removeValidator, - Args: cobrautils.ExactArgs(2), + Args: cobrautils.ExactArgs(1), } networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, false, removeValidatorSupportedNetworkOptions) cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [fuji deploy only]") @@ -50,7 +50,8 @@ these prompts by providing the values with flags.`, cmd.Flags().StringVar(&outputTxPath, "output-tx-path", "", "(for non-SOV blockchain only) file path of the removeValidator tx") cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji)") cmd.Flags().StringSliceVar(&ledgerAddresses, "ledger-addrs", []string{}, "use the given ledger addresses") - cmd.Flags().BoolVar(&nonSOV, "not-sov", false, "set to true if removing validator in a non-SOV blockchain") + cmd.Flags().StringVar(&nodeIDStr, "node-id", "", "node-id of the validator") + cmd.Flags().BoolVar(&sovereign, "sovereign", true, "set to false if removing validator in a non-sovereign blockchain") return cmd } @@ -75,7 +76,7 @@ func removeValidator(_ *cobra.Command, args []string) error { return err } - if nonSOV { + if !sovereign { if outputTxPath != "" { return errors.New("--output-tx-path flag cannot be used for non-SOV (Subnet-Only Validators) blockchains") } @@ -106,7 +107,7 @@ func removeValidator(_ *cobra.Command, args []string) error { switch network.Kind { case models.Local: - if nonSOV { + if !sovereign { return removeFromLocalNonSOV(blockchainName) } case models.Devnet: @@ -151,10 +152,19 @@ func removeValidator(_ *cobra.Command, args []string) error { return errNoSubnetID } - nodeID, err := ids.NodeIDFromString(nodeIDStr) - if err != nil { - return err + var nodeID ids.NodeID + if nodeIDStr == "" { + nodeID, err = PromptNodeID("remove as a blockchain validator") + if err != nil { + return err + } + } else { + nodeID, err = ids.NodeIDFromString(nodeIDStr) + if err != nil { + return err + } } + // check that this guy actually is a validator on the subnet isValidator, err := subnet.IsSubnetValidator(subnetID, nodeID, network) if err != nil { @@ -166,7 +176,7 @@ func removeValidator(_ *cobra.Command, args []string) error { } deployer := subnet.NewPublicDeployer(app, kc, network) - if nonSOV { + if !sovereign { return removeValidatorNonSOV(deployer, network, subnetID, kc, blockchainName, nodeID) } return removeValidatorSOV(deployer, network, subnetID, nodeID) diff --git a/pkg/prompts/prompts.go b/pkg/prompts/prompts.go index b2f267e18..674798724 100644 --- a/pkg/prompts/prompts.go +++ b/pkg/prompts/prompts.go @@ -258,7 +258,7 @@ func (*realPrompter) CaptureID(promptStr string) (ids.ID, error) { func (*realPrompter) CaptureNodeID(promptStr string) (ids.NodeID, error) { prompt := promptui.Prompt{ Label: promptStr, - Validate: validateNodeID, + Validate: ValidateNodeID, } nodeIDStr, err := prompt.Run() diff --git a/pkg/prompts/validations.go b/pkg/prompts/validations.go index b296a4264..38f2d2d35 100644 --- a/pkg/prompts/validations.go +++ b/pkg/prompts/validations.go @@ -83,7 +83,7 @@ func validateTime(input string) error { return err } -func validateNodeID(input string) error { +func ValidateNodeID(input string) error { _, err := ids.NodeIDFromString(input) return err } @@ -134,8 +134,8 @@ func validateValidatorBalance(input string) error { if err != nil { return err } - if val == 0 { - return fmt.Errorf("subnet validator balance must be greater than 0 AVAX") + if val < 1 { + return fmt.Errorf("subnet validator balance must be at least 1 AVAX") } return nil } diff --git a/pkg/subnet/public.go b/pkg/subnet/public.go index 6b75bff40..9a608f5e4 100644 --- a/pkg/subnet/public.go +++ b/pkg/subnet/public.go @@ -114,30 +114,9 @@ func (d *PublicDeployer) AddValidatorNonSOV( return false, tx, remainingSubnetAuthKeys, nil } -// type SetSubnetValidatorWeightTx struct { -// // Metadata, inputs and outputs -// BaseTx -// // AddressedCall with Payload: -// // - ValidationID (SHA256 of the AddressedCall Payload of the RegisterSubnetValidatorTx adding the validator) -// // - Nonce -// // - Weight -// Message warp.Message `json:"message"` -// } func (d *PublicDeployer) SetSubnetValidatorWeight( message warp.Message, ) (*txs.Tx, error) { - // create tx - //unsignedTx, err := wallet.P().Builder().NewSetSubnetValidatorWeightTx(args...) - //if err != nil { - // return nil, fmt.Errorf("error building tx: %w", err) - //} - //tx := txs.Tx{Unsigned: unsignedTx} - // sign with current wallet that contains EVM address controlling POA Validator Manager - // TODO: change code below - //if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { - // return nil, fmt.Errorf("error signing tx: %w", err) - //} - //return &tx, nil return nil, nil } @@ -147,18 +126,6 @@ func (d *PublicDeployer) RegisterSubnetValidator( changeOwner fx.Owner, message warp.Message, ) (*txs.Tx, error) { - // create tx - //unsignedTx, err := wallet.P().Builder().NewRegisterSubnetValidatorTx(args...) - //if err != nil { - // return nil, fmt.Errorf("error building tx: %w", err) - //} - //tx := txs.Tx{Unsigned: unsignedTx} - // sign with current wallet that contains EVM address controlling POA Validator Manager - // TODO: change code below - //if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { - // return nil, fmt.Errorf("error signing tx: %w", err) - //} - //return &tx, nil return nil, nil } @@ -613,24 +580,6 @@ func (d *PublicDeployer) createConvertSubnetTx( // validators []avalanchego.SubnetValidator, wallet primary.Wallet, ) (*txs.Tx, error) { - //fxIDs := make([]ids.ID, 0) - //options := d.getMultisigTxOptions(subnetAuthKeys) - //unsignedTx, err := wallet.P().Builder().NewConvertSubnetTx( - // subnetID, - //chainID, - //address, - //validators, - // fxIDs, - // options..., - //) - //if err != nil { - // return nil, fmt.Errorf("error building tx: %w", err) - //} - //tx := txs.Tx{Unsigned: unsignedTx} - //if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { - // return nil, fmt.Errorf("error signing tx: %w", err) - //} - //return &tx, nil return nil, nil } From 136fd35bf2c1ffbb4a8c3081067a34fa3b6ec8f3 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Tue, 8 Oct 2024 13:15:34 -0400 Subject: [PATCH 73/74] fix lint --- cmd/blockchaincmd/add_validator.go | 24 +----------------------- cmd/blockchaincmd/change_weight.go | 12 ++---------- cmd/blockchaincmd/deploy.go | 22 ---------------------- 3 files changed, 3 insertions(+), 55 deletions(-) diff --git a/cmd/blockchaincmd/add_validator.go b/cmd/blockchaincmd/add_validator.go index 7dbc15a68..39b8a0ecc 100644 --- a/cmd/blockchaincmd/add_validator.go +++ b/cmd/blockchaincmd/add_validator.go @@ -231,28 +231,6 @@ func CallAddValidator( ux.Logger.PrintToUser("Change Address: %s", changeAddr) ux.Logger.PrintToUser("Inputs complete, issuing transaction to add the provided validator information...") - //type RegisterSubnetValidatorTx struct { - // // Metadata, inputs and outputs - // BaseTx - // // Balance <= sum($AVAX inputs) - sum($AVAX outputs) - TxFee. - // Balance uint64 `json:"balance"` - // // [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 does uniquely map to a BLS key - // Signer signer.Signer `json:"signer"` - // // Leftover $AVAX from the Subnet Validator's Balance will be issued to - // // this owner after it is removed from the validator set. - // ChangeOwner fx.Owner `json:"changeOwner"` - // // AddressedCall with Payload: - // // - SubnetID - // // - NodeID (must be Ed25519 NodeID) - // // - Weight - // // - BLS public key - // // - Expiry - // Message warp.Message `json:"message"` - //} - blsInfo, err := getBLSInfo(publicKey, pop) if err != nil { return fmt.Errorf("failure parsing BLS info: %w", err) @@ -283,7 +261,7 @@ func CallAddValidator( return nil } -func generateWarpMessageAddValidator(SubnetID ids.ID, NodeID ids.NodeID, weight uint64, blsPublicKey string, expiry uint64) (warpPlatformVM.Message, error) { +func generateWarpMessageAddValidator(subnetID ids.ID, NodeID ids.NodeID, weight uint64, blsPublicKey string, expiry uint64) (warpPlatformVM.Message, error) { return warpPlatformVM.Message{}, nil } diff --git a/cmd/blockchaincmd/change_weight.go b/cmd/blockchaincmd/change_weight.go index 7d0f88c37..7d86d9726 100644 --- a/cmd/blockchaincmd/change_weight.go +++ b/cmd/blockchaincmd/change_weight.go @@ -5,6 +5,8 @@ package blockchaincmd import ( "errors" "fmt" + "os" + "github.com/ava-labs/avalanche-cli/pkg/cobrautils" "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/keychain" @@ -15,7 +17,6 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanchego/ids" "github.com/spf13/cobra" - "os" ) // avalanche blockchain addValidator @@ -47,9 +48,6 @@ func updateWeight(_ *cobra.Command, args []string) error { return err } - //TODO: add check for non SOV subnet - // return err if non SOV - network, err := networkoptions.GetNetworkFromCmdLineFlags( app, "", @@ -177,12 +175,6 @@ func updateWeight(_ *cobra.Command, args []string) error { return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeID.String()) } -// TODO: implement checkIfSubnetIsSOV -// checkIfSubnetIsSOV returns true if Subnet is SOV from P Chain -func checkIfSubnetIsSOV() (bool, error) { - return false, nil -} - // getValidatorBalanceFromPChain gets remaining balance of validator from p chain func getValidatorBalanceFromPChain() (uint64, error) { return 0, nil diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index dd3396525..87d5479ac 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -600,28 +600,6 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { } } - // type ConvertSubnetTx struct { - // // Metadata, inputs and outputs - // BaseTx - // // ID of the Subnet to transform - // // Restrictions: - // // - Must not be the Primary Network ID - // Subnet ids.ID `json:"subnetID"` - // // BlockchainID where the Subnet manager lives - // ChainID ids.ID `json:"chainID"` - // // Address of the Subnet manager - // Address []byte `json:"address"` - // // Initial pay-as-you-go validators for the Subnet - // Validators []SubnetValidator `json:"validators"` - // // Authorizes this conversion - // SubnetAuth verify.Verifiable `json:"subnetAuthorization"` - // } - - //avaGoBootstrapValidators, err := convertToAvalancheGoSubnetValidator(bootstrapValidators) - //if err != nil { - // return err - //} - // TODO: replace with avalanchego subnetValidators once implemented isFullySigned, convertSubnetTxID, tx, remainingSubnetAuthKeys, err := deployer.ConvertSubnet( controlKeys, subnetAuthKeys, From b60d4ad31c1c8ddea763e5ea0224c615b5e24082 Mon Sep 17 00:00:00 2001 From: sukantoraymond Date: Tue, 8 Oct 2024 15:58:20 -0400 Subject: [PATCH 74/74] Enable non sov subnets (#2203) * subnet create non sov * support non sov blockchain * update test * update tests * update tests * update test * update tests * update tests * fix test * update tests * Rename Subnet to L1 for new commands ACP77 (#2209) * rename convert subnet * rename to l1 * fix lint * fix lint * fix lint * fix lint * Integrate ConvertSubnetTx AvalancheGo (#2213) * complete implementation of avalanche go convertsubnet * update relayer * update convertsubnettx * update avalanchego * update avalanche go * update avalanche go * dont prompt control key for sov * add tx cases for convert subnet tx (#2218) * Poa integration (#2212) * use latest contract * point to latest convert subnet avago PR * nit * use fixed anr * nit * add subnet conversion id calculation * created unsigned warp message * fixed local network deploy * added signing * now this is working * using latest avago master + awm relayer * working pretty well * ready * lint * address PR comments * nit --------- Signed-off-by: sukantoraymond Co-authored-by: sukantoraymond * fix lint * reverse sidecar sov logic * fix merge * add poa setup to blockchain deploy (#2219) * add setup poa to blockchain deploy * add command * add proposervm update * proposervm flag for nodes * nit * nit * fixed dynamic fees params calculation * only adding tracked apis to apis in sidecar * improve prompts * fixing various stuff * add upgrade file * use anr etna enabled * keep upgrade on sync * workin * almost working * working1 * Update cmd/keycmd/transfer.go Co-authored-by: Meaghan FitzGerald Signed-off-by: felipemadero * fix some lint * address PR comments * missing file * nit --------- Signed-off-by: felipemadero Signed-off-by: sukantoraymond Co-authored-by: Meaghan FitzGerald Co-authored-by: sukantoraymond --------- Signed-off-by: sukantoraymond Signed-off-by: felipemadero Co-authored-by: felipemadero Co-authored-by: Meaghan FitzGerald * fix lint --------- Signed-off-by: sukantoraymond Signed-off-by: felipemadero Co-authored-by: felipemadero Co-authored-by: Meaghan FitzGerald --- cmd/blockchaincmd/add_validator.go | 4 +- cmd/blockchaincmd/change_weight.go | 4 +- cmd/blockchaincmd/convert.go | 428 ---------------- cmd/blockchaincmd/create.go | 38 +- cmd/blockchaincmd/deploy.go | 189 ++++++-- cmd/blockchaincmd/describe.go | 13 + cmd/blockchaincmd/export_test.go | 1 + cmd/blockchaincmd/prompt_genesis_input.go | 1 + cmd/blockchaincmd/remove_validator.go | 2 +- cmd/blockchaincmd/upgradecmd/apply.go | 2 +- cmd/contractcmd/contract.go | 2 + cmd/contractcmd/init_poa_validator_manager.go | 132 +++++ cmd/nodecmd/create_devnet.go | 15 +- cmd/nodecmd/helpers.go | 19 +- cmd/nodecmd/sync.go | 2 +- cmd/nodecmd/upgrade.json | 16 + go.mod | 47 +- go.sum | 94 ++-- internal/mocks/binary_checker.go | 2 +- internal/mocks/downloader.go | 2 +- internal/mocks/info.go | 24 +- internal/mocks/installer.go | 2 +- internal/mocks/network.go | 2 +- internal/mocks/plugin_binary_downloader.go | 2 +- internal/mocks/process_checker.go | 2 +- internal/mocks/prompter.go | 173 ++++++- internal/mocks/publisher.go | 2 +- pkg/application/app.go | 4 +- pkg/constants/constants.go | 7 +- pkg/contract/contract.go | 61 +++ pkg/evm/evm.go | 158 +++++- pkg/models/sidecar.go | 2 + pkg/prompts/prompts.go | 8 +- pkg/prompts/validations.go | 7 + pkg/remoteconfig/avalanche.go | 53 +- .../templates/avalanche-node.tmpl | 4 + pkg/ssh/ssh.go | 15 + pkg/subnet/public.go | 128 ++++- pkg/teleporter/relayer.go | 10 +- pkg/txutils/auth.go | 2 + pkg/txutils/info.go | 4 + pkg/ux/progressbar.go | 47 ++ pkg/ux/spinner.go | 2 + ...eployed_poa_validator_manager_bytecode.txt | 2 +- pkg/validatormanager/validatormanager.go | 245 +++++++++- pkg/vm/create_custom.go | 3 +- pkg/vm/create_evm.go | 3 +- pkg/vm/precompiles.go | 3 +- scripts/regenerate_mocks.sh | 8 +- sdk/interchain/signature-aggregator.go | 23 +- tests/e2e/commands/subnet.go | 349 ++++++++++++- tests/e2e/e2e_test.go | 9 +- tests/e2e/testcases/errhandling/suite.go | 17 +- tests/e2e/testcases/network/suite.go | 95 +++- tests/e2e/testcases/packageman/suite.go | 156 +++++- .../testcases/subnet/non-sov/local/suite.go | 366 ++++++++++++++ .../subnet/{ => non-sov}/public/suite.go | 20 +- .../testcases/subnet/{ => sov}/local/suite.go | 68 +-- .../e2e/testcases/subnet/sov/public/suite.go | 419 ++++++++++++++++ tests/e2e/testcases/subnet/suite.go | 25 +- tests/e2e/testcases/upgrade/non-sov/suite.go | 459 ++++++++++++++++++ .../e2e/testcases/upgrade/{ => sov}/suite.go | 64 +-- 62 files changed, 3263 insertions(+), 803 deletions(-) delete mode 100644 cmd/blockchaincmd/convert.go create mode 100644 cmd/contractcmd/init_poa_validator_manager.go create mode 100644 cmd/nodecmd/upgrade.json create mode 100644 pkg/ux/progressbar.go create mode 100644 tests/e2e/testcases/subnet/non-sov/local/suite.go rename tests/e2e/testcases/subnet/{ => non-sov}/public/suite.go (96%) rename tests/e2e/testcases/subnet/{ => sov}/local/suite.go (79%) create mode 100644 tests/e2e/testcases/subnet/sov/public/suite.go create mode 100644 tests/e2e/testcases/upgrade/non-sov/suite.go rename tests/e2e/testcases/upgrade/{ => sov}/suite.go (86%) diff --git a/cmd/blockchaincmd/add_validator.go b/cmd/blockchaincmd/add_validator.go index 39b8a0ecc..d6d92107f 100644 --- a/cmd/blockchaincmd/add_validator.go +++ b/cmd/blockchaincmd/add_validator.go @@ -253,7 +253,7 @@ func CallAddValidator( if err != nil { return err } - tx, err := deployer.RegisterSubnetValidator(balance, blsInfo, changeOwner, message) + tx, err := deployer.RegisterL1Validator(balance, blsInfo, changeOwner, message) if err != nil { return err } @@ -261,7 +261,7 @@ func CallAddValidator( return nil } -func generateWarpMessageAddValidator(subnetID ids.ID, NodeID ids.NodeID, weight uint64, blsPublicKey string, expiry uint64) (warpPlatformVM.Message, error) { +func generateWarpMessageAddValidator(subnetID ids.ID, nodeID ids.NodeID, weight uint64, blsPublicKey string, expiry uint64) (warpPlatformVM.Message, error) { return warpPlatformVM.Message{}, nil } diff --git a/cmd/blockchaincmd/change_weight.go b/cmd/blockchaincmd/change_weight.go index 7d86d9726..af1760ba6 100644 --- a/cmd/blockchaincmd/change_weight.go +++ b/cmd/blockchaincmd/change_weight.go @@ -27,7 +27,7 @@ func newChangeWeightCmd() *cobra.Command { Long: `The blockchain changeWeight command changes the weight of a Subnet Validator. The Subnet has to be a Proof of Authority Subnet-Only Validator Subnet.`, - RunE: updateWeight, + RunE: setWeight, Args: cobrautils.ExactArgs(1), } networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, true, addValidatorSupportedNetworkOptions) @@ -41,7 +41,7 @@ The Subnet has to be a Proof of Authority Subnet-Only Validator Subnet.`, return cmd } -func updateWeight(_ *cobra.Command, args []string) error { +func setWeight(_ *cobra.Command, args []string) error { blockchainName := args[0] err := prompts.ValidateNodeID(nodeIDStr) if err != nil { diff --git a/cmd/blockchaincmd/convert.go b/cmd/blockchaincmd/convert.go deleted file mode 100644 index 8306a2beb..000000000 --- a/cmd/blockchaincmd/convert.go +++ /dev/null @@ -1,428 +0,0 @@ -// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -package blockchaincmd - -import ( - "github.com/ava-labs/avalanche-cli/pkg/cobrautils" - "github.com/ava-labs/avalanche-cli/pkg/networkoptions" - "github.com/spf13/cobra" -) - -// avalanche blockchain convert -func newConvertCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "convert [blockchainName]", - Short: "Converts an Avalanche blockchain into a SOV (Subnet Only Validator) blockchain", - Long: `The blockchain convert command converts a non-SOV Avalanche blockchain (which requires -subnet validators to have at least 2000 AVAX staked in the Primary Network) into a SOV (Subnet Only -Validator) blockchain. - -At the end of the call, the Owner Keys . - -Avalanche-CLI only supports deploying an individual Blockchain once per network. Subsequent -attempts to deploy the same Blockchain to the same network (local, Fuji, Mainnet) aren't -allowed. If you'd like to redeploy a Blockchain locally for testing, you must first call -avalanche network clean to reset all deployed chain state. Subsequent local deploys -redeploy the chain with fresh state. You can deploy the same Blockchain to multiple networks, -so you can take your locally tested Subnet and deploy it on Fuji or Mainnet.`, - RunE: convertSubnet, - PersistentPostRun: handlePostRun, - Args: cobrautils.ExactArgs(1), - } - networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, true, deploySupportedNetworkOptions) - cmd.Flags().StringVar(&userProvidedAvagoVersion, "avalanchego-version", "latest", "use this version of avalanchego (ex: v1.17.12)") - cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [fuji/devnet deploy only]") - cmd.Flags().BoolVarP(&sameControlKey, "same-control-key", "s", false, "use the fee-paying key as control key") - cmd.Flags().Uint32Var(&threshold, "threshold", 0, "required number of control key signatures to make subnet changes") - cmd.Flags().StringSliceVar(&controlKeys, "control-keys", nil, "addresses that may make subnet changes") - cmd.Flags().StringSliceVar(&subnetAuthKeys, "subnet-auth-keys", nil, "control keys that will be used to authenticate chain creation") - cmd.Flags().StringVar(&outputTxPath, "output-tx-path", "", "file path of the blockchain creation tx") - cmd.Flags().BoolVarP(&useEwoq, "ewoq", "e", false, "use ewoq key [fuji/devnet deploy only]") - cmd.Flags().BoolVarP(&useLedger, "ledger", "g", false, "use ledger instead of key (always true on mainnet, defaults to false on fuji/devnet)") - cmd.Flags().StringSliceVar(&ledgerAddresses, "ledger-addrs", []string{}, "use the given ledger addresses") - cmd.Flags().StringVarP(&subnetIDStr, "subnet-id", "u", "", "do not create a subnet, deploy the blockchain into the given subnet id") - cmd.Flags().Uint32Var(&mainnetChainID, "mainnet-chain-id", 0, "use different ChainID for mainnet deployment") - cmd.Flags().StringVar(&avagoBinaryPath, "avalanchego-path", "", "use this avalanchego binary path") - cmd.Flags().BoolVar(&subnetOnly, "subnet-only", false, "only create a subnet") - cmd.Flags().BoolVar(&icmSpec.SkipICMDeploy, "skip-local-teleporter", false, "skip automatic teleporter deploy on local networks [to be deprecated]") - cmd.Flags().BoolVar(&icmSpec.SkipICMDeploy, "skip-teleporter-deploy", false, "skip automatic teleporter deploy") - cmd.Flags().BoolVar(&icmSpec.SkipRelayerDeploy, "skip-relayer", false, "skip relayer deploy") - cmd.Flags().StringVar(&icmSpec.ICMVersion, "teleporter-version", "latest", "teleporter version to deploy") - cmd.Flags().StringVar(&icmSpec.RelayerVersion, "relayer-version", "latest", "relayer version to deploy") - cmd.Flags().StringVar(&icmSpec.MessengerContractAddressPath, "teleporter-messenger-contract-address-path", "", "path to an interchain messenger contract address file") - 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(&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 -} - -// // convertSubnet is the cobra command run for deploying subnets -func convertSubnet(cmd *cobra.Command, args []string) error { - //blockchainName := args[0] - // - //if err := CreateBlockchainFirst(cmd, blockchainName, skipCreatePrompt); err != nil { - // return err - //} - // - //chains, err := ValidateSubnetNameAndGetChains(args) - //if err != nil { - // return err - //} - // - //if icmSpec.MessengerContractAddressPath != "" || icmSpec.MessengerDeployerAddressPath != "" || icmSpec.MessengerDeployerTxPath != "" || icmSpec.RegistryBydecodePath != "" { - // if icmSpec.MessengerContractAddressPath == "" || icmSpec.MessengerDeployerAddressPath == "" || icmSpec.MessengerDeployerTxPath == "" || icmSpec.RegistryBydecodePath == "" { - // return fmt.Errorf("if setting any teleporter asset path, you must set all teleporter asset paths") - // } - //} - // - //var bootstrapValidators []models.SubnetValidator - //if bootstrapValidatorsJSONFilePath != "" { - // bootstrapValidators, err = LoadBootstrapValidator(bootstrapValidatorsJSONFilePath) - // if err != nil { - // return err - // } - //} - // - //chain := chains[0] - // - //sidecar, err := app.LoadSidecar(chain) - //if err != nil { - // return fmt.Errorf("failed to load sidecar for later update: %w", err) - //} - // - //if sidecar.ImportedFromAPM { - // return errors.New("unable to deploy subnets imported from a repo") - //} - // - //if outputTxPath != "" { - // if _, err := os.Stat(outputTxPath); err == nil { - // return fmt.Errorf("outputTxPath %q already exists", outputTxPath) - // } - //} - // - //network, err := networkoptions.GetNetworkFromCmdLineFlags( - // app, - // "", - // globalNetworkFlags, - // true, - // false, - // deploySupportedNetworkOptions, - // "", - //) - //if err != nil { - // return err - //} - // - //isEVMGenesis, validationErr, err := app.HasSubnetEVMGenesis(chain) - //if err != nil { - // return err - //} - //if sidecar.VM == models.SubnetEvm && !isEVMGenesis { - // return fmt.Errorf("failed to validate SubnetEVM genesis format: %w", validationErr) - //} - // - //chainGenesis, err := app.LoadRawGenesis(chain) - //if err != nil { - // return err - //} - // - //if isEVMGenesis { - // // is is a subnet evm or a custom vm based on subnet evm - // if network.Kind == models.Mainnet { - // err = getSubnetEVMMainnetChainID(&sidecar, chain) - // if err != nil { - // return err - // } - // chainGenesis, err = updateSubnetEVMGenesisChainID(chainGenesis, sidecar.SubnetEVMMainnetChainID) - // if err != nil { - // return err - // } - // } - // err = checkSubnetEVMDefaultAddressNotInAlloc(network, chain) - // if err != nil { - // return err - // } - //} - // - //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 { - // app.Log.Debug("Deploy local") - // - // genesisPath := app.GetGenesisPath(chain) - // - // // copy vm binary to the expected location, first downloading it if necessary - // var vmBin string - // switch sidecar.VM { - // case models.SubnetEvm: - // _, vmBin, err = binutils.SetupSubnetEVM(app, sidecar.VMVersion) - // if err != nil { - // return fmt.Errorf("failed to install subnet-evm: %w", err) - // } - // case models.CustomVM: - // vmBin = binutils.SetupCustomBin(app, chain) - // default: - // return fmt.Errorf("unknown vm: %s", sidecar.VM) - // } - // - // // check if selected version matches what is currently running - // nc := localnet.NewStatusChecker() - // avagoVersion, err := CheckForInvalidDeployAndGetAvagoVersion(nc, sidecar.RPCVersion) - // if err != nil { - // return err - // } - // if avagoBinaryPath == "" { - // userProvidedAvagoVersion = avagoVersion - // } - // - // deployer := subnet.NewLocalDeployer(app, userProvidedAvagoVersion, avagoBinaryPath, vmBin) - // deployInfo, err := deployer.DeployToLocalNetwork(chain, genesisPath, icmSpec, subnetIDStr) - // if err != nil { - // if deployer.BackendStartedHere() { - // if innerErr := binutils.KillgRPCServerProcess(app); innerErr != nil { - // app.Log.Warn("tried to kill the gRPC server process but it failed", zap.Error(innerErr)) - // } - // } - // return err - // } - // flags := make(map[string]string) - // flags[constants.MetricsNetwork] = network.Name() - // metrics.HandleTracking(cmd, constants.MetricsSubnetDeployCommand, app, flags) - // if err := app.UpdateSidecarNetworks( - // &sidecar, - // network, - // deployInfo.SubnetID, - // deployInfo.BlockchainID, - // deployInfo.ICMMessengerAddress, - // deployInfo.ICMRegistryAddress, - // bootstrapValidators, - // ); err != nil { - // return err - // } - // return PrintSubnetInfo(blockchainName, true) - //} - // - //// from here on we are assuming a public deploy - //if subnetOnly && subnetIDStr != "" { - // return errMutuallyExlusiveSubnetFlags - //} - // - //createSubnet := true - //var subnetID ids.ID - //if subnetIDStr != "" { - // subnetID, err = ids.FromString(subnetIDStr) - // if err != nil { - // return err - // } - // createSubnet = false - //} else if !subnetOnly && sidecar.Networks != nil { - // model, ok := sidecar.Networks[network.Name()] - // if ok { - // if model.SubnetID != ids.Empty && model.BlockchainID == ids.Empty { - // subnetID = model.SubnetID - // createSubnet = false - // } - // } - //} - // - //fee := uint64(0) - //if !subnetOnly { - // fee += network.GenesisParams().TxFeeConfig.StaticFeeConfig.CreateBlockchainTxFee - //} - //if createSubnet { - // fee += network.GenesisParams().TxFeeConfig.StaticFeeConfig.CreateSubnetTxFee - //} - // - //kc, err := keychain.GetKeychainFromCmdLineFlags( - // app, - // constants.PayTxsFeesMsg, - // network, - // keyName, - // useEwoq, - // useLedger, - // ledgerAddresses, - // fee, - //) - //if err != nil { - // return err - //} - // - //network.HandlePublicNetworkSimulation() - // - //if createSubnet { - // controlKeys, threshold, err = promptOwners( - // kc, - // controlKeys, - // sameControlKey, - // threshold, - // subnetAuthKeys, - // true, - // ) - // if err != nil { - // return err - // } - //} else { - // ux.Logger.PrintToUser(logging.Blue.Wrap( - // fmt.Sprintf("Deploying into pre-existent subnet ID %s", subnetID.String()), - // )) - // var isPermissioned bool - // isPermissioned, controlKeys, threshold, err = txutils.GetOwners(network, subnetID) - // if err != nil { - // return err - // } - // if !isPermissioned { - // return ErrNotPermissionedSubnet - // } - //} - // - //// add control keys to the keychain whenever possible - //if err := kc.AddAddresses(controlKeys); err != nil { - // return err - //} - // - //kcKeys, err := kc.PChainFormattedStrAddresses() - //if err != nil { - // return err - //} - // - //// get keys for blockchain tx signing - //if subnetAuthKeys != nil { - // if err := prompts.CheckSubnetAuthKeys(kcKeys, subnetAuthKeys, controlKeys, threshold); err != nil { - // return err - // } - //} else { - // subnetAuthKeys, err = prompts.GetSubnetAuthKeys(app.Prompt, kcKeys, controlKeys, threshold) - // if err != nil { - // return err - // } - //} - //ux.Logger.PrintToUser("Your subnet auth keys for chain creation: %s", subnetAuthKeys) - // - //// deploy to public network - //deployer := subnet.NewPublicDeployer(app, kc, network) - // - //if createSubnet { - // subnetID, err = deployer.DeploySubnet(controlKeys, threshold) - // if err != nil { - // return err - // } - // // get the control keys in the same order as the tx - // _, controlKeys, threshold, err = txutils.GetOwners(network, subnetID) - // if err != nil { - // return err - // } - //} - // - //var ( - // savePartialTx bool - // blockchainID ids.ID - // tx *txs.Tx - // remainingSubnetAuthKeys []string - // isFullySigned bool - //) - // - //if !subnetOnly { - // isFullySigned, blockchainID, tx, remainingSubnetAuthKeys, err = deployer.DeployBlockchain( - // controlKeys, - // subnetAuthKeys, - // subnetID, - // chain, - // chainGenesis, - // ) - // if err != nil { - // ux.Logger.PrintToUser(logging.Red.Wrap( - // fmt.Sprintf("error deploying blockchain: %s. fix the issue and try again with a new deploy cmd", err), - // )) - // } - // - // savePartialTx = !isFullySigned && err == nil - //} - // - //if err := PrintDeployResults(chain, subnetID, blockchainID); err != nil { - // return err - //} - // - //if savePartialTx { - // if err := SaveNotFullySignedTx( - // "Blockchain Creation", - // tx, - // chain, - // subnetAuthKeys, - // remainingSubnetAuthKeys, - // outputTxPath, - // false, - // ); err != nil { - // return err - // } - //} - // - //// type ConvertSubnetTx struct { - //// // Metadata, inputs and outputs - //// BaseTx - //// // ID of the Subnet to transform - //// // Restrictions: - //// // - Must not be the Primary Network ID - //// Subnet ids.ID `json:"subnetID"` - //// // BlockchainID where the Subnet manager lives - //// ChainID ids.ID `json:"chainID"` - //// // Address of the Subnet manager - //// Address []byte `json:"address"` - //// // Initial pay-as-you-go validators for the Subnet - //// Validators []SubnetValidator `json:"validators"` - //// // Authorizes this conversion - //// SubnetAuth verify.Verifiable `json:"subnetAuthorization"` - //// } - // - ////avaGoBootstrapValidators, err := convertToAvalancheGoSubnetValidator(bootstrapValidators) - ////if err != nil { - //// return err - ////} - //// TODO: replace with avalanchego subnetValidators once implemented - //isFullySigned, convertSubnetTxID, tx, remainingSubnetAuthKeys, err := deployer.ConvertSubnet( - // controlKeys, - // subnetAuthKeys, - // subnetID, - // blockchainID, - // // avaGoBootstrapValidators, - //) - //if err != nil { - // ux.Logger.PrintToUser(logging.Red.Wrap( - // fmt.Sprintf("error converting blockchain: %s. fix the issue and try again with a new convert cmd", err), - // )) - //} - // - //savePartialTx = !isFullySigned && err == nil - //ux.Logger.PrintToUser("ConvertSubnetTx ID: %s", convertSubnetTxID) - // - //if savePartialTx { - // if err := SaveNotFullySignedTx( - // "ConvertSubnetTx", - // tx, - // chain, - // subnetAuthKeys, - // remainingSubnetAuthKeys, - // outputTxPath, - // false, - // ); err != nil { - // return err - // } - //} - // - //flags := make(map[string]string) - //flags[constants.MetricsNetwork] = network.Name() - //metrics.HandleTracking(cmd, constants.MetricsSubnetDeployCommand, app, flags) - // - //// update sidecar - //// TODO: need to do something for backwards compatibility? - //return app.UpdateSidecarNetworks(&sidecar, network, subnetID, blockchainID, "", "", bootstrapValidators) - return nil -} diff --git a/cmd/blockchaincmd/create.go b/cmd/blockchaincmd/create.go index e82a7fbc2..5736b2e22 100644 --- a/cmd/blockchaincmd/create.go +++ b/cmd/blockchaincmd/create.go @@ -62,6 +62,7 @@ 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") + errSOVFlagsOnly = errors.New("flags --proof-of-authority, --proof-of-stake, --poa-manager-owner are only applicable to Subnet Only Validator (SOV) blockchains") ) // avalanche blockchain create @@ -109,6 +110,7 @@ configuration, pass the -f flag.`, 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, "(coming soon) use proof of stake for validator management") cmd.Flags().StringVar(&createFlags.poaValidatorManagerOwner, "poa-manager-owner", "", "EVM address that controls Validator Manager Owner (for Proof of Authority only)") + cmd.Flags().BoolVar(&sovereign, "sovereign", true, "set to false if creating non-sovereign blockchain") return cmd } @@ -193,6 +195,11 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { return errors.New("flags --evm,--custom are mutually exclusive") } + if !sovereign { + if createFlags.proofOfAuthority || createFlags.proofOfStake || createFlags.poaValidatorManagerOwner != "" { + return errSOVFlagsOnly + } + } // validator management type exclusiveness if !flags.EnsureMutuallyExclusive([]bool{createFlags.proofOfAuthority, createFlags.proofOfStake}) { return errMutuallyExlusiveValidatorManagementOptions @@ -225,24 +232,27 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { sc := &models.Sidecar{} - if err = promptValidatorManagementType(app, sc); err != nil { - return err - } - - if !sc.PoA() && createFlags.poaValidatorManagerOwner != "" { - return errors.New("--poa-manager-owner flag cannot be used when blockchain validator management type is not Proof of Authority") + if sovereign { + if err = promptValidatorManagementType(app, sc); err != nil { + return err + } + if !sc.PoA() && createFlags.poaValidatorManagerOwner != "" { + return errors.New("--poa-manager-owner flag cannot be used when blockchain validator management type is not Proof of Authority") + } } if vmType == models.SubnetEvm { - if sc.PoA() { - if createFlags.poaValidatorManagerOwner == "" { - createFlags.poaValidatorManagerOwner, err = getValidatorContractManagerAddr() - if err != nil { - return err + if sovereign { + if sc.PoA() { + if createFlags.poaValidatorManagerOwner == "" { + createFlags.poaValidatorManagerOwner, err = getValidatorContractManagerAddr() + if err != nil { + return err + } } + sc.PoAValidatorManagerOwner = createFlags.poaValidatorManagerOwner + ux.Logger.GreenCheckmarkToUser("Validator Manager Contract owner address %s", createFlags.poaValidatorManagerOwner) } - sc.PoAValidatorManagerOwner = createFlags.poaValidatorManagerOwner - ux.Logger.GreenCheckmarkToUser("Validator Manager Contract owner address %s", createFlags.poaValidatorManagerOwner) } if genesisFile == "" { @@ -325,6 +335,7 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { vmVersion, tokenSymbol, true, + sovereign, ); err != nil { return err } @@ -354,6 +365,7 @@ func createBlockchainConfig(cmd *cobra.Command, args []string) error { customVMBuildScript, vmFile, tokenSymbol, + sovereign, ); err != nil { return err } diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index 87d5479ac..78e19cf7a 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -9,13 +9,16 @@ import ( "os" "path/filepath" "strings" + "time" + "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" + "github.com/ethereum/go-ethereum/common" + + "github.com/ava-labs/avalanche-cli/pkg/contract" + "github.com/ava-labs/avalanche-cli/pkg/utils" "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" - - "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/binutils" "github.com/ava-labs/avalanche-cli/pkg/cobrautils" @@ -29,6 +32,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/subnet" "github.com/ava-labs/avalanche-cli/pkg/txutils" "github.com/ava-labs/avalanche-cli/pkg/ux" + "github.com/ava-labs/avalanche-cli/pkg/validatormanager" "github.com/ava-labs/avalanche-cli/pkg/vm" anrutils "github.com/ava-labs/avalanche-network-runner/utils" "github.com/ava-labs/avalanchego/ids" @@ -67,6 +71,7 @@ var ( icmSpec subnet.ICMSpec generateNodeID bool bootstrapValidatorsJSONFilePath string + privateKeyFlags contract.PrivateKeyFlags 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") @@ -94,6 +99,8 @@ so you can take your locally tested Subnet and deploy it on Fuji or Mainnet.`, Args: cobrautils.ExactArgs(1), } networkoptions.AddNetworkFlagsToCmd(cmd, &globalNetworkFlags, true, deploySupportedNetworkOptions) + privateKeyFlags.SetFlagNames("blockchain-private-key", "blockchain-key", "blockchain-genesis-key") + privateKeyFlags.AddToCmd(cmd, "to fund validator manager initialization") cmd.Flags().StringVar(&userProvidedAvagoVersion, "avalanchego-version", "latest", "use this version of avalanchego (ex: v1.17.12)") cmd.Flags().StringVarP(&keyName, "key", "k", "", "select the key to use [fuji/devnet deploy only]") cmd.Flags().BoolVarP(&sameControlKey, "same-control-key", "s", false, "use the fee-paying key as control key") @@ -337,6 +344,10 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { } } + if !sidecar.Sovereign && bootstrapValidatorsJSONFilePath != "" { + return fmt.Errorf("--bootstrap-filepath flag is only applicable to sovereign blockchains") + } + network, err := networkoptions.GetNetworkFromCmdLineFlags( app, "", @@ -381,7 +392,7 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { } } - if bootstrapValidatorsJSONFilePath == "" { + if sidecar.Sovereign && bootstrapValidatorsJSONFilePath == "" { bootstrapValidators, err = promptBootstrapValidators(network) if err != nil { return err @@ -494,6 +505,9 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { network.HandlePublicNetworkSimulation() if createSubnet { + if sidecar.Sovereign { + sameControlKey = true + } controlKeys, threshold, err = promptOwners( kc, controlKeys, @@ -550,6 +564,7 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { if err != nil { return err } + deployer.CleanCacheWallet() // get the control keys in the same order as the tx _, controlKeys, threshold, err = txutils.GetOwners(network, subnetID) if err != nil { @@ -600,34 +615,129 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { } } - isFullySigned, convertSubnetTxID, tx, remainingSubnetAuthKeys, err := deployer.ConvertSubnet( - controlKeys, - subnetAuthKeys, - subnetID, - blockchainID, - // avaGoBootstrapValidators, - ) - if err != nil { - ux.Logger.PrintToUser(logging.Red.Wrap( - fmt.Sprintf("error converting blockchain: %s. fix the issue and try again with a new convert cmd", err), - )) - } + if sidecar.Sovereign { + avaGoBootstrapValidators, err := ConvertToAvalancheGoSubnetValidator(bootstrapValidators) + if err != nil { + return err + } + deployer.CleanCacheWallet() + managerAddress := common.HexToAddress(validatormanager.ValidatorContractAddress) + isFullySigned, ConvertL1TxID, tx, remainingSubnetAuthKeys, err := deployer.ConvertL1( + controlKeys, + subnetAuthKeys, + subnetID, + blockchainID, + managerAddress, + avaGoBootstrapValidators, + ) + if err != nil { + ux.Logger.RedXToUser("error converting blockchain: %s. fix the issue and try again with a new convert cmd", err) + return err + } - savePartialTx = !isFullySigned && err == nil - ux.Logger.PrintToUser("ConvertSubnetTx ID: %s", convertSubnetTxID) + savePartialTx = !isFullySigned && err == nil + ux.Logger.PrintToUser("ConvertL1Tx ID: %s", ConvertL1TxID) + + if savePartialTx { + if err := SaveNotFullySignedTx( + "ConvertL1Tx", + tx, + chain, + subnetAuthKeys, + remainingSubnetAuthKeys, + outputTxPath, + false, + ); err != nil { + return err + } + } - if savePartialTx { - if err := SaveNotFullySignedTx( - "ConvertSubnetTx", - tx, - chain, - subnetAuthKeys, - remainingSubnetAuthKeys, - outputTxPath, - false, - ); err != nil { + bar, err := ux.TimedProgressBar( + 30*time.Second, + "Waiting for Blockchain to be converted into Subnet Only Validator (SOV) Blockchain ...", + 2, + ) + if err != nil { return err } + + // Issue random transaction >30s after ConverSubnetTx to evict its block from the block map + _, _, err = deployer.PChainTransfer(kc.Addresses().List()[0], 1) + if err != nil { + return err + } + if err := ux.ExtraStepExecuted(bar); err != nil { + return err + } + // Issue random transaction to advance the p-chain height now that the + // ConvertSubnetTx block isn't in the block map + _, _, err = deployer.PChainTransfer(kc.Addresses().List()[0], 1) + if err != nil { + return err + } + if err := ux.ExtraStepExecuted(bar); err != nil { + return err + } + fmt.Println() + + if err := app.UpdateSidecarNetworks(&sidecar, network, subnetID, blockchainID, "", "", bootstrapValidators); err != nil { + return err + } + + if false { + chainSpec := contract.ChainSpec{ + BlockchainName: blockchainName, + } + genesisAddress, genesisPrivateKey, err := contract.GetEVMSubnetPrefundedKey( + app, + network, + chainSpec, + ) + if err != nil { + return err + } + privateKey, err := privateKeyFlags.GetPrivateKey(app, genesisPrivateKey) + if err != nil { + return err + } + if privateKey == "" { + privateKey, err = prompts.PromptPrivateKey( + app.Prompt, + "Which key to you want to use to pay for initializing Validator Manager contract? (Uses Blockchain gas token)", + app.GetKeyDir(), + app.GetKey, + genesisAddress, + genesisPrivateKey, + ) + if err != nil { + return err + } + } + rpcURL, _, err := contract.GetBlockchainEndpoints( + app, + network, + chainSpec, + true, + false, + ) + if err != nil { + return err + } + if err := validatormanager.SetupPoA( + app, + network, + rpcURL, + contract.ChainSpec{ + BlockchainName: blockchainName, + }, + privateKey, + common.HexToAddress(sidecar.PoAValidatorManagerOwner), + avaGoBootstrapValidators, + ); err != nil { + return err + } + ux.Logger.GreenCheckmarkToUser("Subnet is successfully converted into Subnet Only Validator") + } } flags := make(map[string]string) @@ -636,10 +746,10 @@ 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) + return nil } -func getBLSInfo(publicKey, proofOfPossesion string) (signer.Signer, error) { +func getBLSInfo(publicKey, proofOfPossesion string) (signer.ProofOfPossession, error) { type jsonProofOfPossession struct { PublicKey string ProofOfPossession string @@ -650,18 +760,19 @@ func getBLSInfo(publicKey, proofOfPossesion string) (signer.Signer, error) { } popBytes, err := json.Marshal(jsonPop) if err != nil { - return nil, err + return signer.ProofOfPossession{}, err } pop := &signer.ProofOfPossession{} err = pop.UnmarshalJSON(popBytes) if err != nil { - return nil, err + return signer.ProofOfPossession{}, err } - return pop, nil + return *pop, nil } -func convertToAvalancheGoSubnetValidator(subnetValidators []models.SubnetValidator) ([]SubnetValidator, error) { - bootstrapValidators := []SubnetValidator{} +// TODO: add deactivation owner? +func ConvertToAvalancheGoSubnetValidator(subnetValidators []models.SubnetValidator) ([]*txs.ConvertSubnetValidator, error) { + bootstrapValidators := []*txs.ConvertSubnetValidator{} for _, validator := range subnetValidators { nodeID, err := ids.NodeIDFromString(validator.NodeID) if err != nil { @@ -675,14 +786,14 @@ func convertToAvalancheGoSubnetValidator(subnetValidators []models.SubnetValidat if err != nil { return nil, fmt.Errorf("failure parsing change owner address: %w", err) } - bootstrapValidator := SubnetValidator{ - NodeID: nodeID, + bootstrapValidator := &txs.ConvertSubnetValidator{ + NodeID: nodeID[:], Weight: validator.Weight, Balance: validator.Balance, Signer: blsInfo, - ChangeOwner: &secp256k1fx.OutputOwners{ + RemainingBalanceOwner: message.PChainOwner{ Threshold: 1, - Addrs: addrs, + Addresses: addrs, }, } bootstrapValidators = append(bootstrapValidators, bootstrapValidator) diff --git a/cmd/blockchaincmd/describe.go b/cmd/blockchaincmd/describe.go index e62ab66f1..7bbaed6cf 100644 --- a/cmd/blockchaincmd/describe.go +++ b/cmd/blockchaincmd/describe.go @@ -162,6 +162,19 @@ func PrintSubnetInfo(blockchainName string, onlyLocalnetInfo bool) error { t.AppendRow(table.Row{net, "BlockchainID (CB58)", data.BlockchainID.String()}) t.AppendRow(table.Row{net, "BlockchainID (HEX)", hexEncoding}) } + endpoint, _, err := contract.GetBlockchainEndpoints( + app, + network, + contract.ChainSpec{ + BlockchainName: sc.Name, + }, + false, + false, + ) + if err != nil { + return err + } + t.AppendRow(table.Row{net, "RPC Endpoint", endpoint}) } ux.Logger.PrintToUser(t.Render()) diff --git a/cmd/blockchaincmd/export_test.go b/cmd/blockchaincmd/export_test.go index 84a3a1188..8c84ca749 100644 --- a/cmd/blockchaincmd/export_test.go +++ b/cmd/blockchaincmd/export_test.go @@ -47,6 +47,7 @@ func TestExportImportSubnet(t *testing.T) { vmVersion, "Test", false, + true, ) require.NoError(err) err = app.WriteGenesisFile(testSubnet, genBytes) diff --git a/cmd/blockchaincmd/prompt_genesis_input.go b/cmd/blockchaincmd/prompt_genesis_input.go index 3c92a4172..842a2cc45 100644 --- a/cmd/blockchaincmd/prompt_genesis_input.go +++ b/cmd/blockchaincmd/prompt_genesis_input.go @@ -122,6 +122,7 @@ func promptBootstrapValidators(network models.Network) ([]models.SubnetValidator var subnetValidators []models.SubnetValidator numBootstrapValidators, err := app.Prompt.CaptureInt( "How many bootstrap validators do you want to set up?", + prompts.ValidatePositiveInt, ) if err != nil { return nil, err diff --git a/cmd/blockchaincmd/remove_validator.go b/cmd/blockchaincmd/remove_validator.go index f10225b58..c903066ae 100644 --- a/cmd/blockchaincmd/remove_validator.go +++ b/cmd/blockchaincmd/remove_validator.go @@ -218,7 +218,7 @@ func removeValidatorSOV(deployer *subnet.PublicDeployer, network models.Network, if err != nil { return err } - tx, err := deployer.SetSubnetValidatorWeight(message) + tx, err := deployer.SetL1ValidatorWeight(message) if err != nil { return err } diff --git a/cmd/blockchaincmd/upgradecmd/apply.go b/cmd/blockchaincmd/upgradecmd/apply.go index 744c348a6..2df242a29 100644 --- a/cmd/blockchaincmd/upgradecmd/apply.go +++ b/cmd/blockchaincmd/upgradecmd/apply.go @@ -305,7 +305,7 @@ func applyPublicNetworkUpgrade(blockchainName, networkKey string, sc *models.Sid ux.Logger.PrintToUser("Trying to install the upgrade files at the provided %s path", avalanchegoChainConfigDir) chainDir := filepath.Join(avalanchegoChainConfigDir, sc.Networks[networkKey].BlockchainID.String()) - destPath := filepath.Join(chainDir, constants.UpgradeBytesFileName) + destPath := filepath.Join(chainDir, constants.UpgradeFileName) if err = os.Mkdir(chainDir, constants.DefaultPerms755); err != nil && !os.IsExist(err) { return fmt.Errorf("failed to create blockchain directory: %w", err) } diff --git a/cmd/contractcmd/contract.go b/cmd/contractcmd/contract.go index 84df73504..b4e606f92 100644 --- a/cmd/contractcmd/contract.go +++ b/cmd/contractcmd/contract.go @@ -22,5 +22,7 @@ and interacting with smart contracts.`, app = injectedApp // contract deploy cmd.AddCommand(newDeployCmd()) + // contract initpoamanager + cmd.AddCommand(newInitPOAManagerCmd()) return cmd } diff --git a/cmd/contractcmd/init_poa_validator_manager.go b/cmd/contractcmd/init_poa_validator_manager.go new file mode 100644 index 000000000..940f56290 --- /dev/null +++ b/cmd/contractcmd/init_poa_validator_manager.go @@ -0,0 +1,132 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package contractcmd + +import ( + "fmt" + + "github.com/ava-labs/avalanche-cli/cmd/blockchaincmd" + "github.com/ava-labs/avalanche-cli/pkg/cobrautils" + "github.com/ava-labs/avalanche-cli/pkg/contract" + "github.com/ava-labs/avalanche-cli/pkg/networkoptions" + "github.com/ava-labs/avalanche-cli/pkg/prompts" + "github.com/ava-labs/avalanche-cli/pkg/ux" + "github.com/ava-labs/avalanche-cli/pkg/validatormanager" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ethereum/go-ethereum/common" + + "github.com/spf13/cobra" +) + +type InitPOAManagerFlags struct { + Network networkoptions.NetworkFlags + PrivateKeyFlags contract.PrivateKeyFlags + rpcEndpoint string +} + +var ( + initPOAManagerSupportedNetworkOptions = []networkoptions.NetworkOption{ + networkoptions.Local, + networkoptions.Devnet, + networkoptions.Fuji, + } + initPOAManagerFlags InitPOAManagerFlags +) + +// avalanche contract initpoamanager +func newInitPOAManagerCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "initPoaManager", + Short: "Initializes a Proof of Authority Validator Manager on a given Network and Blockchain", + Long: "Initializes Proof of Authority Validator Manager contract on a Blockchain and sets up initial validator set on the Blockchain. For more info on Validator Manager, please head to https://github.com/ava-labs/teleporter/tree/staking-contract/contracts/validator-manager", + RunE: initPOAManager, + Args: cobrautils.ExactArgs(1), + } + networkoptions.AddNetworkFlagsToCmd(cmd, &initPOAManagerFlags.Network, true, initPOAManagerSupportedNetworkOptions) + initPOAManagerFlags.PrivateKeyFlags.AddToCmd(cmd, "as contract deployer") + cmd.Flags().StringVar(&initPOAManagerFlags.rpcEndpoint, "rpc", "", "deploy the contract into the given rpc endpoint") + return cmd +} + +func initPOAManager(_ *cobra.Command, args []string) error { + blockchainName := args[0] + chainSpec := contract.ChainSpec{ + BlockchainName: blockchainName, + } + network, err := networkoptions.GetNetworkFromCmdLineFlags( + app, + "", + initPOAManagerFlags.Network, + true, + false, + initPOAManagerSupportedNetworkOptions, + "", + ) + if err != nil { + return err + } + if initPOAManagerFlags.rpcEndpoint == "" { + initPOAManagerFlags.rpcEndpoint, _, err = contract.GetBlockchainEndpoints( + app, + network, + chainSpec, + true, + false, + ) + if err != nil { + return err + } + } + ux.Logger.PrintToUser(logging.Yellow.Wrap("RPC Endpoint: %s"), initPOAManagerFlags.rpcEndpoint) + genesisAddress, genesisPrivateKey, err := contract.GetEVMSubnetPrefundedKey( + app, + network, + chainSpec, + ) + if err != nil { + return err + } + privateKey, err := initPOAManagerFlags.PrivateKeyFlags.GetPrivateKey(app, genesisPrivateKey) + if err != nil { + return err + } + if privateKey == "" { + privateKey, err = prompts.PromptPrivateKey( + app.Prompt, + "Which key to you want to use to pay for initializing Proof of Authority Validator Manager contract? (Uses Blockchain gas token)", + app.GetKeyDir(), + app.GetKey, + genesisAddress, + genesisPrivateKey, + ) + if err != nil { + return err + } + } + sc, err := app.LoadSidecar(chainSpec.BlockchainName) + if err != nil { + return fmt.Errorf("failed to load sidecar: %w", err) + } + if sc.Networks[network.Name()].BlockchainID == ids.Empty { + return fmt.Errorf("blockchain has not been deployed to %s", network.Name()) + } + bootstrapValidators := sc.Networks[network.Name()].BootstrapValidators + avaGoBootstrapValidators, err := blockchaincmd.ConvertToAvalancheGoSubnetValidator(bootstrapValidators) + if err != nil { + return err + } + if err := validatormanager.SetupPoA( + app, + network, + initPOAManagerFlags.rpcEndpoint, + chainSpec, + privateKey, + common.HexToAddress(sc.PoAValidatorManagerOwner), + avaGoBootstrapValidators, + ); err != nil { + return err + } + ux.Logger.GreenCheckmarkToUser("Proof of Authority Validator Manager contract successfully initialized on blockchain %s", blockchainName) + return nil +} diff --git a/cmd/nodecmd/create_devnet.go b/cmd/nodecmd/create_devnet.go index 889d0eb40..397556b3f 100644 --- a/cmd/nodecmd/create_devnet.go +++ b/cmd/nodecmd/create_devnet.go @@ -3,6 +3,7 @@ package nodecmd import ( + _ "embed" "encoding/json" "fmt" "os" @@ -41,6 +42,9 @@ const ( allocationCommonEthAddress = "0xb3d82b1367d362de99ab59a658165aff520cbd4d" ) +//go:embed upgrade.json +var upgradeBytes []byte + func generateCustomCchainGenesis() ([]byte, error) { cChainGenesisMap := map[string]interface{}{} cChainGenesisMap["config"] = coreth_params.GetChainConfig(avago_upgrade.GetConfig(avago_constants.LocalID), coreth_params.AvalancheLocalChainID) @@ -222,15 +226,20 @@ func setupDevnet(clusterName string, hosts []*models.Host, apiNodeIPMap map[stri confMap[config.NetworkNameKey] = fmt.Sprintf("network-%d", network.ID) confMap[config.BootstrapIDsKey] = strings.Join(bootstrapIDs, ",") confMap[config.BootstrapIPsKey] = strings.Join(bootstrapIPs, ",") - confMap[config.GenesisFileKey] = filepath.Join(constants.DockerNodeConfigPath, "genesis.json") + confMap[config.GenesisFileKey] = filepath.Join(constants.DockerNodeConfigPath, constants.GenesisFileName) + confMap[config.UpgradeFileKey] = filepath.Join(constants.DockerNodeConfigPath, constants.UpgradeFileName) + confMap[config.ProposerVMUseCurrentHeightKey] = constants.DevnetFlagsProposerVMUseCurrentHeight confBytes, err := json.MarshalIndent(confMap, "", " ") if err != nil { return err } - if err := os.WriteFile(filepath.Join(app.GetNodeInstanceDirPath(host.GetCloudID()), "genesis.json"), genesisBytes, constants.WriteReadReadPerms); err != nil { + if err := os.WriteFile(filepath.Join(app.GetNodeInstanceDirPath(host.GetCloudID()), constants.GenesisFileName), genesisBytes, constants.WriteReadReadPerms); err != nil { + return err + } + if err := os.WriteFile(filepath.Join(app.GetNodeInstanceDirPath(host.GetCloudID()), constants.UpgradeFileName), upgradeBytes, constants.WriteReadReadPerms); err != nil { return err } - if err := os.WriteFile(filepath.Join(app.GetNodeInstanceDirPath(host.GetCloudID()), "node.json"), confBytes, constants.WriteReadReadPerms); err != nil { + if err := os.WriteFile(filepath.Join(app.GetNodeInstanceDirPath(host.GetCloudID()), constants.NodeFileName), confBytes, constants.WriteReadReadPerms); err != nil { return err } if slices.Contains(hostsWithoutAPIIDs, host.NodeID) { diff --git a/cmd/nodecmd/helpers.go b/cmd/nodecmd/helpers.go index dc3c683f4..bf004988f 100644 --- a/cmd/nodecmd/helpers.go +++ b/cmd/nodecmd/helpers.go @@ -240,8 +240,10 @@ func getWSEndpoint(endpoint string, blockchainID string) string { return models.NewDevnetNetwork(endpoint, 0).BlockchainWSEndpoint(blockchainID) } -func getPublicEndpoints(clusterName string) ([]string, error) { - endpoints := []string{} +func getPublicEndpoints( + clusterName string, + trackers []*models.Host, +) ([]string, error) { clusterConfig, err := app.GetClusterConfig(clusterName) if err != nil { return nil, err @@ -250,12 +252,11 @@ func getPublicEndpoints(clusterName string) ([]string, error) { if clusterConfig.Network.Kind == models.Devnet { publicNodes = clusterConfig.Nodes } - for _, cloudID := range publicNodes { - nodeConfig, err := app.LoadClusterNodeConfig(cloudID) - if err != nil { - return nil, err - } - endpoints = append(endpoints, getAvalancheGoEndpoint(nodeConfig.ElasticIP)) - } + publicTrackers := utils.Filter(trackers, func(tracker *models.Host) bool { + return utils.Belongs(publicNodes, tracker.GetCloudID()) + }) + endpoints := utils.Map(publicTrackers, func(tracker *models.Host) string { + return getAvalancheGoEndpoint(tracker.IP) + }) return endpoints, nil } diff --git a/cmd/nodecmd/sync.go b/cmd/nodecmd/sync.go index 06a699d02..1eeea041e 100644 --- a/cmd/nodecmd/sync.go +++ b/cmd/nodecmd/sync.go @@ -181,7 +181,7 @@ func trackSubnet( networkInfo := sc.Networks[clusterConfig.Network.Name()] rpcEndpoints := set.Of(networkInfo.RPCEndpoints...) wsEndpoints := set.Of(networkInfo.WSEndpoints...) - publicEndpoints, err := getPublicEndpoints(clusterName) + publicEndpoints, err := getPublicEndpoints(clusterName, hosts) if err != nil { return err } diff --git a/cmd/nodecmd/upgrade.json b/cmd/nodecmd/upgrade.json new file mode 100644 index 000000000..ca3a7f13a --- /dev/null +++ b/cmd/nodecmd/upgrade.json @@ -0,0 +1,16 @@ +{ + "apricotPhase1Time": "2020-12-05T05:00:00Z", + "apricotPhase2Time": "2020-12-05T05:00:00Z", + "apricotPhase3Time": "2020-12-05T05:00:00Z", + "apricotPhase4Time": "2020-12-05T05:00:00Z", + "apricotPhase4MinPChainHeight": 0, + "apricotPhase5Time": "2020-12-05T05:00:00Z", + "apricotPhasePre6Time": "2020-12-05T05:00:00Z", + "apricotPhase6Time": "2020-12-05T05:00:00Z", + "apricotPhasePost6Time": "2020-12-05T05:00:00Z", + "banffTime": "2020-12-05T05:00:00Z", + "cortinaTime": "2020-12-05T05:00:00Z", + "cortinaXChainStopVertexID": "11111111111111111111111111111111LpoYY", + "durangoTime": "2020-12-05T05:00:00Z", + "etnaTime": "2020-12-05T05:00:00Z" +} diff --git a/go.mod b/go.mod index b5d973dd6..3776779d4 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,15 @@ module github.com/ava-labs/avalanche-cli -go 1.22.7 +go 1.22.8 require ( github.com/ava-labs/apm v1.0.0 - github.com/ava-labs/avalanche-network-runner v1.8.4-0.20240930181211-bfc2f5f85973 - github.com/ava-labs/avalanchego v1.11.11 - github.com/ava-labs/awm-relayer v1.4.1-0.20240910171125-3c2ae781fb32 - github.com/ava-labs/coreth v0.13.8-fixed-genesis-upgrade.0.20240815193440-a96bc921e732 - github.com/ava-labs/subnet-evm v0.6.9 - github.com/aws/aws-sdk-go-v2 v1.30.5 + github.com/ava-labs/avalanche-network-runner v1.8.4-0.20241005224128-cc3c07bb1344 + github.com/ava-labs/avalanchego v1.12.0-initial-poc.3 + github.com/ava-labs/awm-relayer v1.4.1-0.20241003162124-807fd305670f + github.com/ava-labs/coreth v0.13.8 + github.com/ava-labs/subnet-evm v0.6.10 + github.com/aws/aws-sdk-go-v2 v1.31.0 github.com/aws/aws-sdk-go-v2/config v1.27.26 github.com/aws/aws-sdk-go-v2/service/ec2 v1.162.0 github.com/chelnak/ysmrr v0.4.0 @@ -18,6 +18,7 @@ require ( github.com/fatih/color v1.17.0 github.com/go-git/go-git/v5 v5.12.0 github.com/jedib0t/go-pretty/v6 v6.5.9 + github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/liyue201/erc20-go v0.0.0-20210521034206-b2824246def0 github.com/manifoldco/promptui v0.9.0 @@ -30,7 +31,8 @@ require ( github.com/pborman/ansi v1.0.0 github.com/pingcap/errors v0.11.4 github.com/posthog/posthog-go v1.2.24 - github.com/prometheus/client_golang v1.20.3 + github.com/prometheus/client_golang v1.20.4 + github.com/schollz/progressbar/v3 v3.16.1 github.com/shirou/gopsutil v3.21.11+incompatible github.com/spf13/afero v1.11.0 github.com/spf13/cobra v1.8.1 @@ -42,7 +44,7 @@ require ( golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 golang.org/x/mod v0.20.0 golang.org/x/net v0.28.0 - golang.org/x/oauth2 v0.21.0 + golang.org/x/oauth2 v0.22.0 golang.org/x/sync v0.8.0 golang.org/x/text v0.17.0 google.golang.org/api v0.184.0 @@ -54,7 +56,7 @@ require ( require ( cloud.google.com/go/auth v0.5.1 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect - cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/compute/metadata v0.5.0 // indirect dario.cat/mergo v1.0.0 // indirect github.com/DataDog/zstd v1.5.2 // indirect github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect @@ -68,16 +70,16 @@ require ( github.com/ava-labs/teleporter v1.0.7 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.26 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect - github.com/aws/aws-sdk-go-v2/service/kms v1.35.7 // indirect + github.com/aws/aws-sdk-go-v2/service/kms v1.36.2 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.22.3 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect - github.com/aws/smithy-go v1.20.4 // indirect + github.com/aws/smithy-go v1.21.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect @@ -136,7 +138,7 @@ require ( github.com/googleapis/gax-go/v2 v2.12.4 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/rpc v1.2.0 // indirect - github.com/gorilla/websocket v1.4.2 // indirect + github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect github.com/hashicorp/go-bexpr v0.1.10 // indirect @@ -161,7 +163,8 @@ require ( github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/pointerstructure v1.2.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect @@ -178,7 +181,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/rs/cors v1.7.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect @@ -216,14 +219,14 @@ require ( go.opentelemetry.io/otel/trace v1.24.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/term v0.23.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/term v0.25.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.24.0 // indirect gonum.org/v1/gonum v0.11.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect - google.golang.org/grpc v1.66.1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/grpc v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index a51a4acea..f7f98a76c 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,8 @@ cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRk cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= @@ -83,23 +83,23 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/ava-labs/apm v1.0.0 h1:6FwozH67hEkbWVsOXNZGexBy5KLpNeYucN9zcFUHv+Q= github.com/ava-labs/apm v1.0.0/go.mod h1:TJL7pTlZNvQatsQPsLUtDHApEwVZ/qS7iSNtRFU83mc= -github.com/ava-labs/avalanche-network-runner v1.8.4-0.20240930181211-bfc2f5f85973 h1:rsAfxjQLCs9dyDGqUZK29fGDrW4uzLzYjQLUVMEkvdQ= -github.com/ava-labs/avalanche-network-runner v1.8.4-0.20240930181211-bfc2f5f85973/go.mod h1:T6AYu2HBw4N6oAIRJWATSLx3/uWLVVADTbfSCIcSlEY= -github.com/ava-labs/avalanchego v1.11.11 h1:MIQq8xRavRj4ZXHA4G+aMiymig7SOScGOG1SApmMvBc= -github.com/ava-labs/avalanchego v1.11.11/go.mod h1:yFx3V31Jy9NFa8GZlgGnwiVf8KGjeF2+Uc99l9Scd/8= -github.com/ava-labs/awm-relayer v1.4.1-0.20240910171125-3c2ae781fb32 h1:KcrFCyWU6A+GJ/E2fZSI6qGjU3M2NPl7QrzJ5fH3t2Q= -github.com/ava-labs/awm-relayer v1.4.1-0.20240910171125-3c2ae781fb32/go.mod h1:Pen08eCEURnN4unfkAdYEIoKo/4RujaLV6ISgRUHj5g= -github.com/ava-labs/coreth v0.13.8-fixed-genesis-upgrade.0.20240815193440-a96bc921e732 h1:wlhGJbmb7s3bU2QWtxKjscGjfHknQiq+cVhhUjONsB8= -github.com/ava-labs/coreth v0.13.8-fixed-genesis-upgrade.0.20240815193440-a96bc921e732/go.mod h1:RkQLaQ961Xe/sUb3ycn4Qi18vPPuEetTqDf2eDcquAs= +github.com/ava-labs/avalanche-network-runner v1.8.4-0.20241005224128-cc3c07bb1344 h1:wD/rBr+QKztcKtRtBNqPjzMhwcxnVcuJ3GT62DdgS2Q= +github.com/ava-labs/avalanche-network-runner v1.8.4-0.20241005224128-cc3c07bb1344/go.mod h1:l4QzFnujbyyyeq6oBQ4F6sw9TrTQCjD2V4vUd7ZBCCo= +github.com/ava-labs/avalanchego v1.12.0-initial-poc.3 h1:JfVooBCdMzpeGUT9/phJNl2GHflkGehlMJokXeWKa2A= +github.com/ava-labs/avalanchego v1.12.0-initial-poc.3/go.mod h1:qSHmog3wMVjo/ruIAQo0ppXAilyni07NIu5K88RyhWE= +github.com/ava-labs/awm-relayer v1.4.1-0.20241003162124-807fd305670f h1:YUQF1wQJeEcTMC5W/OrwgSFTFMS4zeCM8O02rLeEDow= +github.com/ava-labs/awm-relayer v1.4.1-0.20241003162124-807fd305670f/go.mod h1:K01Md6zPkOFRWeQyxmZ/t9HJfoNgUGqa1L8rOp35GXw= +github.com/ava-labs/coreth v0.13.8 h1:f14X3KgwHl9LwzfxlN6S4bbn5VA2rhEsNnHaRLSTo/8= +github.com/ava-labs/coreth v0.13.8/go.mod h1:t3BSv/eQv0AlDPMfEDCMMoD/jq1RkUsbFzQAFg5qBcE= github.com/ava-labs/ledger-avalanche/go v0.0.0-20240610153809-9c955cc90a95 h1:dOVbtdnZL++pENdTCNZ1nu41eYDQkTML4sWebDnnq8c= github.com/ava-labs/ledger-avalanche/go v0.0.0-20240610153809-9c955cc90a95/go.mod h1:pJxaT9bUgeRNVmNRgtCHb7sFDIRKy7CzTQVi8gGNT6g= -github.com/ava-labs/subnet-evm v0.6.9 h1:0FSPjbysSudOlMD/d0ivkli50d2ixpX3sXCi7XHfPbc= -github.com/ava-labs/subnet-evm v0.6.9/go.mod h1:dw4kg0o58dvYlndj2ZcwB7hioRc1kjg00yXYrcSmnj8= +github.com/ava-labs/subnet-evm v0.6.10 h1:uIh6bFMA4GCMVMQ3agBPxTMlYHL8FBR5FrhMR+drfKI= +github.com/ava-labs/subnet-evm v0.6.10/go.mod h1:dw4kg0o58dvYlndj2ZcwB7hioRc1kjg00yXYrcSmnj8= github.com/ava-labs/teleporter v1.0.7 h1:9H0wTWhgWeA4u6uxi9KngdBu/LOoYsfAmIZhXNzuvf8= github.com/ava-labs/teleporter v1.0.7/go.mod h1:wgCgU6vU5MtP83otpjEin8jL2jrflVBNQCTxVXMx/kU= github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= -github.com/aws/aws-sdk-go-v2 v1.30.5 h1:mWSRTwQAb0aLE17dSzztCVJWI9+cRMgqebndjwDyK0g= -github.com/aws/aws-sdk-go-v2 v1.30.5/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0= +github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U= +github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA= github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y= github.com/aws/aws-sdk-go-v2/config v1.27.26 h1:T1kAefbKuNum/AbShMsZEro6eRkeOT8YILfE9wyjAYQ= github.com/aws/aws-sdk-go-v2/config v1.27.26/go.mod h1:ivWHkAWFrw/nxty5Fku7soTIVdqZaZ7dw+tc5iGW3GA= @@ -109,10 +109,10 @@ github.com/aws/aws-sdk-go-v2/credentials v1.17.26/go.mod h1:3vAM49zkIa3q8WT6o9Ve github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2/go.mod h1:3hGg3PpiEjHnrkrlasTfxFqUsZ2GCk/fMUn4CbKgSkM= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 h1:pI7Bzt0BJtYA0N/JEC6B8fJ4RBrEMi1LBrkMdFYNSnQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17/go.mod h1:Dh5zzJYMtxfIjYW+/evjQ8uj2OyR/ve2KROHGHlSFqE= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 h1:Mqr/V5gvrhA2gvgnF42Zh5iMiQNcOYthFYwCyrnuWlc= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17/go.mod h1:aLJpZlCmjE+V+KtN1q1uyZkfnUWpQGpbsn89XPKyzfU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 h1:kYQ3H1u0ANr9KEKlGs/jTLrBFPo8P8NaH/w7A01NeeM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18/go.mod h1:r506HmK5JDUh9+Mw4CfGJGSSoqIiLCndAuqXuhbv67Y= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 h1:Z7IdFUONvTcvS7YuhtVxN99v2cCoHRXOS4mTr0B/pUc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18/go.mod h1:DkKMmksZVVyat+Y+r1dEOgJEfUeA7UngIHWeKsi0yNc= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/service/ec2 v1.162.0 h1:A1YMX7uMzXhfIEL9zc5049oQgSaH4ZeXx/sOth0dk/I= @@ -122,8 +122,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1: github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= -github.com/aws/aws-sdk-go-v2/service/kms v1.35.7 h1:v0D1LeMkA/X+JHAZWERrr+sUGOt8KrCZKnJA6KszkcE= -github.com/aws/aws-sdk-go-v2/service/kms v1.35.7/go.mod h1:K9lwD0Rsx9+NSaJKsdAdlDK4b2G4KKOEve9PzHxPoMI= +github.com/aws/aws-sdk-go-v2/service/kms v1.36.2 h1:yERi4A+3+9emx/NKSQ5bf0QP7DHCGU0bjv3pxdpDcsg= +github.com/aws/aws-sdk-go-v2/service/kms v1.36.2/go.mod h1:OHmlX4+o0XIlJAQGAHPIy0N9yZcYS/vNG+T7geSNcFw= github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7NkwbjlijluLsrIbu/iyl35RO4= github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= github.com/aws/aws-sdk-go-v2/service/sso v1.22.3 h1:Fv1vD2L65Jnp5QRsdiM64JvUM4Xe+E0JyVsRQKv6IeA= @@ -134,8 +134,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxq github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= -github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4= -github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/smithy-go v1.21.0 h1:H7L8dtDRk0P1Qm6y0ji7MCYMQObJ5R9CRpyPhRUkLYA= +github.com/aws/smithy-go v1.21.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -185,6 +185,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chelnak/ysmrr v0.4.0 h1:WMvLGPlBK0kb6wHf5z9FfNvpM6sB9765jy2ajYc1Sfs= github.com/chelnak/ysmrr v0.4.0/go.mod h1:8vCna4PJsPCb6eevtoG7Tljzfx3twpsO203Qj2gafLM= +github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM= +github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= @@ -389,8 +391,8 @@ github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= -github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= +github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -474,8 +476,9 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7 github.com/gorilla/rpc v1.2.0 h1:WvvdC2lNeT1SP32zrIce5l0ECBfbAlmrmSBsuc57wfk= github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= @@ -554,6 +557,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg= +github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= @@ -625,8 +630,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= @@ -635,6 +640,8 @@ github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i github.com/melbahja/goph v1.4.0 h1:z0PgDbBFe66lRYl3v5dGb9aFgPy0kotuQ37QOwSQFqs= github.com/melbahja/goph v1.4.0/go.mod h1:uG+VfK2Dlhk+O32zFrRlc3kYKTlV6+BtvPWd/kK7U68= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= @@ -733,8 +740,8 @@ github.com/posthog/posthog-go v1.2.24 h1:A+iG4saBJemo++VDlcWovbYf8KFFNUfrCoJtsc4 github.com/posthog/posthog-go v1.2.24/go.mod h1:uYC2l1Yktc8E+9FAHJ9QZG4vQf/NHJPD800Hsm7DzoM= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4= -github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= +github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -751,8 +758,9 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= @@ -774,6 +782,8 @@ github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWR github.com/sanity-io/litter v1.5.1 h1:dwnrSypP6q56o3lFxTU+t2fwQ9A+U5qrXVO4Qg9KwVU= github.com/sanity-io/litter v1.5.1/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= +github.com/schollz/progressbar/v3 v3.16.1 h1:RnF1neWZFzLCoGx8yp1yF7SDl4AzNDI5y4I0aUJRrZQ= +github.com/schollz/progressbar/v3 v3.16.1/go.mod h1:I2ILR76gz5VXqYMIY/LdLecvMHDPVcQm3W/MSKi1TME= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -1033,8 +1043,8 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1109,16 +1119,16 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -1221,10 +1231,10 @@ google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU= -google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1235,8 +1245,8 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM= -google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= +google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/internal/mocks/binary_checker.go b/internal/mocks/binary_checker.go index 885860e52..ec21488b8 100644 --- a/internal/mocks/binary_checker.go +++ b/internal/mocks/binary_checker.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.1. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/internal/mocks/downloader.go b/internal/mocks/downloader.go index c119d797a..1a1f45c9a 100644 --- a/internal/mocks/downloader.go +++ b/internal/mocks/downloader.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.1. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/internal/mocks/info.go b/internal/mocks/info.go index da4f831cb..efd1b3bdb 100644 --- a/internal/mocks/info.go +++ b/internal/mocks/info.go @@ -432,14 +432,14 @@ func (_m *InfoClient) Upgrades(_a0 context.Context, _a1 ...rpc.Option) (*upgrade return r0, r1 } -// Uptime provides a mock function with given fields: _a0, _a1, _a2 -func (_m *InfoClient) Uptime(_a0 context.Context, _a1 ids.ID, _a2 ...rpc.Option) (*info.UptimeResponse, error) { - _va := make([]interface{}, len(_a2)) - for _i := range _a2 { - _va[_i] = _a2[_i] +// Uptime provides a mock function with given fields: _a0, _a1 +func (_m *InfoClient) Uptime(_a0 context.Context, _a1 ...rpc.Option) (*info.UptimeResponse, error) { + _va := make([]interface{}, len(_a1)) + for _i := range _a1 { + _va[_i] = _a1[_i] } var _ca []interface{} - _ca = append(_ca, _a0, _a1) + _ca = append(_ca, _a0) _ca = append(_ca, _va...) ret := _m.Called(_ca...) @@ -449,19 +449,19 @@ func (_m *InfoClient) Uptime(_a0 context.Context, _a1 ids.ID, _a2 ...rpc.Option) var r0 *info.UptimeResponse var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ids.ID, ...rpc.Option) (*info.UptimeResponse, error)); ok { - return rf(_a0, _a1, _a2...) + if rf, ok := ret.Get(0).(func(context.Context, ...rpc.Option) (*info.UptimeResponse, error)); ok { + return rf(_a0, _a1...) } - if rf, ok := ret.Get(0).(func(context.Context, ids.ID, ...rpc.Option) *info.UptimeResponse); ok { - r0 = rf(_a0, _a1, _a2...) + if rf, ok := ret.Get(0).(func(context.Context, ...rpc.Option) *info.UptimeResponse); ok { + r0 = rf(_a0, _a1...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*info.UptimeResponse) } } - if rf, ok := ret.Get(1).(func(context.Context, ids.ID, ...rpc.Option) error); ok { - r1 = rf(_a0, _a1, _a2...) + if rf, ok := ret.Get(1).(func(context.Context, ...rpc.Option) error); ok { + r1 = rf(_a0, _a1...) } else { r1 = ret.Error(1) } diff --git a/internal/mocks/installer.go b/internal/mocks/installer.go index 323ff8645..5fa59ad64 100644 --- a/internal/mocks/installer.go +++ b/internal/mocks/installer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.1. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/internal/mocks/network.go b/internal/mocks/network.go index 0a09ba3ed..56ce44811 100644 --- a/internal/mocks/network.go +++ b/internal/mocks/network.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.1. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/internal/mocks/plugin_binary_downloader.go b/internal/mocks/plugin_binary_downloader.go index 465ebf69e..1972dbd49 100644 --- a/internal/mocks/plugin_binary_downloader.go +++ b/internal/mocks/plugin_binary_downloader.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.1. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/internal/mocks/process_checker.go b/internal/mocks/process_checker.go index 39602919b..2398cd337 100644 --- a/internal/mocks/process_checker.go +++ b/internal/mocks/process_checker.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.1. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/internal/mocks/prompter.go b/internal/mocks/prompter.go index d9fa0f805..67991970f 100644 --- a/internal/mocks/prompter.go +++ b/internal/mocks/prompter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.26.1. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -28,6 +28,10 @@ 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 { @@ -54,6 +58,10 @@ 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 CaptureAddresses") + } + var r0 []common.Address var r1 error if rf, ok := ret.Get(0).(func(string) ([]common.Address, error)); ok { @@ -80,6 +88,10 @@ 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 { @@ -104,6 +116,10 @@ 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 { @@ -128,6 +144,10 @@ 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 { @@ -152,6 +172,10 @@ 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 { @@ -176,6 +200,10 @@ 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 { @@ -200,6 +228,10 @@ 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 { @@ -224,6 +256,10 @@ 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 { @@ -250,6 +286,10 @@ 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 { @@ -276,6 +316,10 @@ 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 { @@ -296,23 +340,27 @@ func (_m *Prompter) CaptureIndex(promptStr string, options []interface{}) (int, return r0, r1 } -// CaptureInt provides a mock function with given fields: promptStr -func (_m *Prompter) CaptureInt(promptStr string) (int, error) { - ret := _m.Called(promptStr) +// CaptureInt provides a mock function with given fields: promptStr, validator +func (_m *Prompter) CaptureInt(promptStr string, validator func(int) error) (int, error) { + ret := _m.Called(promptStr, validator) + + 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 { - return rf(promptStr) + if rf, ok := ret.Get(0).(func(string, func(int) error) (int, error)); ok { + return rf(promptStr, validator) } - if rf, ok := ret.Get(0).(func(string) int); ok { - r0 = rf(promptStr) + if rf, ok := ret.Get(0).(func(string, func(int) error) int); ok { + r0 = rf(promptStr, validator) } else { r0 = ret.Get(0).(int) } - if rf, ok := ret.Get(1).(func(string) error); ok { - r1 = rf(promptStr) + if rf, ok := ret.Get(1).(func(string, func(int) error) error); ok { + r1 = rf(promptStr, validator) } else { r1 = ret.Error(1) } @@ -324,6 +372,10 @@ 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 { @@ -348,6 +400,10 @@ 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 { @@ -372,6 +428,10 @@ 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 { @@ -396,6 +456,10 @@ 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 { @@ -420,6 +484,10 @@ 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 { @@ -444,6 +512,10 @@ 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 { @@ -470,6 +542,10 @@ 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 { @@ -494,6 +570,10 @@ 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 { @@ -520,6 +600,10 @@ 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 { @@ -544,6 +628,10 @@ 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 { @@ -568,6 +656,10 @@ 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 { @@ -592,6 +684,10 @@ 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 { @@ -616,6 +712,10 @@ 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 { @@ -640,6 +740,10 @@ 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 { @@ -664,6 +768,10 @@ 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 { @@ -688,6 +796,10 @@ 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 { @@ -712,6 +824,10 @@ 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 { @@ -736,6 +852,10 @@ 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 { @@ -760,6 +880,10 @@ func (_m *Prompter) CaptureValidatedString(promptStr string, validator func(stri func (_m *Prompter) CaptureValidatorBalance(promptStr string) (uint64, error) { ret := _m.Called(promptStr) + if len(ret) == 0 { + panic("no return value specified for CaptureValidatorBalance") + } + var r0 uint64 var r1 error if rf, ok := ret.Get(0).(func(string) (uint64, error)); ok { @@ -784,6 +908,10 @@ func (_m *Prompter) CaptureValidatorBalance(promptStr string) (uint64, error) { 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 { @@ -808,6 +936,10 @@ 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 { @@ -832,6 +964,10 @@ 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 { @@ -856,6 +992,10 @@ 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 { @@ -880,6 +1020,10 @@ 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 { @@ -900,13 +1044,12 @@ func (_m *Prompter) ChooseKeyOrLedger(goal string) (bool, error) { return r0, r1 } -type mockConstructorTestingTNewPrompter interface { +// 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 { mock.TestingT Cleanup(func()) -} - -// 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 { +}) *Prompter { mock := &Prompter{} mock.Mock.Test(t) diff --git a/internal/mocks/publisher.go b/internal/mocks/publisher.go index 743dbe573..45d6028a6 100644 --- a/internal/mocks/publisher.go +++ b/internal/mocks/publisher.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.1. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks diff --git a/pkg/application/app.go b/pkg/application/app.go index 474f5e247..c1668f8d9 100644 --- a/pkg/application/app.go +++ b/pkg/application/app.go @@ -160,7 +160,7 @@ func (app *Avalanche) GetSubnetEVMBinDir() string { } func (app *Avalanche) GetUpgradeBytesFilepath(blockchainName string) string { - return filepath.Join(app.GetSubnetDir(), blockchainName, constants.UpgradeBytesFileName) + return filepath.Join(app.GetSubnetDir(), blockchainName, constants.UpgradeFileName) } func (app *Avalanche) GetCustomVMPath(blockchainName string) string { @@ -286,7 +286,7 @@ func (app *Avalanche) GetKey(keyName string, network models.Network, createIfMis } func (app *Avalanche) GetUpgradeBytesFilePath(blockchainName string) string { - return filepath.Join(app.GetSubnetDir(), blockchainName, constants.UpgradeBytesFileName) + return filepath.Join(app.GetSubnetDir(), blockchainName, constants.UpgradeFileName) } func (app *Avalanche) GetDownloader() Downloader { diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 6e2d3b62a..72edee75c 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -26,6 +26,7 @@ const ( SuffixSeparator = "_" SidecarFileName = "sidecar.json" GenesisFileName = "genesis.json" + UpgradeFileName = "upgrade.json" AliasesFileName = "aliases.json" SidecarSuffix = SuffixSeparator + SidecarFileName GenesisSuffix = SuffixSeparator + GenesisFileName @@ -154,7 +155,8 @@ const ( TimeParseLayout = "2006-01-02 15:04:05" MinStakeWeight = 1 // Default balance when we prompt users for bootstrap validators - BootstrapValidatorBalance = 1 + // nAVAX + BootstrapValidatorBalance = 1000000000 // Default weight when we prompt users for bootstrap validators BootstrapValidatorWeight = 1000000 DefaultStakeWeight = 20 @@ -274,6 +276,8 @@ const ( DevnetLocalAWMRelayerMetricsPort = 9092 FujiLocalAWMRelayerMetricsPort = 9093 + DevnetFlagsProposerVMUseCurrentHeight = true + SubnetEVMBin = "subnet-evm" DefaultNodeRunURL = "http://127.0.0.1:9650" @@ -328,7 +332,6 @@ const ( AvalancheGoDockerImage = "avaplatform/avalanchego" AvalancheGoGitRepo = "https://github.com/ava-labs/avalanchego" - UpgradeBytesFileName = "upgrade.json" UpgradeBytesLockExtension = ".lock" NotAvailableLabel = "Not available" BackendCmd = "avalanche-cli-backend" diff --git a/pkg/contract/contract.go b/pkg/contract/contract.go index eeb051b4e..a2f3eccc3 100644 --- a/pkg/contract/contract.go +++ b/pkg/contract/contract.go @@ -12,6 +12,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/evm" "github.com/ava-labs/avalanche-cli/pkg/utils" + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/common" @@ -283,6 +284,9 @@ func ParseSpec( return name, string(abiBytes), nil } +// get method name and types from [methodsSpec], then call it +// at the smart contract [contractAddress] with the given [params]. +// also send [payment] tokens to it func TxToMethod( rpcURL string, privateKey string, @@ -326,6 +330,63 @@ func TxToMethod( return tx, receipt, nil } +// get method name and types from [methodsSpec], then call it +// at the smart contract [contractAddress] with the given [params]. +// send [warpMessage] on the same call, whose signature is +// going to be verified previously to pass it to the method +// also send [payment] tokens to it +func TxToMethodWithWarpMessage( + rpcURL string, + privateKey string, + contractAddress common.Address, + warpMessage *avalancheWarp.Message, + payment *big.Int, + methodSpec string, + params ...interface{}, +) (*types.Transaction, *types.Receipt, error) { + methodName, methodABI, err := ParseSpec(methodSpec, nil, false, false, false, false, params...) + if err != nil { + return nil, nil, err + } + metadata := &bind.MetaData{ + ABI: methodABI, + } + abi, err := metadata.GetAbi() + if err != nil { + return nil, nil, err + } + callData, err := abi.Pack(methodName, params...) + if err != nil { + return nil, nil, err + } + client, err := evm.GetClient(rpcURL) + if err != nil { + return nil, nil, err + } + defer client.Close() + tx, err := evm.GetSignedTxToMethodWithWarpMessage( + client, + privateKey, + warpMessage, + contractAddress, + callData, + payment, + ) + if err != nil { + return nil, nil, err + } + if err := evm.SendTransaction(client, tx); err != nil { + return nil, nil, err + } + receipt, success, err := evm.WaitForTransaction(client, tx) + if err != nil { + return tx, nil, err + } else if !success { + return tx, receipt, ErrFailedReceiptStatus + } + return tx, receipt, nil +} + func CallToMethod( rpcURL string, contractAddress common.Address, diff --git a/pkg/evm/evm.go b/pkg/evm/evm.go index 92ade8440..eb3aea977 100644 --- a/pkg/evm/evm.go +++ b/pkg/evm/evm.go @@ -3,6 +3,7 @@ package evm import ( + "context" "crypto/ecdsa" "fmt" "math/big" @@ -10,11 +11,16 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/ux" + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/ethclient" + "github.com/ava-labs/subnet-evm/interfaces" + "github.com/ava-labs/subnet-evm/params" + "github.com/ava-labs/subnet-evm/precompile/contracts/warp" + "github.com/ava-labs/subnet-evm/predicate" "github.com/ava-labs/subnet-evm/rpc" - subnetEvmUtils "github.com/ava-labs/subnet-evm/tests/utils" + subnetEvmUtils "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" ) @@ -176,6 +182,28 @@ func EstimateBaseFee( return baseFee, err } +func EstimateGasLimit( + client ethclient.Client, + msg interfaces.CallMsg, +) (uint64, error) { + var ( + gasLimit uint64 + err error + ) + for i := 0; i < repeatsOnFailure; i++ { + ctx, cancel := utils.GetAPILargeContext() + defer cancel() + gasLimit, err = client.EstimateGas(ctx, msg) + if err == nil { + break + } + err = fmt.Errorf("failure estimating gas limit on %#v: %w", client, err) + ux.Logger.RedXToUser("%s", err) + time.Sleep(sleepBetweenRepeats) + } + return gasLimit, err +} + func FundAddress( client ethclient.Client, sourceAddressPrivateKeyStr string, @@ -221,6 +249,62 @@ func FundAddress( return nil } +func GetSignedTxToMethodWithWarpMessage( + client ethclient.Client, + privateKeyStr string, + warpMessage *avalancheWarp.Message, + contract common.Address, + callData []byte, + value *big.Int, +) (*types.Transaction, error) { + privateKey, err := crypto.HexToECDSA(privateKeyStr) + if err != nil { + return nil, err + } + address := crypto.PubkeyToAddress(privateKey.PublicKey) + gasFeeCap, gasTipCap, nonce, err := CalculateTxParams(client, address.Hex()) + if err != nil { + return nil, err + } + chainID, err := GetChainID(client) + if err != nil { + return nil, err + } + accessList := types.AccessList{ + types.AccessTuple{ + Address: warp.ContractAddress, + StorageKeys: subnetEvmUtils.BytesToHashSlice(predicate.PackPredicate(warpMessage.Bytes())), + }, + } + msg := interfaces.CallMsg{ + From: address, + To: &contract, + GasPrice: nil, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Value: value, + Data: callData, + AccessList: accessList, + } + gasLimit, err := EstimateGasLimit(client, msg) + if err != nil { + return nil, err + } + tx := types.NewTx(&types.DynamicFeeTx{ + ChainID: chainID, + Nonce: nonce, + To: &contract, + Gas: gasLimit, + GasFeeCap: gasFeeCap, + GasTipCap: gasTipCap, + Value: value, + Data: callData, + AccessList: accessList, + }) + txSigner := types.LatestSignerForChainID(chainID) + return types.SignTx(tx, txSigner, privateKey) +} + func IssueTx( client ethclient.Client, txStr string, @@ -435,7 +519,7 @@ func IssueTxsToActivateProposerVMFork( for i := 0; i < repeatsOnFailure; i++ { ctx, cancel := utils.GetAPILargeContext() defer cancel() - err = subnetEvmUtils.IssueTxsToActivateProposerVMFork(ctx, chainID, privKey, client) + err = issueTxsToActivateProposerVMFork(client, ctx, chainID, privKey) if err == nil { break } @@ -449,3 +533,73 @@ func IssueTxsToActivateProposerVMFork( } return err } + +// issueTxsToActivateProposerVMFork issues transactions at the current +// timestamp, which should be after the ProposerVM activation time (aka +// ApricotPhase4). This should generate a PostForkBlock because its parent block +// (genesis) has a timestamp (0) that is greater than or equal to the fork +// activation time of 0. Therefore, subsequent blocks should be built with +// BuildBlockWithContext. +func issueTxsToActivateProposerVMFork( + client ethclient.Client, + ctx context.Context, + chainID *big.Int, + fundedKey *ecdsa.PrivateKey, +) error { + const numTriggerTxs = 2 // Number of txs needed to activate the proposer VM fork + addr := crypto.PubkeyToAddress(fundedKey.PublicKey) + nonce, err := client.NonceAt(ctx, addr, nil) + if err != nil { + return err + } + + gasPrice := big.NewInt(params.MinGasPrice) + txSigner := types.LatestSignerForChainID(chainID) + for i := 0; i < numTriggerTxs; i++ { + prevBlockNumber, err := client.BlockNumber(ctx) + if err != nil { + return err + } + tx := types.NewTransaction( + nonce, addr, common.Big1, params.TxGas, gasPrice, nil) + triggerTx, err := types.SignTx(tx, txSigner, fundedKey) + if err != nil { + return err + } + if err := client.SendTransaction(ctx, triggerTx); err != nil { + return err + } + if err := WaitForNewBlock(client, ctx, prevBlockNumber, 0, 0); err != nil { + return err + } + nonce++ + } + return nil +} + +func WaitForNewBlock( + client ethclient.Client, + ctx context.Context, + prevBlockNumber uint64, + totalDuration time.Duration, + stepDuration time.Duration, +) error { + if stepDuration == 0 { + stepDuration = 1 * time.Second + } + if totalDuration == 0 { + totalDuration = 5 * time.Second + } + steps := totalDuration / stepDuration + for seconds := 0; seconds < int(steps); seconds++ { + blockNumber, err := client.BlockNumber(ctx) + if err != nil { + return err + } + if blockNumber > prevBlockNumber { + return nil + } + time.Sleep(stepDuration) + } + return fmt.Errorf("new block not produced in %f seconds", totalDuration.Seconds()) +} diff --git a/pkg/models/sidecar.go b/pkg/models/sidecar.go index e165f3a39..502766a4d 100644 --- a/pkg/models/sidecar.go +++ b/pkg/models/sidecar.go @@ -45,6 +45,8 @@ type Sidecar struct { // TODO: remove if not needed for subnet acp 77 create flow once avalnache go releases etna ValidatorManagement ValidatorManagementType PoAValidatorManagerOwner string + // Subnet defaults to Sovereign post ACP-77 + Sovereign bool } func (sc Sidecar) GetVMID() (string, error) { diff --git a/pkg/prompts/prompts.go b/pkg/prompts/prompts.go index 674798724..94af78ac8 100644 --- a/pkg/prompts/prompts.go +++ b/pkg/prompts/prompts.go @@ -108,7 +108,7 @@ type Prompter interface { CaptureWeight(promptStr string) (uint64, error) CaptureValidatorBalance(promptStr string) (uint64, error) CapturePositiveInt(promptStr string, comparators []Comparator) (int, error) - CaptureInt(promptStr string) (int, error) + CaptureInt(promptStr string, validator func(int) error) (int, error) CaptureUint32(promptStr string) (uint32, error) CaptureUint64(promptStr string) (uint64, error) CaptureFloat(promptStr string, validator func(float64) error) (float64, error) @@ -296,15 +296,15 @@ func (*realPrompter) CaptureWeight(promptStr string) (uint64, error) { return strconv.ParseUint(amountStr, 10, 64) } -func (*realPrompter) CaptureInt(promptStr string) (int, error) { +func (*realPrompter) CaptureInt(promptStr string, validator func(int) error) (int, error) { prompt := promptui.Prompt{ Label: promptStr, Validate: func(input string) error { - _, err := strconv.Atoi(input) + val, err := strconv.Atoi(input) if err != nil { return err } - return nil + return validator(val) }, } input, err := prompt.Run() diff --git a/pkg/prompts/validations.go b/pkg/prompts/validations.go index 38f2d2d35..1d6f4bad9 100644 --- a/pkg/prompts/validations.go +++ b/pkg/prompts/validations.go @@ -363,3 +363,10 @@ func ValidateHexa(input string) error { } return err } + +func ValidatePositiveInt(val int) error { + if val <= 0 { + return fmt.Errorf("value must be greater than cero") + } + return nil +} diff --git a/pkg/remoteconfig/avalanche.go b/pkg/remoteconfig/avalanche.go index 835e99851..181bc51f0 100644 --- a/pkg/remoteconfig/avalanche.go +++ b/pkg/remoteconfig/avalanche.go @@ -13,35 +13,38 @@ import ( ) type AvalancheConfigInputs struct { - HTTPHost string - APIAdminEnabled bool - IndexEnabled bool - NetworkID string - DBDir string - LogDir string - PublicIP string - StateSyncEnabled bool - PruningEnabled bool - Aliases []string - BlockChainID string - TrackSubnets string - BootstrapIDs string - BootstrapIPs string - GenesisPath string + HTTPHost string + APIAdminEnabled bool + IndexEnabled bool + NetworkID string + DBDir string + LogDir string + PublicIP string + StateSyncEnabled bool + PruningEnabled bool + Aliases []string + BlockChainID string + TrackSubnets string + BootstrapIDs string + BootstrapIPs string + GenesisPath string + UpgradePath string + ProposerVMUseCurrentHeight bool } func PrepareAvalancheConfig(publicIP string, networkID string, subnets []string) AvalancheConfigInputs { return AvalancheConfigInputs{ - HTTPHost: "127.0.0.1", - NetworkID: networkID, - DBDir: "/.avalanchego/db/", - LogDir: "/.avalanchego/logs/", - PublicIP: publicIP, - StateSyncEnabled: true, - PruningEnabled: false, - TrackSubnets: strings.Join(subnets, ","), - Aliases: nil, - BlockChainID: "", + HTTPHost: "127.0.0.1", + NetworkID: networkID, + DBDir: "/.avalanchego/db/", + LogDir: "/.avalanchego/logs/", + PublicIP: publicIP, + StateSyncEnabled: true, + PruningEnabled: false, + TrackSubnets: strings.Join(subnets, ","), + Aliases: nil, + BlockChainID: "", + ProposerVMUseCurrentHeight: constants.DevnetFlagsProposerVMUseCurrentHeight, } } diff --git a/pkg/remoteconfig/templates/avalanche-node.tmpl b/pkg/remoteconfig/templates/avalanche-node.tmpl index c5704fd1f..45d7849fd 100644 --- a/pkg/remoteconfig/templates/avalanche-node.tmpl +++ b/pkg/remoteconfig/templates/avalanche-node.tmpl @@ -2,6 +2,7 @@ "http-host": "{{.HTTPHost}}", "api-admin-enabled": {{.APIAdminEnabled}}, "index-enabled": {{.IndexEnabled}}, + "proposervm-use-current-height-bool": {{.ProposerVMUseCurrentHeight}}, "network-id": "{{if .NetworkID}}{{.NetworkID}}{{else}}fuji{{end}}", {{- if .BootstrapIDs }} "bootstrap-ids": "{{ .BootstrapIDs }}", @@ -12,6 +13,9 @@ {{- if .GenesisPath }} "genesis-file": "{{ .GenesisPath }}", {{- end }} +{{- if .UpgradePath }} + "upgrade-file": "{{ .UpgradePath }}", +{{- end }} {{- if .PublicIP }} "public-ip": "{{.PublicIP}}", {{- else }} diff --git a/pkg/ssh/ssh.go b/pkg/ssh/ssh.go index 7c2f1d486..325bc7ccd 100644 --- a/pkg/ssh/ssh.go +++ b/pkg/ssh/ssh.go @@ -429,6 +429,13 @@ func RunSSHSetupDevNet(host *models.Host, nodeInstanceDirPath string) error { ); err != nil { return err } + if err := host.Upload( + filepath.Join(nodeInstanceDirPath, constants.UpgradeFileName), + filepath.Join(constants.CloudNodeConfigPath, constants.UpgradeFileName), + constants.SSHFileOpsTimeout, + ); err != nil { + return err + } if err := host.Upload( filepath.Join(nodeInstanceDirPath, constants.NodeFileName), filepath.Join(constants.CloudNodeConfigPath, constants.NodeFileName), @@ -555,6 +562,9 @@ func RunSSHRenderAvalancheNodeConfig( if genesisFileExists(host) { avagoConf.GenesisPath = filepath.Join(constants.DockerNodeConfigPath, constants.GenesisFileName) } + if upgradeFileExists(host) { + avagoConf.UpgradePath = filepath.Join(constants.DockerNodeConfigPath, constants.UpgradeFileName) + } if network.Kind == models.Local || network.Kind == models.Devnet || isAPIHost { avagoConf.HTTPHost = "0.0.0.0" } @@ -898,6 +908,11 @@ func genesisFileExists(host *models.Host) bool { return genesisFileExists } +func upgradeFileExists(host *models.Host) bool { + upgradeFileExists, _ := host.FileExists(filepath.Join(constants.CloudNodeConfigPath, constants.UpgradeFileName)) + return upgradeFileExists +} + func nodeConfigFileExists(host *models.Host) bool { nodeConfigFileExists, _ := host.FileExists(remoteconfig.GetRemoteAvalancheNodeConfig()) return nodeConfigFileExists diff --git a/pkg/subnet/public.go b/pkg/subnet/public.go index 9a608f5e4..3f717f723 100644 --- a/pkg/subnet/public.go +++ b/pkg/subnet/public.go @@ -9,10 +9,13 @@ import ( "time" "github.com/ava-labs/avalanchego/vms/platformvm/fx" + avagofee "github.com/ava-labs/avalanchego/vms/platformvm/txs/fee" "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/signer" + goethereumcommon "github.com/ethereum/go-ethereum/common" + "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/components/verify" @@ -36,6 +39,8 @@ import ( "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" ) +const showFees = true + var ErrNoSubnetAuthKeysInWallet = errors.New("auth wallet does not contain subnet auth keys") type PublicDeployer struct { @@ -114,15 +119,15 @@ func (d *PublicDeployer) AddValidatorNonSOV( return false, tx, remainingSubnetAuthKeys, nil } -func (d *PublicDeployer) SetSubnetValidatorWeight( +func (d *PublicDeployer) SetL1ValidatorWeight( message warp.Message, ) (*txs.Tx, error) { return nil, nil } -func (d *PublicDeployer) RegisterSubnetValidator( +func (d *PublicDeployer) RegisterL1Validator( balance uint64, - signer signer.Signer, + signer signer.ProofOfPossession, changeOwner fx.Owner, message warp.Message, ) (*txs.Tx, error) { @@ -378,15 +383,15 @@ func (d *PublicDeployer) DeployBlockchain( return isFullySigned, id, tx, remainingSubnetAuthKeys, nil } -// TODO: update ConvertSubnet once avalanchego implementation is up for ACP77 -func (d *PublicDeployer) ConvertSubnet( +func (d *PublicDeployer) ConvertL1( controlKeys []string, subnetAuthKeysStrs []string, subnetID ids.ID, chainID ids.ID, - // validators []blockchaincmd.SubnetValidator, + validatorManagerAddress goethereumcommon.Address, + validators []*txs.ConvertSubnetValidator, ) (bool, ids.ID, *txs.Tx, []string, error) { - ux.Logger.PrintToUser("Now calling ConvertSubnet Tx...") + ux.Logger.PrintToUser("Now calling ConvertL1 Tx...") wallet, err := d.loadCacheWallet(subnetID) if err != nil { @@ -398,12 +403,9 @@ func (d *PublicDeployer) ConvertSubnet( return false, ids.Empty, nil, nil, fmt.Errorf("failure parsing subnet auth keys: %w", err) } - showLedgerSignatureMsg(d.kc.UsesLedger, d.kc.HasOnlyOneKey(), "ConvertSubnet transaction") - - var validatorManagerAddress []byte - // var validators []avalanchego.SubnetValidator + showLedgerSignatureMsg(d.kc.UsesLedger, d.kc.HasOnlyOneKey(), "ConvertL1 transaction") - tx, err := d.createConvertSubnetTx(subnetAuthKeys, subnetID, chainID, validatorManagerAddress, wallet) + tx, err := d.createConvertL1Tx(subnetAuthKeys, subnetID, chainID, validatorManagerAddress.Bytes(), validators, wallet) if err != nil { return false, ids.Empty, nil, nil, err } @@ -425,6 +427,43 @@ func (d *PublicDeployer) ConvertSubnet( return isFullySigned, id, tx, remainingSubnetAuthKeys, nil } +func (d *PublicDeployer) PChainTransfer( + destination ids.ShortID, + amount uint64, +) (ids.ID, *txs.Tx, error) { + wallet, err := d.loadCacheWallet() + if err != nil { + return ids.Empty, nil, err + } + to := secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{destination}, + } + output := &avax.TransferableOutput{ + Asset: avax.Asset{ID: wallet.P().Builder().Context().AVAXAssetID}, + Out: &secp256k1fx.TransferOutput{ + Amt: amount, + OutputOwners: to, + }, + } + outputs := []*avax.TransferableOutput{output} + unsignedTx, err := wallet.P().Builder().NewBaseTx( + outputs, + ) + if err != nil { + return ids.Empty, nil, err + } + tx := txs.Tx{Unsigned: unsignedTx} + if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { + return ids.Empty, nil, err + } + id, err := d.Commit(&tx, true) + if err != nil { + return ids.Empty, nil, err + } + return id, &tx, nil +} + func (d *PublicDeployer) Commit( tx *txs.Tx, waitForTxAcceptance bool, @@ -458,7 +497,7 @@ func (d *PublicDeployer) Commit( time.Sleep(sleepBetweenRepeats) } if issueTxErr != nil { - d.cleanCacheWallet() + d.CleanCacheWallet() } return tx.ID(), issueTxErr } @@ -512,7 +551,7 @@ func (d *PublicDeployer) loadWallet(subnetIDs ...ids.ID) (primary.Wallet, error) return wallet, nil } -func (d *PublicDeployer) cleanCacheWallet() { +func (d *PublicDeployer) CleanCacheWallet() { d.wallet = nil } @@ -564,6 +603,11 @@ func (d *PublicDeployer) createBlockchainTx( if err != nil { return nil, fmt.Errorf("error building tx: %w", err) } + if unsignedTx != nil { + if err := printFee("CreateChainTx", wallet, unsignedTx); err != nil { + return nil, err + } + } tx := txs.Tx{Unsigned: unsignedTx} // sign with current wallet if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { @@ -572,15 +616,35 @@ func (d *PublicDeployer) createBlockchainTx( return &tx, nil } -func (d *PublicDeployer) createConvertSubnetTx( +func (d *PublicDeployer) createConvertL1Tx( subnetAuthKeys []ids.ShortID, subnetID ids.ID, chainID ids.ID, address []byte, - // validators []avalanchego.SubnetValidator, + validators []*txs.ConvertSubnetValidator, wallet primary.Wallet, ) (*txs.Tx, error) { - return nil, nil + options := d.getMultisigTxOptions(subnetAuthKeys) + unsignedTx, err := wallet.P().Builder().NewConvertSubnetTx( + subnetID, + chainID, + address, + validators, + options..., + ) + if err != nil { + return nil, fmt.Errorf("error building tx: %w", err) + } + if unsignedTx != nil { + if err := printFee("ConvertSubnetTX", wallet, unsignedTx); err != nil { + return nil, err + } + } + tx := txs.Tx{Unsigned: unsignedTx} + if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil { + return nil, fmt.Errorf("error signing tx: %w", err) + } + return &tx, nil } func (d *PublicDeployer) createTransferSubnetOwnershipTx( @@ -767,6 +831,11 @@ func (d *PublicDeployer) createSubnetTx(controlKeys []string, threshold uint32, unsignedTx, err := wallet.P().Builder().NewCreateSubnetTx( owners, ) + if unsignedTx != nil { + if err := printFee("CreateSubnetTx", wallet, unsignedTx); err != nil { + return ids.Empty, err + } + } if err != nil { return ids.Empty, fmt.Errorf("error building tx: %w", err) } @@ -778,6 +847,31 @@ func (d *PublicDeployer) createSubnetTx(controlKeys []string, threshold uint32, return d.Commit(&tx, true) } +func printFee(kind string, wallet primary.Wallet, unsignedTx txs.UnsignedTx) error { + if showFees { + var pFeeCalculator avagofee.Calculator + pContext := wallet.P().Builder().Context() + calcKind := "dynamic" + if pContext.GasPrice != 0 { + pFeeCalculator = avagofee.NewDynamicCalculator(pContext.ComplexityWeights, pContext.GasPrice) + } else { + pFeeCalculator = avagofee.NewStaticCalculator(pContext.StaticFeeConfig) + calcKind = "static" + } + txFee, err := pFeeCalculator.CalculateFee(unsignedTx) + if err != nil { + if errors.Is(err, avagofee.ErrUnsupportedTx) { + ux.Logger.PrintToUser(logging.Yellow.Wrap("unable to get %s fee: not supported by %s calculator"), kind, calcKind) + } else { + return err + } + } else { + ux.Logger.PrintToUser(logging.Yellow.Wrap("%s fee: %.9f AVAX"), kind, float64(txFee)/float64(units.Avax)) + } + } + return nil +} + func (d *PublicDeployer) getSubnetAuthAddressesInWallet(subnetAuth []ids.ShortID) []ids.ShortID { walletAddrs := d.kc.Addresses().List() subnetAuthInWallet := []ids.ShortID{} diff --git a/pkg/teleporter/relayer.go b/pkg/teleporter/relayer.go index aa83037dd..034321668 100644 --- a/pkg/teleporter/relayer.go +++ b/pkg/teleporter/relayer.go @@ -30,9 +30,11 @@ import ( ) const ( - localRelayerSetupTime = 2 * time.Second - localRelayerCheckPoolTime = 100 * time.Millisecond - localRelayerCheckTimeout = 3 * time.Second + localRelayerSetupTime = 2 * time.Second + localRelayerCheckPoolTime = 100 * time.Millisecond + localRelayerCheckTimeout = 3 * time.Second + defaultDBWriteIntervalSeconds = 10 + defaultSignatureCacheSize = 1024 * 1024 ) var teleporterRelayerRequiredBalance = big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(500)) // 500 AVAX @@ -362,6 +364,8 @@ func CreateBaseRelayerConfig( SourceBlockchains: []*config.SourceBlockchain{}, DestinationBlockchains: []*config.DestinationBlockchain{}, MetricsPort: metricsPort, + DBWriteIntervalSeconds: defaultDBWriteIntervalSeconds, + SignatureCacheSize: defaultSignatureCacheSize, } return saveRelayerConfig(awmRelayerConfig, relayerConfigPath) } diff --git a/pkg/txutils/auth.go b/pkg/txutils/auth.go index e00e84e6b..1177d56f6 100644 --- a/pkg/txutils/auth.go +++ b/pkg/txutils/auth.go @@ -37,6 +37,8 @@ func GetAuthSigners(tx *txs.Tx, controlKeys []string) ([]string, error) { subnetAuth = unsignedTx.SubnetAuth case *txs.TransferSubnetOwnershipTx: subnetAuth = unsignedTx.SubnetAuth + case *txs.ConvertSubnetTx: + subnetAuth = unsignedTx.SubnetAuth default: return nil, fmt.Errorf("unexpected unsigned tx type %T", unsignedTx) } diff --git a/pkg/txutils/info.go b/pkg/txutils/info.go index 52168bd3b..c801b7a65 100644 --- a/pkg/txutils/info.go +++ b/pkg/txutils/info.go @@ -31,6 +31,8 @@ func GetNetwork(tx *txs.Tx) (models.Network, error) { networkID = unsignedTx.NetworkID case *txs.TransferSubnetOwnershipTx: networkID = unsignedTx.NetworkID + case *txs.ConvertSubnetTx: + networkID = unsignedTx.NetworkID default: return models.UndefinedNetwork, fmt.Errorf("unexpected unsigned tx type %T", unsignedTx) } @@ -58,6 +60,8 @@ func GetSubnetID(tx *txs.Tx) (ids.ID, error) { subnetID = unsignedTx.Subnet case *txs.TransferSubnetOwnershipTx: subnetID = unsignedTx.Subnet + case *txs.ConvertSubnetTx: + subnetID = unsignedTx.Subnet default: return ids.Empty, fmt.Errorf("unexpected unsigned tx type %T", unsignedTx) } diff --git a/pkg/ux/progressbar.go b/pkg/ux/progressbar.go new file mode 100644 index 000000000..90362654b --- /dev/null +++ b/pkg/ux/progressbar.go @@ -0,0 +1,47 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package ux + +import ( + "fmt" + "time" + + ansi "github.com/k0kubun/go-ansi" + progressbar "github.com/schollz/progressbar/v3" +) + +func TimedProgressBar( + duration time.Duration, + title string, + extraSteps int, +) (*progressbar.ProgressBar, error) { + const steps = 1000 + stepDuration := duration / steps + bar := progressbar.NewOptions(steps+extraSteps, + progressbar.OptionSetWriter(ansi.NewAnsiStdout()), + progressbar.OptionEnableColorCodes(true), + progressbar.OptionSetElapsedTime(false), + progressbar.OptionSetWidth(15), + progressbar.OptionSetDescription(title), + progressbar.OptionSetTheme(progressbar.Theme{ + Saucer: "[green]=[reset]", + SaucerHead: "[green]>[reset]", + SaucerPadding: " ", + BarStart: "[", + BarEnd: "]", + })) + for i := 0; i < steps; i++ { + if err := bar.Add(1); err != nil { + return nil, err + } + time.Sleep(stepDuration) + } + if extraSteps == 0 { + fmt.Println() + } + return bar, nil +} + +func ExtraStepExecuted(bar *progressbar.ProgressBar) error { + return bar.Add(1) +} diff --git a/pkg/ux/spinner.go b/pkg/ux/spinner.go index 49e3f732e..c9cf17f33 100644 --- a/pkg/ux/spinner.go +++ b/pkg/ux/spinner.go @@ -11,6 +11,7 @@ import ( "github.com/chelnak/ysmrr" "github.com/chelnak/ysmrr/pkg/animations" "github.com/chelnak/ysmrr/pkg/colors" + ansi "github.com/k0kubun/go-ansi" ) type UserSpinner struct { @@ -55,6 +56,7 @@ func (us *UserSpinner) SpinToUser(msg string, args ...interface{}) *ysmrr.Spinne } func SpinFailWithError(s *ysmrr.Spinner, txt string, err error) { + ansi.CursorShow() if txt == "" { s.ErrorWithMessagef("%s err:%v", s.GetMessage(), err) } else { diff --git a/pkg/validatormanager/deployed_poa_validator_manager_bytecode.txt b/pkg/validatormanager/deployed_poa_validator_manager_bytecode.txt index 7e19811ae..02cd97464 100644 --- a/pkg/validatormanager/deployed_poa_validator_manager_bytecode.txt +++ b/pkg/validatormanager/deployed_poa_validator_manager_bytecode.txt @@ -1 +1 @@ -0x608060405234801561000f575f80fd5b506004361061011c575f3560e01c80638994ab49116100a9578063bee0a03f1161006e578063bee0a03f146102b4578063c974d1b6146102c7578063d5f20ff6146102cf578063df93d8de146102ef578063f2fde38b146102f9575f80fd5b80638994ab49146102295780638da5cb5b1461023c57806397fb70d414610280578063a3a65e4814610293578063b771b3bc146102a6575f80fd5b806360305d62116100ef57806360305d62146101ac57806361e2f490146101c957806366435abf146101dc578063715018a6146102075780638280a25a1461020f575f80fd5b80630322ed98146101205780630cdd098514610135578063467ef06f14610186578063580a400614610199575b5f80fd5b61013361012e366004612db8565b61030c565b005b610173610143366004612db8565b5f9081527fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb09602052604090205490565b6040519081526020015b60405180910390f35b610133610194366004612de7565b6104ca565b6101336101a7366004612e2a565b6104d8565b6101b4601481565b60405163ffffffff909116815260200161017d565b6101336101d7366004612e60565b6105e6565b6101ef6101ea366004612db8565b610c1a565b6040516001600160401b03909116815260200161017d565b610133610c2e565b610217603081565b60405160ff909116815260200161017d565b610173610237366004612ec0565b610c41565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b03165b6040516001600160a01b03909116815260200161017d565b61013361028e366004612db8565b610c5d565b6101336102a1366004612de7565b610c71565b6102686005600160991b0181565b6101336102c2366004612db8565b610e29565b610217601481565b6102e26102dd366004612db8565b610f38565b60405161017d9190612f19565b6101ef6202a30081565b610133610307366004612fcf565b61102b565b5f8181525f805160206137fa8339815191526020526040808220815160e0810190925280545f805160206137da83398151915293929190829060ff16600581111561035957610359612f05565b600581111561036a5761036a612f05565b81526001820154602082015260028201546001600160401b038082166040840152600160401b820481166060840152600160801b820481166080840152600160c01b909104811660a08301526003928301541660c090910152909150815160058111156103d9576103d9612f05565b146104435760405162461bcd60e51b815260206004820152602f60248201527f56616c696461746f724d616e616765723a2056616c696461746f72206e6f742060448201526e1c195b991a5b99c81c995b5bdd985b608a1b60648201526084015b60405180910390fd5b6005600160991b016001600160a01b031663ee5b48eb6104688584606001515f611065565b6040518263ffffffff1660e01b81526004016104849190613013565b6020604051808303815f875af11580156104a0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104c49190613045565b50505050565b6104d38161128a565b505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f8115801561051c5750825b90505f826001600160401b031660011480156105375750303b155b905081158015610545575080155b156105635760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561058d57845460ff60401b1916600160401b1785555b6105978787611602565b83156105dd57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b7fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb0a545f805160206137da8339815191529060ff16156106835760405162461bcd60e51b815260206004820152603360248201527f56616c696461746f724d616e616765723a20616c726561647920696e697469616044820152721b1a5e9959081d985b1a59185d1bdc881cd95d606a1b606482015260840161043a565b6005600160991b016001600160a01b0316634213cf786040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106c6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106ea9190613045565b83602001351461074c5760405162461bcd60e51b815260206004820152602760248201527f56616c696461746f724d616e616765723a20696e76616c696420626c6f636b636044820152661a185a5b88125160ca1b606482015260840161043a565b3061075d6060850160408601612fcf565b6001600160a01b0316146107cf5760405162461bcd60e51b815260206004820152603360248201527f56616c696461746f724d616e616765723a20696e76616c69642076616c696461604482015272746f72206d616e61676572206164647265737360681b606482015260840161043a565b5f6107dd606085018561305c565b91505f90508435602086013560146107fb6060890160408a01612fcf565b60408051602081019590955284019290925260e090811b6001600160e01b03199081166060808601919091529290921b6bffffffffffffffffffffffff1916606484015284901b166078820152607c0160405160208183030381529060405290505f805b838163ffffffff161015610b2a575f61087b606089018961305c565b8363ffffffff16818110610891576108916130a1565b90506020028101906108a391906130b5565b6108ac90613165565b80515f81815260098901602052604090205491925090156108df5760405162461bcd60e51b815260040161043a906131fc565b84825f0151836020015184604001516040516020016109019493929190613244565b60408051808303601f190181528282528b35602084015260e086901b6001600160e01b031916838301528151602481850301815260449093019182905296505f9160029161094f9190613293565b602060405180830381855afa15801561096a573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061098d9190613045565b5f83815260098a0160209081526040808320849055805160e0810182526002815287518184015287830180516001600160401b039081168385015260608301869052905181166080830152421660a082015260c0810184905284845260088d01909252909120815181549394509192909190829060ff19166001836005811115610a1957610a19612f05565b0217905550602082810151600183015560408301516002830180546060860151608087015160a08801516001600160401b039586166001600160801b031990941693909317600160401b92861692909202919091176001600160801b0316600160801b918516919091026001600160c01b031617600160c01b9184169190910217905560c0909301516003909201805467ffffffffffffffff191692841692909217909155840151610acc9116866132b8565b83516020808601516040516001600160401b039091168152929750909183917fb815f891730222788b3f8d66249b3a287ce680c3df13866fd9a4f37743ae1014910160405180910390a350505080610b23906132cb565b905061085f565b50600584018190555f610b3c86611620565b90505f610b4c82604001516117fa565b905080600285604051610b5f9190613293565b602060405180830381855afa158015610b7a573d5f803e3d5ffd5b5050506040513d601f19601f82011682018060405250810190610b9d9190613045565b14610c015760405162461bcd60e51b815260206004820152602e60248201527f56616c696461746f724d616e616765723a20696e76616c6964207375626e657460448201526d0818dbdb9d995c9cda5bdb88125160921b606482015260840161043a565b505050600a909201805460ff1916600117905550505050565b5f610c2482610f38565b6080015192915050565b610c36611963565b610c3f5f6119be565b565b5f610c4a611963565b610c548383611a2e565b90505b92915050565b610c65611963565b610c6e81611f93565b50565b5f805160206137da8339815191525f610c8983611620565b90505f80610c9a8360400151612204565b9150915080610cfc5760405162461bcd60e51b815260206004820152602860248201527f56616c696461746f724d616e616765723a20526567697374726174696f6e206e6044820152671bdd081d985b1a5960c21b606482015260840161043a565b5f82815260078501602052604081208054610d16906132ed565b9050118015610d48575060015f83815260088601602052604090205460ff166005811115610d4657610d46612f05565b145b610d645760405162461bcd60e51b815260040161043a9061331f565b5f8281526007850160205260408120610d7c91612d6e565b5f8281526008850160208181526040808420805460ff191660029081178255810180546001600160401b0342818116600160c01b026001600160c01b03909316929092178355600190930154875260098b0185528387208990559588905293835292548151600160801b90910490931683529082019290925283917ff8fd1c90fb9cfa2ca2358fdf5806b086ad43315d92b221c929efc7f105ce7568910160405180910390a25050505050565b5f8181527fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb076020526040812080545f805160206137da833981519152929190610e71906132ed565b9050118015610ea3575060015f83815260088301602052604090205460ff166005811115610ea157610ea1612f05565b145b610ebf5760405162461bcd60e51b815260040161043a9061331f565b5f82815260078201602052604090819020905163ee5b48eb60e01b81526005600160991b019163ee5b48eb91610ef89190600401613366565b6020604051808303815f875af1158015610f14573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104d39190613045565b6040805160e080820183525f8083526020808401829052838501829052606084018290526080840182905260a0840182905260c084018290528582525f805160206137fa83398151915290528390208351918201909352825491925f805160206137da83398151915292829060ff166005811115610fb857610fb8612f05565b6005811115610fc957610fc9612f05565b81526001820154602082015260028201546001600160401b038082166040840152600160401b820481166060840152600160801b820481166080840152600160c01b909104811660a083015260039092015490911660c0909101529392505050565b611033611963565b6001600160a01b03811661105c57604051631e4fbdf760e01b81525f600482015260240161043a565b610c6e816119be565b60408051603680825260608281019093525f91906020820181803683370190505090505f5b60028110156110de5761109e8160016133f0565b6110a9906008613403565b5081515f908390839081106110c0576110c06130a1565b60200101906001600160f81b03191690815f1a90535060010161108a565b505f5b6004811015611149576110f58160036133f0565b611100906008613403565b600263ffffffff16901c60f81b8282600261111b91906132b8565b8151811061112b5761112b6130a1565b60200101906001600160f81b03191690815f1a9053506001016110e1565b505f5b60208110156111ab5761116081601f6133f0565b61116b906008613403565b86901c60f81b8261117d8360066132b8565b8151811061118d5761118d6130a1565b60200101906001600160f81b03191690815f1a90535060010161114c565b505f5b6008811015611216576111c28160076133f0565b6111cd906008613403565b6001600160401b038616901c60f81b826111e88360266132b8565b815181106111f8576111f86130a1565b60200101906001600160f81b03191690815f1a9053506001016111ae565b505f5b60088110156112815761122d8160076133f0565b611238906008613403565b6001600160401b038516901c60f81b8261125383602e6132b8565b81518110611263576112636130a1565b60200101906001600160f81b03191690815f1a905350600101611219565b50949350505050565b6040805160e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c081018290525f805160206137da8339815191525f6112d985611620565b90505f806112ea8360400151612204565b91509150801561134f5760405162461bcd60e51b815260206004820152602a60248201527f56616c696461746f724d616e616765723a20726567697374726174696f6e20736044820152691d1a5b1b081d985b1a5960b21b606482015260840161043a565b5f828152600885016020526040808220815160e081019092528054829060ff16600581111561138057611380612f05565b600581111561139157611391612f05565b81526001820154602082015260028201546001600160401b038082166040840152600160401b820481166060840152600160801b820481166080840152600160c01b909104811660a08301526003928301541660c0909101529091505f908251600581111561140257611402612f05565b1480611420575060018251600581111561141e5761141e612f05565b145b61147f5760405162461bcd60e51b815260206004820152602a60248201527f56616c696461746f724d616e616765723a20696e76616c69642076616c696461604482015269746f722073746174757360b01b606482015260840161043a565b60038251600581111561149457611494612f05565b036114a1575060046114a5565b5060055b6020808301515f908152600988019091526040812055818160058111156114ce576114ce612f05565b908160058111156114e1576114e1612f05565b9052505f84815260088701602052604090208251815484929190829060ff1916600183600581111561151557611515612f05565b02179055506020820151600182015560408201516002820180546060850151608086015160a08701516001600160401b039586166001600160801b031990941693909317600160401b92861692909202919091176001600160801b0316600160801b918516919091026001600160c01b031617600160c01b9184169190910217905560c0909201516003909101805467ffffffffffffffff191691909216179055815160058111156115c9576115c9612f05565b60405185907f1c08e59656f1a18dc2da76826cdc52805c43e897a17c50faefb8ab3c1526cc16905f90a350919791965090945050505050565b61160a6123a5565b611613826123ee565b61161c81612407565b5050565b60408051606080820183525f8083526020830181905292820152905f805160206137da8339815191526040516306f8253560e41b815263ffffffff851660048201529091505f9081906005600160991b0190636f825350906024015f60405180830381865afa158015611695573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526116bc9190810190613429565b915091508061171c5760405162461bcd60e51b815260206004820152602660248201527f56616c696461746f724d616e616765723a20696e76616c69642077617270206d60448201526565737361676560d01b606482015260840161043a565b825482511461177f5760405162461bcd60e51b815260206004820152602960248201527f56616c696461746f724d616e616765723a20696e76616c696420736f757263656044820152680818da185a5b88125160ba1b606482015260840161043a565b60208201516001600160a01b0316156117f25760405162461bcd60e51b815260206004820152602f60248201527f56616c696461746f724d616e616765723a20696e76616c6964206f726967696e60448201526e2073656e646572206164647265737360881b606482015260840161043a565b509392505050565b5f815160261461181c5760405162461bcd60e51b815260040161043a906134fb565b5f805b600281101561186b576118338160016133f0565b61183e906008613403565b61ffff16848281518110611854576118546130a1565b016020015160f81c901b919091179060010161181f565b5061ffff81161561188e5760405162461bcd60e51b815260040161043a90613544565b5f805b60048110156118e9576118a58160036133f0565b6118b0906008613403565b63ffffffff16856118c28360026132b8565b815181106118d2576118d26130a1565b016020015160f81c901b9190911790600101611891565b5063ffffffff81161561190e5760405162461bcd60e51b815260040161043a90613587565b5f805b60208110156112815761192581601f6133f0565b611930906008613403565b8661193c8360066132b8565b8151811061194c5761194c6130a1565b016020015160f81c901b9190911790600101611911565b336119957f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b031614610c3f5760405163118cdaa760e01b815233600482015260240161043a565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b7fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb0a545f9060ff16611ab95760405162461bcd60e51b815260206004820152602f60248201527f56616c696461746f724d616e616765723a2076616c696461746f72207365742060448201526e1b9bdd081a5b9a5d1a585b1a5e9959608a1b606482015260840161043a565b5f805160206137da83398151915242611ad860408601602087016135ce565b6001600160401b031611611b4a5760405162461bcd60e51b815260206004820152603360248201527f56616c696461746f724d616e616765723a20726567697374726174696f6e20656044820152727870697279206e6f7420696e2066757475726560681b606482015260840161043a565b611b5a60408501602086016135ce565b6001600160401b0316611b706202a300426132b8565b11611be35760405162461bcd60e51b815260206004820152603760248201527f56616c696461746f724d616e616765723a20726567697374726174696f6e206560448201527f787069727920746f6f2066617220696e20667574757265000000000000000000606482015260840161043a565b8335611c3b5760405162461bcd60e51b815260206004820152602160248201527f56616c696461746f724d616e616765723a20696e76616c6964206e6f646520496044820152601160fa1b606482015260840161043a565b83355f90815260098201602052604090205415611c6a5760405162461bcd60e51b815260040161043a906131fc565b6030611c7960408601866135e7565b905014611cde5760405162461bcd60e51b815260206004820152602d60248201527f56616c696461746f724d616e616765723a20696e76616c696420626c7350756260448201526c0d8d2c696caf240d8cadccee8d609b1b606482015260840161043a565b611ce8835f612418565b5f80611d826040518060a0016040528085600101548152602001885f01358152602001876001600160401b03168152602001886020016020810190611d2d91906135ce565b6001600160401b03168152602001611d4860408a018a6135e7565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505050915250612626565b5f82815260078601602052604090209193509150611da08282613674565b5060405163ee5b48eb60e01b81525f906005600160991b019063ee5b48eb90611dcd908590600401613013565b6020604051808303815f875af1158015611de9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e0d9190613045565b6040805160e08101909152909150806001815288356020808301919091526001600160401b03891660408084018290525f60608501819052608085019290925260a0840182905260c0909301819052868152600888019091522081518154829060ff19166001836005811115611e8557611e85612f05565b021790555060208281015160018301556040808401516002840180546060870151608088015160a08901516001600160401b039586166001600160801b031990941693909317600160401b92861692909202919091176001600160801b0316600160801b918516919091026001600160c01b031617600160c01b9184169190910217905560c0909401516003909301805467ffffffffffffffff19169390941692909217909255829189359186917f79b81620b81daf2c08cd5bb3dbb79e75d2d7a87f52171fde5aadc8c47823026e918b91611f65918e01908e016135ce565b604080516001600160401b0393841681529290911660208301520160405180910390a4509095945050505050565b5f8181525f805160206137fa8339815191526020526040808220815160e0810190925280545f805160206137da83398151915293929190829060ff166005811115611fe057611fe0612f05565b6005811115611ff157611ff1612f05565b8152600182015460208201526002808301546001600160401b038082166040850152600160401b820481166060850152600160801b820481166080850152600160c01b909104811660a084015260039093015490921660c0909101529091508151600581111561206357612063612f05565b146120bf5760405162461bcd60e51b815260206004820152602660248201527f56616c696461746f724d616e616765723a2076616c696461746f72206e6f742060448201526561637469766560d01b606482015260840161043a565b60038152426001600160401b031660c08201525f83815260088301602052604090208151815483929190829060ff1916600183600581111561210357612103612f05565b02179055506020820151600182015560408201516002820180546060850151608086015160a08701516001600160401b039586166001600160801b031990941693909317600160401b92861692909202919091176001600160801b0316600160801b918516919091026001600160c01b031617600160c01b9184169190910217905560c0909201516003909101805467ffffffffffffffff1916919092161790555f6121af84826129d5565b6080840151604080516001600160401b03909216825242602083015291935083925086917f13d58394cf269d48bcf927959a29a5ffee7c9924dafff8927ecdf3c48ffa7c67910160405180910390a350505050565b5f8082516027146122275760405162461bcd60e51b815260040161043a906134fb565b5f805b60028110156122765761223e8160016133f0565b612249906008613403565b61ffff1685828151811061225f5761225f6130a1565b016020015160f81c901b919091179060010161222a565b5061ffff8116156122995760405162461bcd60e51b815260040161043a90613544565b5f805b60048110156122f4576122b08160036133f0565b6122bb906008613403565b63ffffffff16866122cd8360026132b8565b815181106122dd576122dd6130a1565b016020015160f81c901b919091179060010161229c565b5063ffffffff811660031461231b5760405162461bcd60e51b815260040161043a90613587565b5f805b60208110156123705761233281601f6133f0565b61233d906008613403565b876123498360066132b8565b81518110612359576123596130a1565b016020015160f81c901b919091179060010161231e565b505f86602681518110612385576123856130a1565b016020015191976001600160f81b03199092161515965090945050505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff16610c3f57604051631afcd79f60e31b815260040160405180910390fd5b6123f66123a5565b6123fe612b2c565b610c6e81612b34565b61240f6123a5565b610c6e81612cd6565b5f805160206137da8339815191525f6001600160401b03808416908516111561244c576124458385613733565b9050612459565b6124568484613733565b90505b6040805160808101825260038401548082526004850154602083015260058501549282019290925260068401546001600160401b03166060820152429115806124bb5750600284015481516124b7916001600160401b0316906132b8565b8210155b156124e1576001600160401b038316606082015281815260408101516020820152612500565b82816060018181516124f39190613753565b6001600160401b03169052505b6060810151612510906064613773565b602082015160028601546001600160401b03929092169161253b9190600160401b900460ff16613403565b101561259f5760405162461bcd60e51b815260206004820152602d60248201527f56616c696461746f724d616e616765723a206d6178696d756d20636875726e2060448201526c1c985d1948195e18d959591959609a1b606482015260840161043a565b856001600160401b0316816040018181516125ba91906132b8565b9052506040810180516001600160401b03871691906125da9083906133f0565b905250805160038501556020810151600485015560408101516005850155606001516006909301805467ffffffffffffffff19166001600160401b039094169390931790925550505050565b5f60608260800151516030146126905760405162461bcd60e51b815260206004820152602960248201527f5374616b696e674d657373616765733a20696e76616c6964207369676e6174756044820152680e4ca40d8cadccee8d60bb1b606482015260840161043a565b60408051608680825260c082019092525f916020820181803683370190505090505f5b6002811015612707576126c78160016133f0565b6126d2906008613403565b5081515f908390839081106126e9576126e96130a1565b60200101906001600160f81b03191690815f1a9053506001016126b3565b505f5b600481101561276f5761271e8160036133f0565b612729906008613403565b600160ff919091161c60f81b826127418360026132b8565b81518110612751576127516130a1565b60200101906001600160f81b03191690815f1a90535060010161270a565b505f5b60208110156127cc578451816020811061278e5761278e6130a1565b1a60f81b8261279e8360066132b8565b815181106127ae576127ae6130a1565b60200101906001600160f81b03191690815f1a905350600101612772565b505f5b602081101561282c57846020015181602081106127ee576127ee6130a1565b1a60f81b826127fe8360266132b8565b8151811061280e5761280e6130a1565b60200101906001600160f81b03191690815f1a9053506001016127cf565b505f5b60088110156128a0576128438160076133f0565b61284e906008613403565b60ff1685604001516001600160401b0316901c60f81b8282604661287291906132b8565b81518110612882576128826130a1565b60200101906001600160f81b03191690815f1a90535060010161282f565b505f5b603081101561290b57846080015181815181106128c2576128c26130a1565b01602001516001600160f81b031916826128dd83604e6132b8565b815181106128ed576128ed6130a1565b60200101906001600160f81b03191690815f1a9053506001016128a3565b505f5b600881101561297d576129228160076133f0565b61292d906008613403565b60608601516001600160401b0390811691161c60f81b8261294f83607e6132b8565b8151811061295f5761295f6130a1565b60200101906001600160f81b03191690815f1a90535060010161290e565b5060028160405161298e9190613293565b602060405180830381855afa1580156129a9573d5f803e3d5ffd5b5050506040513d601f19601f820116820180604052508101906129cc9190613045565b94909350915050565b5f8281525f805160206137fa833981519152602052604081206002015481905f805160206137da83398151915290600160801b90046001600160401b0316612a1d8582612418565b5f612a2787612cde565b5f8881526008850160205260408120600201805467ffffffffffffffff60801b1916600160801b6001600160401b038b16021790559091506005600160991b0163ee5b48eb612a778a858b611065565b6040518263ffffffff1660e01b8152600401612a939190613013565b6020604051808303815f875af1158015612aaf573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ad39190613045565b604080516001600160401b038a811682526020820184905282519394508516928b927f07de5ff35a674a8005e661f3333c907ca6333462808762d19dc7b3abb1a8c1df928290030190a3909450925050505b9250929050565b610c3f6123a5565b612b3c6123a5565b80355f805160206137da83398151915290815560208201357fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb01556014612b88608084016060850161379e565b60ff161115612bf55760405162461bcd60e51b815260206004820152603360248201527f56616c696461746f724d616e616765723a206d6178696d756d20636875726e206044820152720e0cae4c6cadce8c2ceca40e8dede40d0d2ced606b1b606482015260840161043a565b5f612c06608084016060850161379e565b60ff1611612c6e5760405162461bcd60e51b815260206004820152602f60248201527f56616c696461746f724d616e616765723a207a65726f206d6178696d756d206360448201526e6875726e2070657263656e7461676560881b606482015260840161043a565b612c7e608083016060840161379e565b60028201805460ff92909216600160401b0260ff60401b19909216919091179055612caf60608301604084016135ce565b600291909101805467ffffffffffffffff19166001600160401b0390921691909117905550565b6110336123a5565b5f8181525f805160206137fa8339815191526020526040812060020180545f805160206137da83398151915291600160401b9091046001600160401b0316906008612d28836137be565b82546101009290920a6001600160401b038181021990931691831602179091555f94855260089290920160205250604090922060020154600160401b9004909116919050565b508054612d7a906132ed565b5f825580601f10612d89575050565b601f0160209004905f5260205f2090810190610c6e91905b80821115612db4575f8155600101612da1565b5090565b5f60208284031215612dc8575f80fd5b5035919050565b803563ffffffff81168114612de2575f80fd5b919050565b5f60208284031215612df7575f80fd5b610c5482612dcf565b5f60808284031215612e10575f80fd5b50919050565b6001600160a01b0381168114610c6e575f80fd5b5f8060a08385031215612e3b575f80fd5b612e458484612e00565b91506080830135612e5581612e16565b809150509250929050565b5f8060408385031215612e71575f80fd5b82356001600160401b03811115612e86575f80fd5b612e9285828601612e00565b925050612ea160208401612dcf565b90509250929050565b80356001600160401b0381168114612de2575f80fd5b5f8060408385031215612ed1575f80fd5b82356001600160401b03811115612ee6575f80fd5b830160608186031215612ef7575f80fd5b9150612ea160208401612eaa565b634e487b7160e01b5f52602160045260245ffd5b815160e082019060068110612f3c57634e487b7160e01b5f52602160045260245ffd5b80835250602083015160208301526001600160401b0360408401511660408301526060830151612f7760608401826001600160401b03169052565b506080830151612f9260808401826001600160401b03169052565b5060a0830151612fad60a08401826001600160401b03169052565b5060c0830151612fc860c08401826001600160401b03169052565b5092915050565b5f60208284031215612fdf575f80fd5b8135612fea81612e16565b9392505050565b5f5b8381101561300b578181015183820152602001612ff3565b50505f910152565b602081525f8251806020840152613031816040850160208701612ff1565b601f01601f19169190910160400192915050565b5f60208284031215613055575f80fd5b5051919050565b5f808335601e19843603018112613071575f80fd5b8301803591506001600160401b0382111561308a575f80fd5b6020019150600581901b3603821315612b25575f80fd5b634e487b7160e01b5f52603260045260245ffd5b5f8235605e198336030181126130c9575f80fd5b9190910192915050565b634e487b7160e01b5f52604160045260245ffd5b604051606081016001600160401b0381118282101715613109576131096130d3565b60405290565b604051601f8201601f191681016001600160401b0381118282101715613137576131376130d3565b604052919050565b5f6001600160401b03821115613157576131576130d3565b50601f01601f191660200190565b5f60608236031215613175575f80fd5b61317d6130e7565b82358152602061318e818501612eaa565b8183015260408401356001600160401b038111156131aa575f80fd5b840136601f8201126131ba575f80fd5b80356131cd6131c88261313f565b61310f565b81815236848385010111156131e0575f80fd5b81848401858301375f9181019093015250604082015292915050565b60208082526028908201527f56616c696461746f724d616e616765723a206e6f646520494420616c72656164604082015267792061637469766560c01b606082015260800190565b5f8551613255818460208a01612ff1565b80830190508581526001600160401b0360c01b8560c01b1660208201528351613285816028840160208801612ff1565b016028019695505050505050565b5f82516130c9818460208701612ff1565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610c5757610c576132a4565b5f63ffffffff8083168181036132e3576132e36132a4565b6001019392505050565b600181811c9082168061330157607f821691505b602082108103612e1057634e487b7160e01b5f52602260045260245ffd5b60208082526027908201527f56616c696461746f724d616e616765723a20696e76616c69642076616c6964616040820152661d1a5bdb88125160ca1b606082015260800190565b5f60208083525f8454613378816132ed565b806020870152604060018084165f811461339957600181146133b5576133e2565b60ff19851660408a0152604084151560051b8a010195506133e2565b895f5260205f205f5b858110156133d95781548b82018601529083019088016133be565b8a016040019650505b509398975050505050505050565b81810381811115610c5757610c576132a4565b8082028115828204841417610c5757610c576132a4565b80518015158114612de2575f80fd5b5f806040838503121561343a575f80fd5b82516001600160401b0380821115613450575f80fd5b9084019060608287031215613463575f80fd5b61346b6130e7565b8251815260208084015161347e81612e16565b82820152604084015183811115613493575f80fd5b80850194505087601f8501126134a7575f80fd5b835192506134b76131c88461313f565b83815288828587010111156134ca575f80fd5b6134d984838301848801612ff1565b806040840152508195506134ee81880161341a565b9450505050509250929050565b60208082526029908201527f56616c696461746f724d657373616765733a20696e76616c6964206d657373616040820152680ceca40d8cadccee8d60bb1b606082015260800190565b60208082526023908201527f56616c696461746f724d657373616765733a20696e76616c696420636f64656360408201526208125160ea1b606082015260800190565b60208082526027908201527f56616c696461746f724d657373616765733a20696e76616c6964206d657373616040820152666765207479706560c81b606082015260800190565b5f602082840312156135de575f80fd5b610c5482612eaa565b5f808335601e198436030181126135fc575f80fd5b8301803591506001600160401b03821115613615575f80fd5b602001915036819003821315612b25575f80fd5b601f8211156104d357805f5260205f20601f840160051c8101602085101561364e5750805b601f840160051c820191505b8181101561366d575f815560010161365a565b5050505050565b81516001600160401b0381111561368d5761368d6130d3565b6136a18161369b84546132ed565b84613629565b602080601f8311600181146136d4575f84156136bd5750858301515b5f19600386901b1c1916600185901b17855561372b565b5f85815260208120601f198616915b82811015613702578886015182559484019460019091019084016136e3565b508582101561371f57878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b6001600160401b03828116828216039080821115612fc857612fc86132a4565b6001600160401b03818116838216019080821115612fc857612fc86132a4565b6001600160401b03818116838216028082169190828114613796576137966132a4565b505092915050565b5f602082840312156137ae575f80fd5b813560ff81168114612fea575f80fd5b5f6001600160401b038083168181036132e3576132e36132a456fee92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb00e92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb08a164736f6c6343000819000a +0x608060405234801561000f575f80fd5b5060043610610132575f3560e01c8063a3a65e48116100b4578063c974d1b611610079578063c974d1b6146102a7578063d588c18f146102af578063d5f20ff6146102c2578063df93d8de146102e2578063f2fde38b146102ec578063fd7ac5e7146102ff575f80fd5b8063a3a65e481461024c578063b18f3f1a1461025f578063b771b3bc14610272578063bc5fbfec14610280578063bee0a03f14610294575f80fd5b8063715018a6116100fa578063715018a6146101be578063732214f8146101c65780638280a25a146101db5780638da5cb5b146101f557806397fb70d414610239575f80fd5b80630322ed981461013657806320d91b7a1461014b578063467ef06f1461015e57806360305d621461017157806366435abf14610193575b5f80fd5b610149610144366004612acf565b610312565b005b610149610159366004612afe565b610529565b61014961016c366004612b4c565b610a15565b610179601481565b60405163ffffffff90911681526020015b60405180910390f35b6101a66101a1366004612acf565b610a23565b6040516001600160401b03909116815260200161018a565b610149610a37565b6101cd5f81565b60405190815260200161018a565b6101e3603081565b60405160ff909116815260200161018a565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b03165b6040516001600160a01b03909116815260200161018a565b610149610247366004612acf565b610a4a565b61014961025a366004612b4c565b610a5f565b6101cd61026d366004612b7b565b610be8565b6102216005600160991b0181565b6101cd5f805160206136c983398151915281565b6101496102a2366004612acf565b610c04565b6101e3601481565b6101496102bd366004612bd4565b610d41565b6102d56102d0366004612acf565b610e4f565b60405161018a9190612c91565b6101a66202a30081565b6101496102fa366004612d11565b610f9e565b6101cd61030d366004612d33565b610fdb565b5f8181525f805160206136e98339815191526020526040808220815160e0810190925280545f805160206136c983398151915293929190829060ff16600581111561035f5761035f612c10565b600581111561037057610370612c10565b815260200160018201805461038490612d9e565b80601f01602080910402602001604051908101604052809291908181526020018280546103b090612d9e565b80156103fb5780601f106103d2576101008083540402835291602001916103fb565b820191905f5260205f20905b8154815290600101906020018083116103de57829003601f168201915b505050918352505060028201546001600160401b038082166020840152600160401b820481166040840152600160801b820481166060840152600160c01b909104811660808301526003928301541660a0909101529091508151600581111561046657610466612c10565b146104a2575f8381526007830160205260409081902054905163170cc93360e21b81526104999160ff1690600401612dd6565b60405180910390fd5b6005600160991b016001600160a01b031663ee5b48eb6104c78584606001515f611036565b6040518263ffffffff1660e01b81526004016104e39190612de4565b6020604051808303815f875af11580156104ff573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105239190612df6565b50505050565b7fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb09545f805160206136c98339815191529060ff161561057b57604051637fab81e560e01b815260040160405180910390fd5b6005600160991b016001600160a01b0316634213cf786040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105be573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105e29190612df6565b83602001351461060b576040516372b0a7e760e11b815260208401356004820152602401610499565b3061061c6060850160408601612d11565b6001600160a01b03161461065f5761063a6060840160408501612d11565b604051632f88120d60e21b81526001600160a01b039091166004820152602401610499565b5f61066d6060850185612e0d565b905090505f805b828163ffffffff161015610955575f6106906060880188612e0d565b8363ffffffff168181106106a6576106a6612e52565b90506020028101906106b89190612e66565b6106c190612f8a565b80516040519192505f9160088801916106d991613003565b9081526020016040518091039020541461070957805160405163a41f772f60e01b81526104999190600401612de4565b5f6002885f01358460405160200161073892919091825260e01b6001600160e01b031916602082015260240190565b60408051601f198184030181529082905261075291613003565b602060405180830381855afa15801561076d573d5f803e3d5ffd5b5050506040513d601f19601f820116820180604052508101906107909190612df6565b90508086600801835f01516040516107a89190613003565b90815260408051602092819003830181209390935560e0830181526002835284518284015284810180516001600160401b03908116858401525f60608601819052915181166080860152421660a085015260c0840181905284815260078a01909252902081518154829060ff1916600183600581111561082a5761082a612c10565b021790555060208201516001820190610843908261305f565b506040828101516002830180546060860151608087015160a08801516001600160401b039586166001600160801b031990941693909317600160401b92861692909202919091176001600160801b0316600160801b918516919091026001600160c01b031617600160c01b9184169190910217905560c0909301516003909201805467ffffffffffffffff1916928416929092179091558301516108e8911685613132565b82516040519195506108f991613003565b60408051918290038220908401516001600160401b031682529082907f9d47fef9da077661546e646d61830bfcbda90506c2e5eed38195e82c4eb1cbdf9060200160405180910390a350508061094e90613145565b9050610674565b50600483018190555f61097361096a86611085565b6040015161119b565b90505f61097f87611310565b90505f6002826040516109929190613003565b602060405180830381855afa1580156109ad573d5f803e3d5ffd5b5050506040513d601f19601f820116820180604052508101906109d09190612df6565b90508281146109fc57604051631872fc8d60e01b81526004810182905260248101849052604401610499565b5050506009909201805460ff1916600117905550505050565b610a1e81611549565b505050565b5f610a2d82610e4f565b6080015192915050565b610a3f611887565b610a485f6118e2565b565b610a52611887565b610a5b81611952565b5050565b5f805160206136c98339815191525f80610a84610a7b85611085565b60400151611c36565b9150915080610aaa57604051632d07135360e01b81528115156004820152602401610499565b5f82815260068401602052604090208054610ac490612d9e565b90505f03610ae85760405163089938b360e11b815260048101839052602401610499565b60015f83815260078501602052604090205460ff166005811115610b0e57610b0e612c10565b14610b41575f8281526007840160205260409081902054905163170cc93360e21b81526104999160ff1690600401612dd6565b5f8281526006840160205260408120610b5991612a43565b5f828152600784016020908152604091829020805460ff1916600290811782550180546001600160401b0342818116600160c01b026001600160c01b0390931692909217928390558451600160801b9093041682529181019190915283917ff8fd1c90fb9cfa2ca2358fdf5806b086ad43315d92b221c929efc7f105ce7568910160405180910390a250505050565b5f610bf1611887565b610bfb8383611dda565b90505b92915050565b5f8181527fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb066020526040902080545f805160206136c98339815191529190610c4b90612d9e565b90505f03610c6f5760405163089938b360e11b815260048101839052602401610499565b60015f83815260078301602052604090205460ff166005811115610c9557610c95612c10565b14610cc8575f8281526007820160205260409081902054905163170cc93360e21b81526104999160ff1690600401612dd6565b5f82815260068201602052604090819020905163ee5b48eb60e01b81526005600160991b019163ee5b48eb91610d019190600401613167565b6020604051808303815f875af1158015610d1d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a1e9190612df6565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f81158015610d855750825b90505f826001600160401b03166001148015610da05750303b155b905081158015610dae575080155b15610dcc5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610df657845460ff60401b1916600160401b1785555b610e00878761232b565b8315610e4657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b610e57612a7a565b5f8281525f805160206136e9833981519152602052604090819020815160e0810190925280545f805160206136c9833981519152929190829060ff166005811115610ea457610ea4612c10565b6005811115610eb557610eb5612c10565b8152602001600182018054610ec990612d9e565b80601f0160208091040260200160405190810160405280929190818152602001828054610ef590612d9e565b8015610f405780601f10610f1757610100808354040283529160200191610f40565b820191905f5260205f20905b815481529060010190602001808311610f2357829003601f168201915b505050918352505060028201546001600160401b038082166020840152600160401b820481166040840152600160801b820481166060840152600160c01b9091048116608083015260039092015490911660a0909101529392505050565b610fa6611887565b6001600160a01b038116610fcf57604051631e4fbdf760e01b81525f6004820152602401610499565b610fd8816118e2565b50565b6040515f905f805160206136c9833981519152907fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb089061101e90869086906131f1565b90815260200160405180910390205491505092915050565b604080515f6020820152600360e01b602282015260268101949094526001600160c01b031960c093841b811660468601529190921b16604e830152805180830360360181526056909201905290565b60408051606080820183525f8083526020830152918101919091526040516306f8253560e41b815263ffffffff831660048201525f9081906005600160991b0190636f825350906024015f60405180830381865afa1580156110e9573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611110919081019061320f565b915091508061113257604051636b2f19e960e01b815260040160405180910390fd5b815115611158578151604051636ba589a560e01b81526004810191909152602401610499565b60208201516001600160a01b031615611194576020820151604051624de75d60e31b81526001600160a01b039091166004820152602401610499565b5092915050565b5f81516026146111be57604051638d0242c960e01b815260040160405180910390fd5b5f805b600281101561120d576111d58160016132e1565b6111e09060086132f4565b61ffff168482815181106111f6576111f6612e52565b016020015160f81c901b91909117906001016111c1565b5061ffff8116156112315760405163059510e360e31b815260040160405180910390fd5b5f805b600481101561128c576112488160036132e1565b6112539060086132f4565b63ffffffff1685611265836002613132565b8151811061127557611275612e52565b016020015160f81c901b9190911790600101611234565b5063ffffffff8116156112b257604051635b60892f60e01b815260040160405180910390fd5b5f805b6020811015611307576112c981601f6132e1565b6112d49060086132f4565b866112e0836006613132565b815181106112f0576112f0612e52565b016020015160f81c901b91909117906001016112b5565b50949350505050565b60605f8083356020850135601461132c87870160408901612d11565b6113396060890189612e0d565b60405160f09790971b6001600160f01b0319166020880152602287019590955250604285019290925260e090811b6001600160e01b0319908116606286015260609290921b6bffffffffffffffffffffffff191660668501529190911b16607a820152607e0160405160208183030381529060405290505f5b6113bf6060850185612e0d565b9050811015611194576113d56060850185612e0d565b828181106113e5576113e5612e52565b90506020028101906113f79190612e66565b61140590602081019061330b565b905060301461142757604051634be6321b60e01b815260040160405180910390fd5b816114356060860186612e0d565b8381811061144557611445612e52565b90506020028101906114579190612e66565b611461908061330b565b90506114706060870187612e0d565b8481811061148057611480612e52565b90506020028101906114929190612e66565b61149c908061330b565b6114a96060890189612e0d565b868181106114b9576114b9612e52565b90506020028101906114cb9190612e66565b6114d990602081019061330b565b6114e660608b018b612e0d565b888181106114f6576114f6612e52565b90506020028101906115089190612e66565b61151990606081019060400161334d565b60405160200161152f9796959493929190613366565b60408051601f1981840301815291905291506001016113b2565b5f611552612a7a565b5f805160206136c98339815191525f8061156e610a7b87611085565b91509150801561159557604051632d07135360e01b81528115156004820152602401610499565b5f828152600784016020526040808220815160e081019092528054829060ff1660058111156115c6576115c6612c10565b60058111156115d7576115d7612c10565b81526020016001820180546115eb90612d9e565b80601f016020809104026020016040519081016040528092919081815260200182805461161790612d9e565b80156116625780601f1061163957610100808354040283529160200191611662565b820191905f5260205f20905b81548152906001019060200180831161164557829003601f168201915b505050918352505060028201546001600160401b038082166020840152600160401b820481166040840152600160801b820481166060840152600160c01b909104811660808301526003928301541660a090910152909150815160058111156116cd576116cd612c10565b141580156116ee57506001815160058111156116eb576116eb612c10565b14155b1561170f57805160405163170cc93360e21b81526104999190600401612dd6565b60038151600581111561172457611724612c10565b036117325760048152611737565b600581525b83600801816020015160405161174d9190613003565b90815260408051602092819003830190205f908190558581526007870190925290208151815483929190829060ff1916600183600581111561179157611791612c10565b0217905550602082015160018201906117aa908261305f565b5060408201516002820180546060850151608086015160a08701516001600160401b039586166001600160801b031990941693909317600160401b92861692909202919091176001600160801b0316600160801b918516919091026001600160c01b031617600160c01b9184169190910217905560c0909201516003909101805467ffffffffffffffff1916919092161790558051600581111561185057611850612c10565b60405184907f1c08e59656f1a18dc2da76826cdc52805c43e897a17c50faefb8ab3c1526cc16905f90a39196919550909350505050565b336118b97f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b031614610a485760405163118cdaa760e01b8152336004820152602401610499565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b61195a612a7a565b5f8281525f805160206136e98339815191526020526040808220815160e0810190925280545f805160206136c983398151915293929190829060ff1660058111156119a7576119a7612c10565b60058111156119b8576119b8612c10565b81526020016001820180546119cc90612d9e565b80601f01602080910402602001604051908101604052809291908181526020018280546119f890612d9e565b8015611a435780601f10611a1a57610100808354040283529160200191611a43565b820191905f5260205f20905b815481529060010190602001808311611a2657829003601f168201915b50505091835250506002828101546001600160401b038082166020850152600160401b820481166040850152600160801b820481166060850152600160c01b9091048116608084015260039093015490921660a09091015290915081516005811115611ab157611ab1612c10565b14611ae4575f8481526007830160205260409081902054905163170cc93360e21b81526104999160ff1690600401612dd6565b60038152426001600160401b031660c08201525f84815260078301602052604090208151815483929190829060ff19166001836005811115611b2857611b28612c10565b021790555060208201516001820190611b41908261305f565b5060408201516002820180546060850151608086015160a08701516001600160401b039586166001600160801b031990941693909317600160401b92861692909202919091176001600160801b0316600160801b918516919091026001600160c01b031617600160c01b9184169190910217905560c0909201516003909101805467ffffffffffffffff1916919092161790555f611bdf8582612345565b6080840151604080516001600160401b03909216825242602083015291935083925087917f13d58394cf269d48bcf927959a29a5ffee7c9924dafff8927ecdf3c48ffa7c67910160405180910390a3509392505050565b5f808251602714611c5a57604051638d0242c960e01b815260040160405180910390fd5b5f805b6002811015611ca957611c718160016132e1565b611c7c9060086132f4565b61ffff16858281518110611c9257611c92612e52565b016020015160f81c901b9190911790600101611c5d565b5061ffff811615611ccd5760405163059510e360e31b815260040160405180910390fd5b5f805b6004811015611d2857611ce48160036132e1565b611cef9060086132f4565b63ffffffff1686611d01836002613132565b81518110611d1157611d11612e52565b016020015160f81c901b9190911790600101611cd0565b5063ffffffff8116600214611d5057604051635b60892f60e01b815260040160405180910390fd5b5f805b6020811015611da557611d6781601f6132e1565b611d729060086132f4565b87611d7e836006613132565b81518110611d8e57611d8e612e52565b016020015160f81c901b9190911790600101611d53565b505f86602681518110611dba57611dba612e52565b016020015191976001600160f81b03199092161515965090945050505050565b7fe92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb09545f9060ff16611e1e57604051637fab81e560e01b815260040160405180910390fd5b5f805160206136c983398151915242611e3d60a086016080870161334d565b6001600160401b0316111580611e775750611e5b6202a30042613132565b611e6b60a086016080870161334d565b6001600160401b031610155b15611eb157611e8c60a085016080860161334d565b604051635879da1360e11b81526001600160401b039091166004820152602401610499565b6030611ec0602086018661330b565b905014611ef257611ed4602085018561330b565b6040516326475b2f60e11b8152610499925060040190815260200190565b611efc848061330b565b90505f03611f2957611f0e848061330b565b604051633e08a12560e11b81526004016104999291906133cf565b5f60088201611f38868061330b565b604051611f469291906131f1565b90815260200160405180910390205414611f7f57611f64848061330b565b60405163a41f772f60e01b81526004016104999291906133cf565b611f89835f61249c565b6040805160e08101909152815481525f9081906120959060208101611fae898061330b565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250505090825250602090810190611ff6908a018a61330b565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050509082525060200161203f60a08a0160808b0161334d565b6001600160401b0316815260200161205a60408a018a6133fd565b61206390613411565b815260200161207560608a018a6133fd565b61207e90613411565b8152602001876001600160401b0316815250612676565b5f828152600686016020526040902091935091506120b3828261305f565b5081600884016120c3888061330b565b6040516120d19291906131f1565b9081526040519081900360200181209190915563ee5b48eb60e01b81525f906005600160991b019063ee5b48eb9061210d908590600401612de4565b6020604051808303815f875af1158015612129573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061214d9190612df6565b6040805160e08101909152909150806001815260200161216d898061330b565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201829052509385525050506001600160401b0389166020808401829052604080850184905260608501929092526080840183905260a0909301829052868252600788019092522081518154829060ff191660018360058111156121fc576121fc612c10565b021790555060208201516001820190612215908261305f565b5060408201516002820180546060850151608086015160a08701516001600160401b039586166001600160801b031990941693909317600160401b92861692909202919091176001600160801b0316600160801b918516919091026001600160c01b031617600160c01b9184169190910217905560c0909201516003909101805467ffffffffffffffff191691909216179055806122b3888061330b565b6040516122c19291906131f1565b604051908190039020847fb77297e3befc691bfc864a81e241f83e2ef722b6e7becaa2ecec250c6d52b430896122fd60a08d0160808e0161334d565b604080516001600160401b0393841681529290911660208301520160405180910390a4509095945050505050565b612333612863565b61233c826128ac565b610a5b816128c5565b5f8281525f805160206136e9833981519152602052604081206002015481905f805160206136c983398151915290600160801b90046001600160401b031661238d858261249c565b5f612397876128d6565b5f8881526007850160205260408120600201805467ffffffffffffffff60801b1916600160801b6001600160401b038b16021790559091506005600160991b0163ee5b48eb6123e78a858b611036565b6040518263ffffffff1660e01b81526004016124039190612de4565b6020604051808303815f875af115801561241f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124439190612df6565b604080516001600160401b038a811682526020820184905282519394508516928b927f07de5ff35a674a8005e661f3333c907ca6333462808762d19dc7b3abb1a8c1df928290030190a3909450925050505b9250929050565b5f805160206136c98339815191525f6001600160401b0380841690851611156124d0576124c983856134d8565b90506124dd565b6124da84846134d8565b90505b6040805160808101825260028401548082526003850154602083015260048501549282019290925260058401546001600160401b031660608201524291158061253f57506001840154815161253b916001600160401b031690613132565b8210155b15612565576001600160401b038316606082015281815260408101516020820152612584565b828160600181815161257791906134f8565b6001600160401b03169052505b6060810151612594906064613518565b602082015160018601546001600160401b0392909216916125bf9190600160401b900460ff166132f4565b10156125ef57606081015160405163dfae880160e01b81526001600160401b039091166004820152602401610499565b856001600160401b03168160400181815161260a9190613132565b9052506040810180516001600160401b038716919061262a9083906132e1565b905250805160028501556020810151600385015560408101516004850155606001516005909301805467ffffffffffffffff19166001600160401b039094169390931790925550505050565b5f606082604001515160301461269f57604051638d0242c960e01b815260040160405180910390fd5b82516020808501518051604080880151606089015160808a01518051908701515193515f986126e0988a986001989297929690959094909390929101613543565b60405160208183030381529060405290505f5b846080015160200151518110156127525781856080015160200151828151811061271f5761271f612e52565b60200260200101516040516020016127389291906135eb565b60408051601f1981840301815291905291506001016126f3565b5060a0840151805160209182015151604051612772938593929101613621565b60405160208183030381529060405290505f5b8460a0015160200151518110156127e457818560a001516020015182815181106127b1576127b1612e52565b60200260200101516040516020016127ca9291906135eb565b60408051601f198184030181529190529150600101612785565b5060c08401516040516127fb91839160200161365c565b604051602081830303815290604052905060028160405161281c9190613003565b602060405180830381855afa158015612837573d5f803e3d5ffd5b5050506040513d601f19601f8201168201806040525081019061285a9190612df6565b94909350915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff16610a4857604051631afcd79f60e31b815260040160405180910390fd5b6128b4612863565b6128bc61294b565b610fd881612953565b6128cd612863565b610fd881612a3b565b5f8181525f805160206136e98339815191526020526040812060020180545f805160206136c9833981519152919060089061292090600160401b90046001600160401b031661368d565b91906101000a8154816001600160401b0302191690836001600160401b031602179055915050919050565b610a48612863565b61295b612863565b80355f805160206136c9833981519152908155601461298060608401604085016136a8565b60ff16118061299f575061299a60608301604084016136a8565b60ff16155b156129d3576129b460608301604084016136a8565b604051634a59bbff60e11b815260ff9091166004820152602401610499565b6129e360608301604084016136a8565b60018201805460ff92909216600160401b0260ff60401b19909216919091179055612a14604083016020840161334d565b600191909101805467ffffffffffffffff19166001600160401b0390921691909117905550565b610fa6612863565b508054612a4f90612d9e565b5f825580601f10612a5e575050565b601f0160209004905f5260205f2090810190610fd89190612ab7565b6040805160e08101909152805f81526060602082018190525f604083018190529082018190526080820181905260a0820181905260c09091015290565b5b80821115612acb575f8155600101612ab8565b5090565b5f60208284031215612adf575f80fd5b5035919050565b803563ffffffff81168114612af9575f80fd5b919050565b5f8060408385031215612b0f575f80fd5b82356001600160401b03811115612b24575f80fd5b830160808186031215612b35575f80fd5b9150612b4360208401612ae6565b90509250929050565b5f60208284031215612b5c575f80fd5b610bfb82612ae6565b80356001600160401b0381168114612af9575f80fd5b5f8060408385031215612b8c575f80fd5b82356001600160401b03811115612ba1575f80fd5b830160a08186031215612bb2575f80fd5b9150612b4360208401612b65565b6001600160a01b0381168114610fd8575f80fd5b5f808284036080811215612be6575f80fd5b6060811215612bf3575f80fd5b508291506060830135612c0581612bc0565b809150509250929050565b634e487b7160e01b5f52602160045260245ffd5b60068110612c4057634e487b7160e01b5f52602160045260245ffd5b9052565b5f5b83811015612c5e578181015183820152602001612c46565b50505f910152565b5f8151808452612c7d816020860160208601612c44565b601f01601f19169290920160200192915050565b60208152612ca3602082018351612c24565b5f602083015160e06040840152612cbe610100840182612c66565b905060408401516001600160401b0380821660608601528060608701511660808601528060808701511660a08601528060a08701511660c08601528060c08701511660e086015250508091505092915050565b5f60208284031215612d21575f80fd5b8135612d2c81612bc0565b9392505050565b5f8060208385031215612d44575f80fd5b82356001600160401b0380821115612d5a575f80fd5b818501915085601f830112612d6d575f80fd5b813581811115612d7b575f80fd5b866020828501011115612d8c575f80fd5b60209290920196919550909350505050565b600181811c90821680612db257607f821691505b602082108103612dd057634e487b7160e01b5f52602260045260245ffd5b50919050565b60208101610bfe8284612c24565b602081525f610bfb6020830184612c66565b5f60208284031215612e06575f80fd5b5051919050565b5f808335601e19843603018112612e22575f80fd5b8301803591506001600160401b03821115612e3b575f80fd5b6020019150600581901b3603821315612495575f80fd5b634e487b7160e01b5f52603260045260245ffd5b5f8235605e19833603018112612e7a575f80fd5b9190910192915050565b634e487b7160e01b5f52604160045260245ffd5b604051606081016001600160401b0381118282101715612eba57612eba612e84565b60405290565b604080519081016001600160401b0381118282101715612eba57612eba612e84565b604051601f8201601f191681016001600160401b0381118282101715612f0a57612f0a612e84565b604052919050565b5f6001600160401b03821115612f2a57612f2a612e84565b50601f01601f191660200190565b5f82601f830112612f47575f80fd5b8135612f5a612f5582612f12565b612ee2565b818152846020838601011115612f6e575f80fd5b816020850160208301375f918101602001919091529392505050565b5f60608236031215612f9a575f80fd5b612fa2612e98565b82356001600160401b0380821115612fb8575f80fd5b612fc436838701612f38565b83526020850135915080821115612fd9575f80fd5b50612fe636828601612f38565b602083015250612ff860408401612b65565b604082015292915050565b5f8251612e7a818460208701612c44565b601f821115610a1e57805f5260205f20601f840160051c810160208510156130395750805b601f840160051c820191505b81811015613058575f8155600101613045565b5050505050565b81516001600160401b0381111561307857613078612e84565b61308c816130868454612d9e565b84613014565b602080601f8311600181146130bf575f84156130a85750858301515b5f19600386901b1c1916600185901b178555613116565b5f85815260208120601f198616915b828110156130ed578886015182559484019460019091019084016130ce565b508582101561310a57878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610bfe57610bfe61311e565b5f63ffffffff80831681810361315d5761315d61311e565b6001019392505050565b5f60208083525f845461317981612d9e565b806020870152604060018084165f811461319a57600181146131b6576131e3565b60ff19851660408a0152604084151560051b8a010195506131e3565b895f5260205f205f5b858110156131da5781548b82018601529083019088016131bf565b8a016040019650505b509398975050505050505050565b818382375f9101908152919050565b80518015158114612af9575f80fd5b5f8060408385031215613220575f80fd5b82516001600160401b0380821115613236575f80fd5b9084019060608287031215613249575f80fd5b613251612e98565b8251815260208084015161326481612bc0565b82820152604084015183811115613279575f80fd5b80850194505087601f85011261328d575f80fd5b8351925061329d612f5584612f12565b83815288828587010111156132b0575f80fd5b6132bf84838301848801612c44565b806040840152508195506132d4818801613200565b9450505050509250929050565b81810381811115610bfe57610bfe61311e565b8082028115828204841417610bfe57610bfe61311e565b5f808335601e19843603018112613320575f80fd5b8301803591506001600160401b03821115613339575f80fd5b602001915036819003821315612495575f80fd5b5f6020828403121561335d575f80fd5b610bfb82612b65565b5f8851613377818460208d01612c44565b60e089901b6001600160e01b031916908301908152868860048301378681019050600481015f8152858782375060c09390931b6001600160c01b0319166004939094019283019390935250600c019695505050505050565b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b5f8235603e19833603018112612e7a575f80fd5b5f60408236031215613421575f80fd5b613429612ec0565b61343283612ae6565b81526020808401356001600160401b038082111561344e575f80fd5b9085019036601f830112613460575f80fd5b81358181111561347257613472612e84565b8060051b9150613483848301612ee2565b818152918301840191848101903684111561349c575f80fd5b938501935b838510156134c657843592506134b683612bc0565b82825293850193908501906134a1565b94860194909452509295945050505050565b6001600160401b038281168282160390808211156111945761119461311e565b6001600160401b038181168382160190808211156111945761119461311e565b6001600160401b0381811683821602808216919082811461353b5761353b61311e565b505092915050565b61ffff60f01b8a60f01b1681525f63ffffffff60e01b808b60e01b1660028401528960068401528860268401528751613583816046860160208c01612c44565b87519084019061359a816046840160208c01612c44565b60c088901b6001600160c01b0319166046929091019182015260e086901b8216604e8201526135d8605282018660e01b6001600160e01b0319169052565b6056019c9b505050505050505050505050565b5f83516135fc818460208801612c44565b60609390931b6bffffffffffffffffffffffff19169190920190815260140192915050565b5f8451613632818460208901612c44565b6001600160e01b031960e095861b8116919093019081529290931b16600482015260080192915050565b5f835161366d818460208801612c44565b60c09390931b6001600160c01b0319169190920190815260080192915050565b5f6001600160401b0380831681810361315d5761315d61311e565b5f602082840312156136b8575f80fd5b813560ff81168114612d2c575f80fdfee92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb00e92546d698950ddd38910d2e15ed1d923cd0a7b3dde9e2a6a3f380565559cb07a164736f6c6343000819000a diff --git a/pkg/validatormanager/validatormanager.go b/pkg/validatormanager/validatormanager.go index 099d78d1f..05f61f80b 100644 --- a/pkg/validatormanager/validatormanager.go +++ b/pkg/validatormanager/validatormanager.go @@ -4,12 +4,24 @@ package validatormanager import ( _ "embed" + "fmt" "math/big" "strings" + "github.com/ava-labs/avalanche-cli/pkg/application" "github.com/ava-labs/avalanche-cli/pkg/contract" + "github.com/ava-labs/avalanche-cli/pkg/evm" + "github.com/ava-labs/avalanche-cli/pkg/models" + "github.com/ava-labs/avalanche-cli/sdk/interchain" "github.com/ava-labs/avalanchego/ids" + avagoconstants "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + warp "github.com/ava-labs/avalanchego/vms/platformvm/warp" + warpMessage "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" + warpPayload "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/common" ) @@ -31,36 +43,237 @@ func AddPoAValidatorManagerContractToAllocations( } } -func InitializePoAValidatorManager( +// initializes contract [managerAddress] at [rpcURL], to +// manage validators on [subnetID], with +// owner given by [ownerAddress] +func PoAValidatorManagerInitialize( rpcURL string, - remoteAddress common.Address, + managerAddress common.Address, privateKey string, subnetID ids.ID, - initialOwner common.Address, -) error { - pChainBlockchainID := ids.Empty - churnPeriodSeconds := uint64(0) - maximumChurnPercentage := uint8(20) + ownerAddress common.Address, +) (*types.Transaction, *types.Receipt, error) { + const ( + defaultChurnPeriodSeconds = uint64(0) + defaultMaximumChurnPercentage = uint8(20) + ) type Params struct { - PChainBlockchainID [32]byte SubnetID [32]byte ChurnPeriodSeconds uint64 MaximumChurnPercentage uint8 } params := Params{ - PChainBlockchainID: pChainBlockchainID, SubnetID: subnetID, - ChurnPeriodSeconds: churnPeriodSeconds, - MaximumChurnPercentage: maximumChurnPercentage, + ChurnPeriodSeconds: defaultChurnPeriodSeconds, + MaximumChurnPercentage: defaultMaximumChurnPercentage, } - _, _, err := contract.TxToMethod( + return contract.TxToMethod( rpcURL, privateKey, - remoteAddress, + managerAddress, nil, - "initialize((bytes32,bytes32,uint64,uint8),address)", + "initialize((bytes32,uint64,uint8),address)", params, - initialOwner, + ownerAddress, + ) +} + +func TransactionError(tx *types.Transaction, err error, msg string, args ...interface{}) error { + msgSuffix := ":%w" + if tx != nil { + msgSuffix += fmt.Sprintf(" (txHash=%s)", tx.Hash().String()) + } + args = append(args, err) + return fmt.Errorf(msg+msgSuffix, args...) +} + +// constructs p-chain-validated (signed) subnet conversion warp +// message, to be sent to the validators manager when +// initializing validators set +// the message specifies [subnetID] that is being converted +// together with the validator's manager [managerBlockchainID], +// [managerAddress], and the initial list of [validators] +func PoaValidatorManagerGetPChainSubnetConversionWarpMessage( + network models.Network, + aggregatorLogger logging.Logger, + aggregatorLogLevel logging.Level, + aggregatorQuorumPercentage uint64, + subnetID ids.ID, + managerBlockchainID ids.ID, + managerAddress common.Address, + convertSubnetValidators []*txs.ConvertSubnetValidator, +) (*warp.Message, error) { + validators := []warpMessage.SubnetConversionValidatorData{} + for _, convertSubnetValidator := range convertSubnetValidators { + validators = append(validators, warpMessage.SubnetConversionValidatorData{ + NodeID: convertSubnetValidator.NodeID[:], + BLSPublicKey: convertSubnetValidator.Signer.PublicKey, + Weight: convertSubnetValidator.Weight, + }) + } + subnetConversionData := warpMessage.SubnetConversionData{ + SubnetID: subnetID, + ManagerChainID: managerBlockchainID, + ManagerAddress: managerAddress.Bytes(), + Validators: validators, + } + subnetConversionID, err := warpMessage.SubnetConversionID(subnetConversionData) + if err != nil { + return nil, err + } + addressedCallPayload, err := warpMessage.NewSubnetConversion(subnetConversionID) + if err != nil { + return nil, err + } + subnetConversionAddressedCall, err := warpPayload.NewAddressedCall( + nil, + addressedCallPayload.Bytes(), ) - return err + if err != nil { + return nil, err + } + subnetConversionUnsignedMessage, err := warp.NewUnsignedMessage( + network.ID, + avagoconstants.PlatformChainID, + subnetConversionAddressedCall.Bytes(), + ) + if err != nil { + return nil, err + } + signatureAggregator, err := interchain.NewSignatureAggregator( + network, + aggregatorLogger, + aggregatorLogLevel, + subnetID, + aggregatorQuorumPercentage, + ) + if err != nil { + return nil, err + } + return signatureAggregator.Sign(subnetConversionUnsignedMessage, subnetID[:]) +} + +// calls poa manager validators set init method, +// passing to it the p-chain signed [subnetConversionSignedMessage] +// so as to verify p-chain already proceesed the associated +// ConvertSubnetTx +func PoAValidatorManagerInitializeValidatorsSet( + rpcURL string, + managerAddress common.Address, + privateKey string, + subnetID ids.ID, + managerBlockchainID ids.ID, + convertSubnetValidators []*txs.ConvertSubnetValidator, + subnetConversionSignedMessage *warp.Message, +) (*types.Transaction, *types.Receipt, error) { + type InitialValidator struct { + NodeID []byte + BlsPublicKey []byte + Weight uint64 + } + type SubnetConversionData struct { + SubnetID [32]byte + ValidatorManagerBlockchainID [32]byte + ValidatorManagerAddress common.Address + InitialValidators []InitialValidator + } + validators := []InitialValidator{} + for _, convertSubnetValidator := range convertSubnetValidators { + validators = append(validators, InitialValidator{ + NodeID: convertSubnetValidator.NodeID[:], + BlsPublicKey: convertSubnetValidator.Signer.PublicKey[:], + Weight: convertSubnetValidator.Weight, + }) + } + subnetConversionData := SubnetConversionData{ + SubnetID: subnetID, + ValidatorManagerBlockchainID: managerBlockchainID, + ValidatorManagerAddress: managerAddress, + InitialValidators: validators, + } + return contract.TxToMethodWithWarpMessage( + rpcURL, + privateKey, + managerAddress, + subnetConversionSignedMessage, + big.NewInt(0), + "initializeValidatorSet((bytes32,bytes32,address,[(bytes,bytes,uint64)]),uint32)", + subnetConversionData, + uint32(0), + ) +} + +// setups PoA manager after a successful execution of +// ConvertSubnetTx on P-Chain +// needs the list of validators for that tx, +// [convertSubnetValidators], together with an evm [ownerAddress] +// to set as the owner of the PoA manager +func SetupPoA( + app *application.Avalanche, + network models.Network, + rpcURL string, + chainSpec contract.ChainSpec, + privateKey string, + ownerAddress common.Address, + convertSubnetValidators []*txs.ConvertSubnetValidator, +) error { + if err := evm.SetupProposerVM( + rpcURL, + privateKey, + ); err != nil { + return err + } + subnetID, err := contract.GetSubnetID( + app, + network, + chainSpec, + ) + if err != nil { + return err + } + blockchainID, err := contract.GetBlockchainID( + app, + network, + chainSpec, + ) + if err != nil { + return err + } + managerAddress := common.HexToAddress(ValidatorContractAddress) + tx, _, err := PoAValidatorManagerInitialize( + rpcURL, + managerAddress, + privateKey, + subnetID, + ownerAddress, + ) + if err != nil { + return TransactionError(tx, err, "failure initializing poa validator manager") + } + subnetConversionSignedMessage, err := PoaValidatorManagerGetPChainSubnetConversionWarpMessage( + network, + app.Log, + logging.Info, + 0, + subnetID, + blockchainID, + managerAddress, + convertSubnetValidators, + ) + if err != nil { + return fmt.Errorf("failure signing subnet conversion warp message: %w", err) + } + tx, _, err = PoAValidatorManagerInitializeValidatorsSet( + rpcURL, + managerAddress, + privateKey, + subnetID, + blockchainID, + convertSubnetValidators, + subnetConversionSignedMessage, + ) + if err != nil { + return TransactionError(tx, err, "failure initializing validators set on poa manager") + } + return nil } diff --git a/pkg/vm/create_custom.go b/pkg/vm/create_custom.go index 358820778..536c623a1 100644 --- a/pkg/vm/create_custom.go +++ b/pkg/vm/create_custom.go @@ -26,6 +26,7 @@ func CreateCustomSidecar( customVMBuildScript string, vmPath string, tokenSymbol string, + sovereign bool, ) (*models.Sidecar, error) { ux.Logger.PrintToUser("creating custom VM subnet %s", subnetName) @@ -82,7 +83,7 @@ func CreateCustomSidecar( } sc.RPCVersion = rpcVersion - + sc.Sovereign = sovereign return sc, nil } diff --git a/pkg/vm/create_evm.go b/pkg/vm/create_evm.go index 68e0305c9..417de5f53 100644 --- a/pkg/vm/create_evm.go +++ b/pkg/vm/create_evm.go @@ -38,6 +38,7 @@ func CreateEvmSidecar( subnetEVMVersion string, tokenSymbol string, getRPCVersionFromBinary bool, + sovereign bool, ) (*models.Sidecar, error) { var ( err error @@ -71,7 +72,7 @@ func CreateEvmSidecar( sc.Subnet = subnetName sc.TokenSymbol = tokenSymbol sc.TokenName = tokenSymbol + " Token" - + sc.Sovereign = sovereign return sc, nil } diff --git a/pkg/vm/precompiles.go b/pkg/vm/precompiles.go index ee32c5a64..6bd199831 100644 --- a/pkg/vm/precompiles.go +++ b/pkg/vm/precompiles.go @@ -99,7 +99,8 @@ func configureRewardManager( func configureWarp(timestamp *uint64) warp.Config { return warp.Config{ - QuorumNumerator: warp.WarpDefaultQuorumNumerator, + QuorumNumerator: warp.WarpDefaultQuorumNumerator, + RequirePrimaryNetworkSigners: true, Upgrade: precompileconfig.Upgrade{ BlockTimestamp: timestamp, }, diff --git a/scripts/regenerate_mocks.sh b/scripts/regenerate_mocks.sh index 6537a1d31..f94eee0a7 100755 --- a/scripts/regenerate_mocks.sh +++ b/scripts/regenerate_mocks.sh @@ -14,11 +14,11 @@ if ! [[ "$0" =~ scripts/regenerate_mocks.sh ]]; then exit 1 fi -go install github.com/vektra/mockery/v2@latest +go install github.com/vektra/mockery/v2@v2.43.2 -mockery -r --output ./internal/mocks --name BinaryChecker --filename binaryChecker.go -mockery -r --output ./internal/mocks --name PluginBinaryDownloader --filename pluginBinaryDownloader.go -mockery -r --output ./internal/mocks --name ProcessChecker --filename processChecker.go +mockery -r --output ./internal/mocks --name BinaryChecker --filename binary_checker.go +mockery -r --output ./internal/mocks --name PluginBinaryDownloader --filename plugin_binary_downloader.go +mockery -r --output ./internal/mocks --name ProcessChecker --filename process_checker.go mockery -r --output ./internal/mocks --name Prompter --filename prompter.go mockery -r --output ./internal/mocks --name Installer --filename installer.go mockery -r --output ./internal/mocks --name Publisher --filename publisher.go diff --git a/sdk/interchain/signature-aggregator.go b/sdk/interchain/signature-aggregator.go index 186f468b0..00d8840dc 100644 --- a/sdk/interchain/signature-aggregator.go +++ b/sdk/interchain/signature-aggregator.go @@ -12,6 +12,7 @@ import ( "github.com/ava-labs/avalanchego/message" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/vms/platformvm/warp" apiConfig "github.com/ava-labs/awm-relayer/config" "github.com/ava-labs/awm-relayer/peers" "github.com/ava-labs/awm-relayer/signature-aggregator/aggregator" @@ -80,13 +81,12 @@ func initSignatureAggregator( ) (*SignatureAggregator, error) { sa := &SignatureAggregator{} // set quorum percentage + sa.quorumPercentage = quorumPercentage if quorumPercentage == 0 { sa.quorumPercentage = DefaultQuorumPercentage } else if quorumPercentage > 100 { return nil, fmt.Errorf("quorum percentage cannot be greater than 100") } - sa.quorumPercentage = quorumPercentage - sa.subnetID = subnetID messageCreator, err := message.NewCreator( @@ -171,11 +171,26 @@ func (s *SignatureAggregator) AggregateSignatures( } // aggregate signatures - signedMessage, err := s.aggregator.CreateSignedMessage( + signedMessage, err := s.Sign( message, justificationBytes, + ) + return hex.EncodeToString(signedMessage.Bytes()), err +} + +// Sign aggregates signatures for a given message and justification. +// +// msg is the message to be signed +// justification is the justification for the signature. +// Returns the signed message, and an error if the operation fails. +func (s *SignatureAggregator) Sign( + msg *warp.UnsignedMessage, + justification []byte, +) (*warp.Message, error) { + return s.aggregator.CreateSignedMessage( + msg, + justification, s.subnetID, s.quorumPercentage, ) - return hex.EncodeToString(signedMessage.Bytes()), err } diff --git a/tests/e2e/commands/subnet.go b/tests/e2e/commands/subnet.go index c311a49c6..2f6639a15 100644 --- a/tests/e2e/commands/subnet.go +++ b/tests/e2e/commands/subnet.go @@ -23,17 +23,66 @@ const ( ) /* #nosec G204 */ -func CreateSubnetEvmConfig(subnetName string, genesisPath string) (string, string) { +func CreateSubnetEvmConfigNonSOV(subnetName string, genesisPath string) (string, string) { mapper := utils.NewVersionMapper() mapping, err := utils.GetVersionMapping(mapper) gomega.Expect(err).Should(gomega.BeNil()) // let's use a SubnetEVM version which has a guaranteed compatible avago - CreateSubnetEvmConfigWithVersion(subnetName, genesisPath, mapping[utils.LatestEVM2AvagoKey]) + CreateSubnetEvmConfigWithVersionNonSOV(subnetName, genesisPath, mapping[utils.LatestEVM2AvagoKey]) + return mapping[utils.LatestEVM2AvagoKey], mapping[utils.LatestAvago2EVMKey] +} + +func CreateSubnetEvmConfigSOV(subnetName string, genesisPath string) (string, string) { + mapper := utils.NewVersionMapper() + mapping, err := utils.GetVersionMapping(mapper) + gomega.Expect(err).Should(gomega.BeNil()) + // let's use a SubnetEVM version which has a guaranteed compatible avago + CreateSubnetEvmConfigWithVersionSOV(subnetName, genesisPath, mapping[utils.LatestEVM2AvagoKey]) return mapping[utils.LatestEVM2AvagoKey], mapping[utils.LatestAvago2EVMKey] } /* #nosec G204 */ -func CreateSubnetEvmConfigWithVersion(subnetName string, genesisPath string, version string) { +func CreateSubnetEvmConfigWithVersionNonSOV(subnetName string, genesisPath string, version string) { + // Check config does not already exist + exists, err := utils.SubnetConfigExists(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(exists).Should(gomega.BeFalse()) + + // Create config + cmdArgs := []string{ + SubnetCmd, + "create", + "--genesis", + genesisPath, + "--sovereign=false", + "--evm", + subnetName, + "--" + constants.SkipUpdateFlag, + "--teleporter=false", + "--evm-token", + "TOK", + } + if version == "" { + cmdArgs = append(cmdArgs, "--latest") + } else { + cmdArgs = append(cmdArgs, "--vm-version", version) + } + cmd := exec.Command(CLIBinary, cmdArgs...) + output, err := cmd.CombinedOutput() + if err != nil { + fmt.Println(cmd.String()) + fmt.Println(string(output)) + utils.PrintStdErr(err) + } + gomega.Expect(err).Should(gomega.BeNil()) + + // Config should now exist + exists, err = utils.SubnetConfigExists(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(exists).Should(gomega.BeTrue()) +} + +func CreateSubnetEvmConfigWithVersionSOV(subnetName string, genesisPath string, version string) { // Check config does not already exist exists, err := utils.SubnetConfigExists(subnetName) gomega.Expect(err).Should(gomega.BeNil()) @@ -112,7 +161,58 @@ func ConfigurePerNodeChainConfig(subnetName string, perNodeChainConfigPath strin } /* #nosec G204 */ -func CreateCustomVMConfig(subnetName string, genesisPath string, vmPath string) { +func CreateCustomVMConfigNonSOV(subnetName string, genesisPath string, vmPath string) { + // Check config does not already exist + exists, err := utils.SubnetConfigExists(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(exists).Should(gomega.BeFalse()) + // Check vm binary does not already exist + exists, err = utils.SubnetCustomVMExists(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(exists).Should(gomega.BeFalse()) + + // Create config + cmd := exec.Command( + CLIBinary, + SubnetCmd, + "create", + "--genesis", + genesisPath, + "--sovereign=false", + "--custom", + subnetName, + "--custom-vm-path", + vmPath, + "--"+constants.SkipUpdateFlag, + "--teleporter=false", + "--evm-token", + "TOK", + ) + output, err := cmd.CombinedOutput() + if err != nil { + var ( + exitErr *exec.ExitError + stderr string + ) + if errors.As(err, &exitErr) { + stderr = string(exitErr.Stderr) + } + fmt.Println(string(output)) + utils.PrintStdErr(err) + fmt.Println(stderr) + gomega.Expect(err).Should(gomega.BeNil()) + } + + // Config should now exist + exists, err = utils.SubnetConfigExists(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(exists).Should(gomega.BeTrue()) + exists, err = utils.SubnetCustomVMExists(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(exists).Should(gomega.BeTrue()) +} + +func CreateCustomVMConfigSOV(subnetName string, genesisPath string, vmPath string) { // Check config does not already exist exists, err := utils.SubnetConfigExists(subnetName) gomega.Expect(err).Should(gomega.BeNil()) @@ -190,38 +290,100 @@ func DeleteSubnetConfig(subnetName string) { // Returns the deploy output /* #nosec G204 */ -func DeploySubnetLocally(subnetName string) string { - return DeploySubnetLocallyWithArgs(subnetName, "", "") +func DeploySubnetLocallyNonSOV(subnetName string) string { + return DeploySubnetLocallyWithArgsNonSOV(subnetName, "", "") +} + +func DeploySubnetLocallySOV(subnetName string) string { + return DeploySubnetLocallyWithArgsSOV(subnetName, "", "") } /* #nosec G204 */ -func DeploySubnetLocallyExpectError(subnetName string) { +func DeploySubnetLocallyExpectErrorNonSOV(subnetName string) { + mapper := utils.NewVersionMapper() + mapping, err := utils.GetVersionMapping(mapper) + gomega.Expect(err).Should(gomega.BeNil()) + + DeploySubnetLocallyWithArgsExpectErrorNonSOV(subnetName, mapping[utils.OnlyAvagoKey], "") +} + +func DeploySubnetLocallyExpectErrorSOV(subnetName string) { mapper := utils.NewVersionMapper() mapping, err := utils.GetVersionMapping(mapper) gomega.Expect(err).Should(gomega.BeNil()) - DeploySubnetLocallyWithArgsExpectError(subnetName, mapping[utils.OnlyAvagoKey], "") + DeploySubnetLocallyWithArgsExpectErrorSOV(subnetName, mapping[utils.OnlyAvagoKey], "") } // Returns the deploy output /* #nosec G204 */ -func DeploySubnetLocallyWithViperConf(subnetName string, confPath string) string { +func DeploySubnetLocallyWithViperConfNonSOV(subnetName string, confPath string) string { + mapper := utils.NewVersionMapper() + mapping, err := utils.GetVersionMapping(mapper) + gomega.Expect(err).Should(gomega.BeNil()) + + return DeploySubnetLocallyWithArgsNonSOV(subnetName, mapping[utils.OnlyAvagoKey], confPath) +} + +func DeploySubnetLocallyWithViperConfSOV(subnetName string, confPath string) string { mapper := utils.NewVersionMapper() mapping, err := utils.GetVersionMapping(mapper) gomega.Expect(err).Should(gomega.BeNil()) - return DeploySubnetLocallyWithArgs(subnetName, mapping[utils.OnlyAvagoKey], confPath) + return DeploySubnetLocallyWithArgsSOV(subnetName, mapping[utils.OnlyAvagoKey], confPath) } // Returns the deploy output /* #nosec G204 */ -func DeploySubnetLocallyWithVersion(subnetName string, version string) string { - return DeploySubnetLocallyWithArgs(subnetName, version, "") +func DeploySubnetLocallyWithVersionNonSOV(subnetName string, version string) string { + return DeploySubnetLocallyWithArgsNonSOV(subnetName, version, "") +} + +func DeploySubnetLocallyWithVersionSOV(subnetName string, version string) string { + return DeploySubnetLocallyWithArgsSOV(subnetName, version, "") } // Returns the deploy output /* #nosec G204 */ -func DeploySubnetLocallyWithArgs(subnetName string, version string, confPath string) string { +func DeploySubnetLocallyWithArgsNonSOV(subnetName string, version string, confPath string) string { + // Check config exists + exists, err := utils.SubnetConfigExists(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(exists).Should(gomega.BeTrue()) + + // Deploy subnet locally + cmdArgs := []string{SubnetCmd, "deploy", "--local", subnetName, "--" + constants.SkipUpdateFlag} + if version != "" { + cmdArgs = append(cmdArgs, "--avalanchego-version", version) + } + if confPath != "" { + cmdArgs = append(cmdArgs, "--config", confPath) + } + // in case we want to use specific avago for local tests + debugAvalanchegoPath := os.Getenv(constants.E2EDebugAvalanchegoPath) + if debugAvalanchegoPath != "" { + cmdArgs = append(cmdArgs, "--avalanchego-path", debugAvalanchegoPath) + } + cmd := exec.Command(CLIBinary, cmdArgs...) + output, err := cmd.CombinedOutput() + if err != nil { + var ( + exitErr *exec.ExitError + stderr string + ) + if errors.As(err, &exitErr) { + stderr = string(exitErr.Stderr) + } + fmt.Println(string(output)) + utils.PrintStdErr(err) + fmt.Println(stderr) + } + gomega.Expect(err).Should(gomega.BeNil()) + + return string(output) +} + +func DeploySubnetLocallyWithArgsSOV(subnetName string, version string, confPath string) string { // Check config exists exists, err := utils.SubnetConfigExists(subnetName) gomega.Expect(err).Should(gomega.BeNil()) @@ -259,7 +421,30 @@ func DeploySubnetLocallyWithArgs(subnetName string, version string, confPath str return string(output) } -func DeploySubnetLocallyWithArgsAndOutput(subnetName string, version string, confPath string) ([]byte, error) { +func DeploySubnetLocallyWithArgsAndOutputNonSOV(subnetName string, version string, confPath string) ([]byte, error) { + // Check config exists + exists, err := utils.SubnetConfigExists(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(exists).Should(gomega.BeTrue()) + + // Deploy subnet locally + cmdArgs := []string{SubnetCmd, "deploy", "--local", subnetName, "--" + constants.SkipUpdateFlag} + if version != "" { + cmdArgs = append(cmdArgs, "--avalanchego-version", version) + } + if confPath != "" { + cmdArgs = append(cmdArgs, "--config", confPath) + } + // in case we want to use specific avago for local tests + debugAvalanchegoPath := os.Getenv(constants.E2EDebugAvalanchegoPath) + if debugAvalanchegoPath != "" { + cmdArgs = append(cmdArgs, "--avalanchego-path", debugAvalanchegoPath) + } + cmd := exec.Command(CLIBinary, cmdArgs...) + return cmd.CombinedOutput() +} + +func DeploySubnetLocallyWithArgsAndOutputSOV(subnetName string, version string, confPath string) ([]byte, error) { // Check config exists exists, err := utils.SubnetConfigExists(subnetName) gomega.Expect(err).Should(gomega.BeNil()) @@ -283,14 +468,63 @@ func DeploySubnetLocallyWithArgsAndOutput(subnetName string, version string, con } /* #nosec G204 */ -func DeploySubnetLocallyWithArgsExpectError(subnetName string, version string, confPath string) { - _, err := DeploySubnetLocallyWithArgsAndOutput(subnetName, version, confPath) +func DeploySubnetLocallyWithArgsExpectErrorNonSOV(subnetName string, version string, confPath string) { + _, err := DeploySubnetLocallyWithArgsAndOutputNonSOV(subnetName, version, confPath) + gomega.Expect(err).Should(gomega.HaveOccurred()) +} + +func DeploySubnetLocallyWithArgsExpectErrorSOV(subnetName string, version string, confPath string) { + _, err := DeploySubnetLocallyWithArgsAndOutputSOV(subnetName, version, confPath) gomega.Expect(err).Should(gomega.HaveOccurred()) } // simulates fuji deploy execution path on a local network /* #nosec G204 */ -func SimulateFujiDeploy( +func SimulateFujiDeployNonSOV( + subnetName string, + key string, + controlKeys string, +) string { + // Check config exists + exists, err := utils.SubnetConfigExists(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(exists).Should(gomega.BeTrue()) + + // enable simulation of public network execution paths on a local network + err = os.Setenv(constants.SimulatePublicNetwork, "true") + gomega.Expect(err).Should(gomega.BeNil()) + + // Deploy subnet locally + cmd := exec.Command( + CLIBinary, + SubnetCmd, + "deploy", + "--fuji", + "--threshold", + "1", + "--key", + key, + "--control-keys", + controlKeys, + subnetName, + "--"+constants.SkipUpdateFlag, + ) + output, err := cmd.CombinedOutput() + if err != nil { + fmt.Println(cmd.String()) + fmt.Println(string(output)) + utils.PrintStdErr(err) + } + gomega.Expect(err).Should(gomega.BeNil()) + + // disable simulation of public network execution paths on a local network + err = os.Unsetenv(constants.SimulatePublicNetwork) + gomega.Expect(err).Should(gomega.BeNil()) + + return string(output) +} + +func SimulateFujiDeploySOV( subnetName string, key string, controlKeys string, @@ -337,7 +571,45 @@ func SimulateFujiDeploy( // simulates mainnet deploy execution path on a local network /* #nosec G204 */ -func SimulateMainnetDeploy( +func SimulateMainnetDeployNonSOV( + subnetName string, + mainnetChainID int, + errorIsExpected bool, +) string { + // Check config exists + exists, err := utils.SubnetConfigExists(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(exists).Should(gomega.BeTrue()) + + // enable simulation of public network execution paths on a local network + err = os.Setenv(constants.SimulatePublicNetwork, "true") + gomega.Expect(err).Should(gomega.BeNil()) + + if mainnetChainID == 0 { + mainnetChainID = subnetEVMMainnetChainID + } + + // Deploy subnet locally + return utils.ExecCommand( + CLIBinary, + []string{ + SubnetCmd, + "deploy", + "--mainnet", + "--threshold", + "1", + "--same-control-key", + "--mainnet-chain-id", + fmt.Sprint(mainnetChainID), + subnetName, + "--" + constants.SkipUpdateFlag, + }, + true, + errorIsExpected, + ) +} + +func SimulateMainnetDeploySOV( subnetName string, mainnetChainID int, errorIsExpected bool, @@ -378,7 +650,46 @@ func SimulateMainnetDeploy( // simulates multisig mainnet deploy execution path on a local network /* #nosec G204 */ -func SimulateMultisigMainnetDeploy( +func SimulateMultisigMainnetDeployNonSOV( + subnetName string, + subnetControlAddrs []string, + chainCreationAuthAddrs []string, + txPath string, + errorIsExpected bool, +) string { + // Check config exists + exists, err := utils.SubnetConfigExists(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(exists).Should(gomega.BeTrue()) + + // enable simulation of public network execution paths on a local network + err = os.Setenv(constants.SimulatePublicNetwork, "true") + gomega.Expect(err).Should(gomega.BeNil()) + + // Multisig deploy for local subnet with possible tx file generation + return utils.ExecCommand( + CLIBinary, + []string{ + SubnetCmd, + "deploy", + "--mainnet", + "--control-keys", + strings.Join(subnetControlAddrs, ","), + "--subnet-auth-keys", + strings.Join(chainCreationAuthAddrs, ","), + "--output-tx-path", + txPath, + "--mainnet-chain-id", + fmt.Sprint(subnetEVMMainnetChainID), + subnetName, + "--" + constants.SkipUpdateFlag, + }, + true, + errorIsExpected, + ) +} + +func SimulateMultisigMainnetDeploySOV( subnetName string, subnetControlAddrs []string, chainCreationAuthAddrs []string, diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index c0cbed1e3..f799dc971 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -19,9 +19,12 @@ import ( _ "github.com/ava-labs/avalanche-cli/tests/e2e/testcases/packageman" _ "github.com/ava-labs/avalanche-cli/tests/e2e/testcases/root" _ "github.com/ava-labs/avalanche-cli/tests/e2e/testcases/subnet" - _ "github.com/ava-labs/avalanche-cli/tests/e2e/testcases/subnet/local" - _ "github.com/ava-labs/avalanche-cli/tests/e2e/testcases/subnet/public" - _ "github.com/ava-labs/avalanche-cli/tests/e2e/testcases/upgrade" + _ "github.com/ava-labs/avalanche-cli/tests/e2e/testcases/subnet/non-sov/local" + _ "github.com/ava-labs/avalanche-cli/tests/e2e/testcases/subnet/non-sov/public" + _ "github.com/ava-labs/avalanche-cli/tests/e2e/testcases/subnet/sov/local" + _ "github.com/ava-labs/avalanche-cli/tests/e2e/testcases/subnet/sov/public" + _ "github.com/ava-labs/avalanche-cli/tests/e2e/testcases/upgrade/non-sov" + _ "github.com/ava-labs/avalanche-cli/tests/e2e/testcases/upgrade/sov" ginkgo "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" "github.com/onsi/gomega/format" diff --git a/tests/e2e/testcases/errhandling/suite.go b/tests/e2e/testcases/errhandling/suite.go index 014bcdcca..7aaf4db30 100644 --- a/tests/e2e/testcases/errhandling/suite.go +++ b/tests/e2e/testcases/errhandling/suite.go @@ -35,13 +35,24 @@ var _ = ginkgo.Describe("[Error handling]", func() { // delete custom vm utils.DeleteCustomBinary(subnetName) }) - ginkgo.It("subnet-evm has error but booted", func() { + ginkgo.It("subnet-evm has error but booted non SOV", func() { // tip: if you really want to run this, reduce the RequestTimeout ginkgo.Skip("run this manually only, times out") // this will boot the subnet with a bad genesis: // the root gas limit is smaller than the fee config gas limit, should fail - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisBadPath) - out, err := commands.DeploySubnetLocallyWithArgsAndOutput(subnetName, "", "") + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisBadPath) + out, err := commands.DeploySubnetLocallyWithArgsAndOutputNonSOV(subnetName, "", "") + gomega.Expect(err).Should(gomega.HaveOccurred()) + gomega.Expect(out).Should(gomega.ContainSubstring("does not match gas limit")) + fmt.Println(string(out)) + }) + ginkgo.It("subnet-evm has error but booted SOV", func() { + // tip: if you really want to run this, reduce the RequestTimeout + ginkgo.Skip("run this manually only, times out") + // this will boot the subnet with a bad genesis: + // the root gas limit is smaller than the fee config gas limit, should fail + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisBadPath) + out, err := commands.DeploySubnetLocallyWithArgsAndOutputSOV(subnetName, "", "") gomega.Expect(err).Should(gomega.HaveOccurred()) gomega.Expect(out).Should(gomega.ContainSubstring("does not match gas limit")) fmt.Println(string(out)) diff --git a/tests/e2e/testcases/network/suite.go b/tests/e2e/testcases/network/suite.go index ab39d7a2e..4317ff7c1 100644 --- a/tests/e2e/testcases/network/suite.go +++ b/tests/e2e/testcases/network/suite.go @@ -23,9 +23,9 @@ var _ = ginkgo.Describe("[Network]", ginkgo.Ordered, func() { gomega.Expect(err).Should(gomega.BeNil()) }) - ginkgo.It("can stop and restart a deployed subnet", func() { - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisPath) - deployOutput := commands.DeploySubnetLocally(subnetName) + ginkgo.It("can stop and restart a deployed subnet non SOV", func() { + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath) + deployOutput := commands.DeploySubnetLocallyNonSOV(subnetName) rpcs, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -79,9 +79,92 @@ var _ = ginkgo.Describe("[Network]", ginkgo.Ordered, func() { commands.DeleteSubnetConfig(subnetName) }) - ginkgo.It("clean hard deletes plugin binaries", func() { - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisPath) - deployOutput := commands.DeploySubnetLocally(subnetName) + ginkgo.It("can stop and restart a deployed subnet SOV", func() { + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisPath) + deployOutput := commands.DeploySubnetLocallySOV(subnetName) + rpcs, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs).Should(gomega.HaveLen(1)) + rpc := rpcs[0] + + err = utils.SetHardhatRPC(rpc) + gomega.Expect(err).Should(gomega.BeNil()) + + // Deploy greeter contract + scriptOutput, scriptErr, err := utils.RunHardhatScript(utils.GreeterScript) + if scriptErr != "" { + fmt.Println(scriptOutput) + fmt.Println(scriptErr) + } + gomega.Expect(err).Should(gomega.BeNil()) + err = utils.ParseGreeterAddress(scriptOutput) + gomega.Expect(err).Should(gomega.BeNil()) + + // Check greeter script before stopping + scriptOutput, scriptErr, err = utils.RunHardhatScript(utils.GreeterCheck) + if scriptErr != "" { + fmt.Println(scriptOutput) + fmt.Println(scriptErr) + } + gomega.Expect(err).Should(gomega.BeNil()) + + commands.StopNetwork() + restartOutput := commands.StartNetwork() + rpcs, err = utils.ParseRPCsFromOutput(restartOutput) + if err != nil { + fmt.Println(restartOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs).Should(gomega.HaveLen(1)) + rpc = rpcs[0] + + err = utils.SetHardhatRPC(rpc) + gomega.Expect(err).Should(gomega.BeNil()) + + // Check greeter contract has right value + scriptOutput, scriptErr, err = utils.RunHardhatScript(utils.GreeterCheck) + if scriptErr != "" { + fmt.Println(scriptOutput) + fmt.Println(scriptErr) + } + gomega.Expect(err).Should(gomega.BeNil()) + + commands.DeleteSubnetConfig(subnetName) + }) + + ginkgo.It("clean hard deletes plugin binaries non SOV", func() { + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath) + deployOutput := commands.DeploySubnetLocallyNonSOV(subnetName) + rpcs, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs).Should(gomega.HaveLen(1)) + + // check that plugin binaries exist + plugins, err := utils.GetPluginBinaries() + // should have only subnet-evm binary + gomega.Expect(len(plugins)).Should(gomega.Equal(1)) + gomega.Expect(err).Should(gomega.BeNil()) + + commands.CleanNetwork() + + // check that plugin binaries exist + plugins, err = utils.GetPluginBinaries() + // should be empty + gomega.Expect(len(plugins)).Should(gomega.Equal(0)) + gomega.Expect(err).Should(gomega.BeNil()) + + commands.DeleteSubnetConfig(subnetName) + }) + + ginkgo.It("clean hard deletes plugin binaries SOV", func() { + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisPath) + deployOutput := commands.DeploySubnetLocallySOV(subnetName) rpcs, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) diff --git a/tests/e2e/testcases/packageman/suite.go b/tests/e2e/testcases/packageman/suite.go index cdb302911..3d2e2d572 100644 --- a/tests/e2e/testcases/packageman/suite.go +++ b/tests/e2e/testcases/packageman/suite.go @@ -40,13 +40,13 @@ var _ = ginkgo.Describe("[Package Management]", ginkgo.Ordered, func() { gomega.Expect(err).Should(gomega.BeNil()) }) - ginkgo.It("can deploy a subnet with subnet-evm version", func() { + ginkgo.It("can deploy a subnet with subnet-evm version non SOV", func() { // check subnet-evm install precondition gomega.Expect(utils.CheckSubnetEVMExists(binaryToVersion[utils.SoloSubnetEVMKey1])).Should(gomega.BeFalse()) gomega.Expect(utils.CheckAvalancheGoExists(binaryToVersion[utils.SoloAvagoKey])).Should(gomega.BeFalse()) - commands.CreateSubnetEvmConfigWithVersion(subnetName, utils.SubnetEvmGenesisPath, binaryToVersion[utils.SoloSubnetEVMKey1]) - deployOutput := commands.DeploySubnetLocallyWithVersion(subnetName, binaryToVersion[utils.SoloAvagoKey]) + commands.CreateSubnetEvmConfigWithVersionNonSOV(subnetName, utils.SubnetEvmGenesisPath, binaryToVersion[utils.SoloSubnetEVMKey1]) + deployOutput := commands.DeploySubnetLocallyWithVersionNonSOV(subnetName, binaryToVersion[utils.SoloAvagoKey]) rpcs, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -68,15 +68,87 @@ var _ = ginkgo.Describe("[Package Management]", ginkgo.Ordered, func() { commands.DeleteSubnetConfig(subnetName) }) - ginkgo.It("can deploy multiple subnet-evm versions", func() { + ginkgo.It("can deploy a subnet with subnet-evm version SOV", func() { + // check subnet-evm install precondition + gomega.Expect(utils.CheckSubnetEVMExists(binaryToVersion[utils.SoloSubnetEVMKey1])).Should(gomega.BeFalse()) + gomega.Expect(utils.CheckAvalancheGoExists(binaryToVersion[utils.SoloAvagoKey])).Should(gomega.BeFalse()) + + commands.CreateSubnetEvmConfigWithVersionSOV(subnetName, utils.SubnetEvmGenesisPath, binaryToVersion[utils.SoloSubnetEVMKey1]) + deployOutput := commands.DeploySubnetLocallyWithVersionSOV(subnetName, binaryToVersion[utils.SoloAvagoKey]) + rpcs, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs).Should(gomega.HaveLen(1)) + rpc := rpcs[0] + + err = utils.SetHardhatRPC(rpc) + gomega.Expect(err).Should(gomega.BeNil()) + + err = utils.RunHardhatTests(utils.BaseTest) + gomega.Expect(err).Should(gomega.BeNil()) + + // check subnet-evm install + gomega.Expect(utils.CheckSubnetEVMExists(binaryToVersion[utils.SoloSubnetEVMKey1])).Should(gomega.BeTrue()) + gomega.Expect(utils.CheckAvalancheGoExists(binaryToVersion[utils.SoloAvagoKey])).Should(gomega.BeTrue()) + + commands.DeleteSubnetConfig(subnetName) + }) + + ginkgo.It("can deploy multiple subnet-evm versions non SOV", func() { + // check subnet-evm install precondition + gomega.Expect(utils.CheckSubnetEVMExists(binaryToVersion[utils.SoloSubnetEVMKey1])).Should(gomega.BeFalse()) + gomega.Expect(utils.CheckSubnetEVMExists(binaryToVersion[utils.SoloSubnetEVMKey2])).Should(gomega.BeFalse()) + + commands.CreateSubnetEvmConfigWithVersionNonSOV(subnetName, utils.SubnetEvmGenesisPath, binaryToVersion[utils.SoloSubnetEVMKey1]) + commands.CreateSubnetEvmConfigWithVersionNonSOV(secondSubnetName, utils.SubnetEvmGenesis2Path, binaryToVersion[utils.SoloSubnetEVMKey2]) + + deployOutput := commands.DeploySubnetLocallyNonSOV(subnetName) + rpcs1, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs1).Should(gomega.HaveLen(1)) + + deployOutput = commands.DeploySubnetLocallyNonSOV(secondSubnetName) + rpcs2, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs2).Should(gomega.HaveLen(1)) + + err = utils.SetHardhatRPC(rpcs1[0]) + gomega.Expect(err).Should(gomega.BeNil()) + + err = utils.RunHardhatTests(utils.BaseTest) + gomega.Expect(err).Should(gomega.BeNil()) + + err = utils.SetHardhatRPC(rpcs2[0]) + gomega.Expect(err).Should(gomega.BeNil()) + + err = utils.RunHardhatTests(utils.BaseTest) + gomega.Expect(err).Should(gomega.BeNil()) + + // check subnet-evm install + gomega.Expect(utils.CheckSubnetEVMExists(binaryToVersion[utils.SoloSubnetEVMKey1])).Should(gomega.BeTrue()) + gomega.Expect(utils.CheckSubnetEVMExists(binaryToVersion[utils.SoloSubnetEVMKey2])).Should(gomega.BeTrue()) + + commands.DeleteSubnetConfig(subnetName) + commands.DeleteSubnetConfig(secondSubnetName) + }) + + ginkgo.It("can deploy multiple subnet-evm versions SOV", func() { // check subnet-evm install precondition gomega.Expect(utils.CheckSubnetEVMExists(binaryToVersion[utils.SoloSubnetEVMKey1])).Should(gomega.BeFalse()) gomega.Expect(utils.CheckSubnetEVMExists(binaryToVersion[utils.SoloSubnetEVMKey2])).Should(gomega.BeFalse()) - commands.CreateSubnetEvmConfigWithVersion(subnetName, utils.SubnetEvmGenesisPath, binaryToVersion[utils.SoloSubnetEVMKey1]) - commands.CreateSubnetEvmConfigWithVersion(secondSubnetName, utils.SubnetEvmGenesis2Path, binaryToVersion[utils.SoloSubnetEVMKey2]) + commands.CreateSubnetEvmConfigWithVersionSOV(subnetName, utils.SubnetEvmGenesisPath, binaryToVersion[utils.SoloSubnetEVMKey1]) + commands.CreateSubnetEvmConfigWithVersionSOV(secondSubnetName, utils.SubnetEvmGenesis2Path, binaryToVersion[utils.SoloSubnetEVMKey2]) - deployOutput := commands.DeploySubnetLocally(subnetName) + deployOutput := commands.DeploySubnetLocallySOV(subnetName) rpcs1, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -84,7 +156,7 @@ var _ = ginkgo.Describe("[Package Management]", ginkgo.Ordered, func() { gomega.Expect(err).Should(gomega.BeNil()) gomega.Expect(rpcs1).Should(gomega.HaveLen(1)) - deployOutput = commands.DeploySubnetLocally(secondSubnetName) + deployOutput = commands.DeploySubnetLocallySOV(secondSubnetName) rpcs2, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -112,14 +184,74 @@ var _ = ginkgo.Describe("[Package Management]", ginkgo.Ordered, func() { commands.DeleteSubnetConfig(secondSubnetName) }) - ginkgo.It("can deploy with multiple avalanchego versions", func() { + ginkgo.It("can deploy with multiple avalanchego versions non SOV", func() { + ginkgo.Skip("skipped until two consecutive avago version with dynamic fees are available") + // check avago install precondition + gomega.Expect(utils.CheckAvalancheGoExists(binaryToVersion[utils.MultiAvago1Key])).Should(gomega.BeFalse()) + gomega.Expect(utils.CheckAvalancheGoExists(binaryToVersion[utils.MultiAvago2Key])).Should(gomega.BeFalse()) + + commands.CreateSubnetEvmConfigWithVersionNonSOV(subnetName, utils.SubnetEvmGenesisPath, binaryToVersion[utils.MultiAvagoSubnetEVMKey]) + deployOutput := commands.DeploySubnetLocallyWithVersionNonSOV(subnetName, binaryToVersion[utils.MultiAvago1Key]) + rpcs, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs).Should(gomega.HaveLen(1)) + rpc := rpcs[0] + + err = utils.SetHardhatRPC(rpc) + gomega.Expect(err).Should(gomega.BeNil()) + + // Deploy greeter contract + scriptOutput, scriptErr, err := utils.RunHardhatScript(utils.GreeterScript) + if scriptErr != "" { + fmt.Println(scriptOutput) + fmt.Println(scriptErr) + } + gomega.Expect(err).Should(gomega.BeNil()) + err = utils.ParseGreeterAddress(scriptOutput) + gomega.Expect(err).Should(gomega.BeNil()) + + err = utils.RunHardhatTests(utils.BaseTest) + gomega.Expect(err).Should(gomega.BeNil()) + + // check avago install + gomega.Expect(utils.CheckAvalancheGoExists(binaryToVersion[utils.MultiAvago1Key])).Should(gomega.BeTrue()) + gomega.Expect(utils.CheckAvalancheGoExists(binaryToVersion[utils.MultiAvago2Key])).Should(gomega.BeFalse()) + + commands.CleanNetwork() + + deployOutput = commands.DeploySubnetLocallyWithVersionNonSOV(subnetName, binaryToVersion[utils.MultiAvago2Key]) + rpcs, err = utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs).Should(gomega.HaveLen(1)) + rpc = rpcs[0] + + err = utils.SetHardhatRPC(rpc) + gomega.Expect(err).Should(gomega.BeNil()) + + err = utils.RunHardhatTests(utils.BaseTest) + gomega.Expect(err).Should(gomega.BeNil()) + + // check avago install + gomega.Expect(utils.CheckAvalancheGoExists(binaryToVersion[utils.MultiAvago1Key])).Should(gomega.BeTrue()) + gomega.Expect(utils.CheckAvalancheGoExists(binaryToVersion[utils.MultiAvago2Key])).Should(gomega.BeTrue()) + + commands.DeleteSubnetConfig(subnetName) + }) + + ginkgo.It("can deploy with multiple avalanchego versions SOV", func() { ginkgo.Skip("skipped until two consecutive avago version with dynamic fees are available") // check avago install precondition gomega.Expect(utils.CheckAvalancheGoExists(binaryToVersion[utils.MultiAvago1Key])).Should(gomega.BeFalse()) gomega.Expect(utils.CheckAvalancheGoExists(binaryToVersion[utils.MultiAvago2Key])).Should(gomega.BeFalse()) - commands.CreateSubnetEvmConfigWithVersion(subnetName, utils.SubnetEvmGenesisPath, binaryToVersion[utils.MultiAvagoSubnetEVMKey]) - deployOutput := commands.DeploySubnetLocallyWithVersion(subnetName, binaryToVersion[utils.MultiAvago1Key]) + commands.CreateSubnetEvmConfigWithVersionSOV(subnetName, utils.SubnetEvmGenesisPath, binaryToVersion[utils.MultiAvagoSubnetEVMKey]) + deployOutput := commands.DeploySubnetLocallyWithVersionSOV(subnetName, binaryToVersion[utils.MultiAvago1Key]) rpcs, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -150,7 +282,7 @@ var _ = ginkgo.Describe("[Package Management]", ginkgo.Ordered, func() { commands.CleanNetwork() - deployOutput = commands.DeploySubnetLocallyWithVersion(subnetName, binaryToVersion[utils.MultiAvago2Key]) + deployOutput = commands.DeploySubnetLocallyWithVersionSOV(subnetName, binaryToVersion[utils.MultiAvago2Key]) rpcs, err = utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) diff --git a/tests/e2e/testcases/subnet/non-sov/local/suite.go b/tests/e2e/testcases/subnet/non-sov/local/suite.go new file mode 100644 index 000000000..cd5ab51be --- /dev/null +++ b/tests/e2e/testcases/subnet/non-sov/local/suite.go @@ -0,0 +1,366 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package subnet + +import ( + "context" + "fmt" + "net/url" + "os" + "path" + "strconv" + "strings" + + "github.com/ava-labs/avalanche-cli/pkg/constants" + "github.com/ava-labs/avalanche-cli/tests/e2e/commands" + "github.com/ava-labs/avalanche-cli/tests/e2e/utils" + "github.com/ava-labs/avalanche-network-runner/api" + "github.com/ethereum/go-ethereum/common" + ginkgo "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" +) + +const ( + subnetName = "e2eSubnetTest" + secondSubnetName = "e2eSecondSubnetTest" + confPath = "tests/e2e/assets/test_avalanche-cli.json" +) + +var ( + mapping map[string]string + err error +) + +var _ = ginkgo.Describe("[Local Subnet non SOV]", ginkgo.Ordered, func() { + _ = ginkgo.BeforeAll(func() { + mapper := utils.NewVersionMapper() + mapping, err = utils.GetVersionMapping(mapper) + gomega.Expect(err).Should(gomega.BeNil()) + }) + + ginkgo.AfterEach(func() { + commands.CleanNetwork() + err := utils.DeleteConfigs(subnetName) + if err != nil { + fmt.Println("Clean network error:", err) + } + gomega.Expect(err).Should(gomega.BeNil()) + err = utils.DeleteConfigs(secondSubnetName) + if err != nil { + fmt.Println("Delete config error:", err) + } + gomega.Expect(err).Should(gomega.BeNil()) + + // delete custom vm + utils.DeleteCustomBinary(subnetName) + utils.DeleteCustomBinary(secondSubnetName) + }) + + ginkgo.It("can deploy a custom vm subnet to local non SOV", func() { + customVMPath, err := utils.DownloadCustomVMBin(mapping[utils.SoloSubnetEVMKey1]) + gomega.Expect(err).Should(gomega.BeNil()) + commands.CreateCustomVMConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath, customVMPath) + deployOutput := commands.DeploySubnetLocallyWithVersionNonSOV(subnetName, mapping[utils.SoloAvagoKey]) + rpcs, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs).Should(gomega.HaveLen(1)) + rpc := rpcs[0] + + err = utils.SetHardhatRPC(rpc) + gomega.Expect(err).Should(gomega.BeNil()) + + err = utils.RunHardhatTests(utils.BaseTest) + gomega.Expect(err).Should(gomega.BeNil()) + + commands.DeleteSubnetConfig(subnetName) + }) + + ginkgo.It("can deploy a SubnetEvm subnet to local non SOV", func() { + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath) + deployOutput := commands.DeploySubnetLocallyNonSOV(subnetName) + rpcs, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs).Should(gomega.HaveLen(1)) + rpc := rpcs[0] + + err = utils.SetHardhatRPC(rpc) + gomega.Expect(err).Should(gomega.BeNil()) + + err = utils.RunHardhatTests(utils.BaseTest) + gomega.Expect(err).Should(gomega.BeNil()) + + commands.DeleteSubnetConfig(subnetName) + }) + + ginkgo.It("can load viper config and setup node properties for local deploy non SOV", func() { + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath) + deployOutput := commands.DeploySubnetLocallyWithViperConfNonSOV(subnetName, confPath) + rpcs, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs).Should(gomega.HaveLen(1)) + rpc := rpcs[0] + gomega.Expect(rpc).Should(gomega.HavePrefix("http://0.0.0.0:")) + + commands.DeleteSubnetConfig(subnetName) + }) + + ginkgo.It("can't deploy the same subnet twice to local non SOV", func() { + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath) + + deployOutput := commands.DeploySubnetLocallyNonSOV(subnetName) + fmt.Println(deployOutput) + rpcs, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs).Should(gomega.HaveLen(1)) + + out, err := commands.DeploySubnetLocallyWithArgsAndOutputNonSOV(subnetName, "", "") + gomega.Expect(err).Should(gomega.HaveOccurred()) + deployOutput = string(out) + rpcs, err = utils.ParseRPCsFromOutput(deployOutput) + if err == nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.HaveOccurred()) + gomega.Expect(rpcs).Should(gomega.HaveLen(0)) + gomega.Expect(deployOutput).Should(gomega.ContainSubstring("has already been deployed")) + }) + + ginkgo.It("can deploy multiple subnets to local non SOV", func() { + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath) + commands.CreateSubnetEvmConfigNonSOV(secondSubnetName, utils.SubnetEvmGenesis2Path) + + deployOutput := commands.DeploySubnetLocallyNonSOV(subnetName) + rpcs1, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs1).Should(gomega.HaveLen(1)) + + deployOutput = commands.DeploySubnetLocallyNonSOV(secondSubnetName) + rpcs2, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs2).Should(gomega.HaveLen(1)) + + err = utils.SetHardhatRPC(rpcs1[0]) + gomega.Expect(err).Should(gomega.BeNil()) + + err = utils.RunHardhatTests(utils.BaseTest) + gomega.Expect(err).Should(gomega.BeNil()) + + err = utils.SetHardhatRPC(rpcs2[0]) + gomega.Expect(err).Should(gomega.BeNil()) + + err = utils.RunHardhatTests(utils.BaseTest) + gomega.Expect(err).Should(gomega.BeNil()) + + commands.DeleteSubnetConfig(subnetName) + commands.DeleteSubnetConfig(secondSubnetName) + }) + + ginkgo.It("can deploy custom chain config non SOV", func() { + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmAllowFeeRecpPath) + + addr := "0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC" + + chainConfig := "{\"feeRecipient\": \"" + addr + "\"}" + + // create a chain config in tmp + file, err := os.CreateTemp("", constants.ChainConfigFileName+"*") + gomega.Expect(err).Should(gomega.BeNil()) + err = os.WriteFile(file.Name(), []byte(chainConfig), constants.DefaultPerms755) + gomega.Expect(err).Should(gomega.BeNil()) + + commands.ConfigureChainConfig(subnetName, file.Name()) + + deployOutput := commands.DeploySubnetLocallyNonSOV(subnetName) + rpcs, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs).Should(gomega.HaveLen(1)) + + rpc := rpcs[0] + err = utils.SetHardhatRPC(rpc) + gomega.Expect(err).Should(gomega.BeNil()) + + err = utils.RunHardhatTests(utils.BaseTest) + gomega.Expect(err).Should(gomega.BeNil()) + + url, err := url.Parse(rpc) + gomega.Expect(err).Should(gomega.BeNil()) + port, err := strconv.Atoi(url.Port()) + gomega.Expect(err).Should(gomega.BeNil()) + cClient := api.NewEthClient(url.Hostname(), uint(port)) + + ethAddr := common.HexToAddress(addr) + balance, err := cClient.BalanceAt(context.Background(), ethAddr, nil) + gomega.Expect(err).Should(gomega.BeNil()) + + gomega.Expect(balance.Int64()).Should(gomega.Not(gomega.BeZero())) + + commands.DeleteSubnetConfig(subnetName) + }) + + ginkgo.It("can deploy with custom per chain config node non SOV", func() { + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath) + + // create per node chain config + nodesRPCTxFeeCap := map[string]string{ + "node1": "101", + "node2": "102", + "node3": "103", + "node4": "104", + "node5": "105", + } + perNodeChainConfig := "{\n" + i := 0 + for nodeName, rpcTxFeeCap := range nodesRPCTxFeeCap { + commaStr := "," + if i == len(nodesRPCTxFeeCap)-1 { + commaStr = "" + } + perNodeChainConfig += fmt.Sprintf(" \"%s\": {\"rpc-tx-fee-cap\": %s}%s\n", nodeName, rpcTxFeeCap, commaStr) + i++ + } + perNodeChainConfig += "}\n" + + // configure the subnet + file, err := os.CreateTemp("", constants.PerNodeChainConfigFileName+"*") + gomega.Expect(err).Should(gomega.BeNil()) + err = os.WriteFile(file.Name(), []byte(perNodeChainConfig), constants.DefaultPerms755) + gomega.Expect(err).Should(gomega.BeNil()) + commands.ConfigurePerNodeChainConfig(subnetName, file.Name()) + + // deploy + deployOutput := commands.DeploySubnetLocallyNonSOV(subnetName) + rpcs, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs).Should(gomega.HaveLen(1)) + + // get blockchain ID + rpcParts := strings.Split(rpcs[0], "/") + gomega.Expect(rpcParts).Should(gomega.HaveLen(7)) + blockchainID := rpcParts[5] + + // verify that plugin logs reflect per node configuration + nodesInfo, err := utils.GetNodesInfo() + gomega.Expect(err).Should(gomega.BeNil()) + for nodeName, nodeInfo := range nodesInfo { + logFile := path.Join(nodeInfo.LogDir, blockchainID+".log") + fileBytes, err := os.ReadFile(logFile) + gomega.Expect(err).Should(gomega.BeNil()) + rpcTxFeeCap, ok := nodesRPCTxFeeCap[nodeName] + gomega.Expect(ok).Should(gomega.BeTrue()) + gomega.Expect(fileBytes).Should(gomega.ContainSubstring("RPCTxFeeCap:%s", rpcTxFeeCap)) + } + + commands.DeleteSubnetConfig(subnetName) + }) + + ginkgo.It("can list a subnet's validators non SOV", func() { + nodeIDs := []string{ + "NodeID-P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5", + "NodeID-GWPcbFJZFfZreETSoWjPimr846mXEKCtu", + "NodeID-NFBbbJ4qCmNaCzeW7sxErhvWqvEQMnYcN", + "NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ", + "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg", + } + + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath) + deployOutput := commands.DeploySubnetLocallyNonSOV(subnetName) + _, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + + output, err := commands.ListValidators(subnetName, "local") + gomega.Expect(err).Should(gomega.BeNil()) + + for _, nodeID := range nodeIDs { + gomega.Expect(output).Should(gomega.ContainSubstring(nodeID)) + } + + commands.DeleteSubnetConfig(subnetName) + }) +}) + +var _ = ginkgo.Describe("[Subnet Compatibility]", func() { + ginkgo.AfterEach(func() { + commands.CleanNetwork() + if err := utils.DeleteConfigs(subnetName); err != nil { + fmt.Println("Clean network error:", err) + gomega.Expect(err).Should(gomega.BeNil()) + } + + if err := utils.DeleteConfigs(secondSubnetName); err != nil { + fmt.Println("Delete config error:", err) + gomega.Expect(err).Should(gomega.BeNil()) + } + }) + + ginkgo.It("can deploy a subnet-evm with old version", func() { + subnetEVMVersion := "v0.6.9" + + commands.CreateSubnetEvmConfigWithVersionNonSOV(subnetName, utils.SubnetEvmGenesisPath, subnetEVMVersion) + deployOutput := commands.DeploySubnetLocallyNonSOV(subnetName) + rpcs, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs).Should(gomega.HaveLen(1)) + rpc := rpcs[0] + + err = utils.SetHardhatRPC(rpc) + gomega.Expect(err).Should(gomega.BeNil()) + + err = utils.RunHardhatTests(utils.BaseTest) + gomega.Expect(err).Should(gomega.BeNil()) + + commands.DeleteSubnetConfig(subnetName) + }) + + ginkgo.It("can't deploy conflicting vm versions", func() { + // TODO: These shouldn't be hardcoded either + subnetEVMVersion1 := "v0.6.9" + subnetEVMVersion2 := "v0.6.8" + + commands.CreateSubnetEvmConfigWithVersionNonSOV(subnetName, utils.SubnetEvmGenesisPath, subnetEVMVersion1) + commands.CreateSubnetEvmConfigWithVersionNonSOV(secondSubnetName, utils.SubnetEvmGenesis2Path, subnetEVMVersion2) + + deployOutput := commands.DeploySubnetLocallyNonSOV(subnetName) + rpcs, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(rpcs).Should(gomega.HaveLen(1)) + + commands.DeploySubnetLocallyExpectErrorNonSOV(secondSubnetName) + + commands.DeleteSubnetConfig(subnetName) + commands.DeleteSubnetConfig(secondSubnetName) + }) +}) diff --git a/tests/e2e/testcases/subnet/public/suite.go b/tests/e2e/testcases/subnet/non-sov/public/suite.go similarity index 96% rename from tests/e2e/testcases/subnet/public/suite.go rename to tests/e2e/testcases/subnet/non-sov/public/suite.go index a1f829cf9..b3258339d 100644 --- a/tests/e2e/testcases/subnet/public/suite.go +++ b/tests/e2e/testcases/subnet/non-sov/public/suite.go @@ -36,9 +36,9 @@ const ( mainnetChainID = 123456 ) -func deploySubnetToFuji() (string, map[string]utils.NodeInfo) { +func deploySubnetToFujiNonSOV() (string, map[string]utils.NodeInfo) { // deploy - s := commands.SimulateFujiDeploy(subnetName, keyName, controlKeys) + s := commands.SimulateFujiDeployNonSOV(subnetName, keyName, controlKeys) subnetID, err := utils.ParsePublicDeployOutput(s) gomega.Expect(err).Should(gomega.BeNil()) // add validators to subnet @@ -69,7 +69,7 @@ func deploySubnetToFuji() (string, map[string]utils.NodeInfo) { return subnetID, nodeInfos } -var _ = ginkgo.Describe("[Public Subnet]", func() { +var _ = ginkgo.Describe("[Public Subnet non SOV]", func() { ginkgo.BeforeEach(func() { // key _ = utils.DeleteKey(keyName) @@ -81,7 +81,7 @@ var _ = ginkgo.Describe("[Public Subnet]", func() { gomega.Expect(err).Should(gomega.BeNil()) // subnet config _ = utils.DeleteConfigs(subnetName) - _, avagoVersion := commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisPath) + _, avagoVersion := commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath) // local network commands.StartNetworkWithVersion(avagoVersion) @@ -95,7 +95,7 @@ var _ = ginkgo.Describe("[Public Subnet]", func() { }) ginkgo.It("deploy subnet to fuji", func() { - deploySubnetToFuji() + deploySubnetToFujiNonSOV() }) ginkgo.It("deploy subnet to mainnet", func() { @@ -109,7 +109,7 @@ var _ = ginkgo.Describe("[Public Subnet]", func() { gomega.Expect(err).Should(gomega.BeNil()) fmt.Println() fmt.Println(logging.LightRed.Wrap("DEPLOYING SUBNET. VERIFY LEDGER ADDRESS HAS CUSTOM HRP BEFORE SIGNING")) - s := commands.SimulateMainnetDeploy(subnetName, 0, false) + s := commands.SimulateMainnetDeployNonSOV(subnetName, 0, false) // deploy subnetID, err := utils.ParsePublicDeployOutput(s) gomega.Expect(err).Should(gomega.BeNil()) @@ -162,14 +162,14 @@ var _ = ginkgo.Describe("[Public Subnet]", func() { subnetMainnetChainID, err := utils.GetSubnetEVMMainneChainID(subnetName) gomega.Expect(err).Should(gomega.BeNil()) gomega.Expect(subnetMainnetChainID).Should(gomega.Equal(uint(0))) - _ = commands.SimulateMainnetDeploy(subnetName, mainnetChainID, true) + _ = commands.SimulateMainnetDeployNonSOV(subnetName, mainnetChainID, true) subnetMainnetChainID, err = utils.GetSubnetEVMMainneChainID(subnetName) gomega.Expect(err).Should(gomega.BeNil()) gomega.Expect(subnetMainnetChainID).Should(gomega.Equal(uint(mainnetChainID))) }) ginkgo.It("remove validator fuji", func() { - subnetIDStr, nodeInfos := deploySubnetToFuji() + subnetIDStr, nodeInfos := deploySubnetToFujiNonSOV() // pick a validator to remove var validatorToRemove string @@ -249,7 +249,7 @@ var _ = ginkgo.Describe("[Public Subnet]", func() { // multisig deploy from unfunded ledger1 should not create any subnet/blockchain gomega.Expect(err).Should(gomega.BeNil()) - s := commands.SimulateMultisigMainnetDeploy( + s := commands.SimulateMultisigMainnetDeployNonSOV( subnetName, []string{ledger2Addr, ledger3Addr, ledger4Addr}, []string{ledger2Addr, ledger3Addr}, @@ -269,7 +269,7 @@ var _ = ginkgo.Describe("[Public Subnet]", func() { // multisig deploy from funded ledger1 should create the subnet but not deploy the blockchain, // instead signing only its tx fee as it is not a subnet auth key, // and creating the tx file to wait for subnet auths from ledger2 and ledger3 - s = commands.SimulateMultisigMainnetDeploy( + s = commands.SimulateMultisigMainnetDeployNonSOV( subnetName, []string{ledger2Addr, ledger3Addr, ledger4Addr}, []string{ledger2Addr, ledger3Addr}, diff --git a/tests/e2e/testcases/subnet/local/suite.go b/tests/e2e/testcases/subnet/sov/local/suite.go similarity index 79% rename from tests/e2e/testcases/subnet/local/suite.go rename to tests/e2e/testcases/subnet/sov/local/suite.go index 3b155d4f1..7cdfd5e97 100644 --- a/tests/e2e/testcases/subnet/local/suite.go +++ b/tests/e2e/testcases/subnet/sov/local/suite.go @@ -32,7 +32,7 @@ var ( err error ) -var _ = ginkgo.Describe("[Local Subnet]", ginkgo.Ordered, func() { +var _ = ginkgo.Describe("[Local Subnet SOV]", ginkgo.Ordered, func() { _ = ginkgo.BeforeAll(func() { mapper := utils.NewVersionMapper() mapping, err = utils.GetVersionMapping(mapper) @@ -57,11 +57,11 @@ var _ = ginkgo.Describe("[Local Subnet]", ginkgo.Ordered, func() { utils.DeleteCustomBinary(secondSubnetName) }) - ginkgo.It("can deploy a custom vm subnet to local", func() { + ginkgo.It("can deploy a custom vm subnet to local SOV", func() { customVMPath, err := utils.DownloadCustomVMBin(mapping[utils.SoloSubnetEVMKey1]) gomega.Expect(err).Should(gomega.BeNil()) - commands.CreateCustomVMConfig(subnetName, utils.SubnetEvmGenesisPath, customVMPath) - deployOutput := commands.DeploySubnetLocallyWithVersion(subnetName, mapping[utils.SoloAvagoKey]) + commands.CreateCustomVMConfigSOV(subnetName, utils.SubnetEvmGenesisPath, customVMPath) + deployOutput := commands.DeploySubnetLocallyWithVersionSOV(subnetName, mapping[utils.SoloAvagoKey]) rpcs, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -79,9 +79,9 @@ var _ = ginkgo.Describe("[Local Subnet]", ginkgo.Ordered, func() { commands.DeleteSubnetConfig(subnetName) }) - ginkgo.It("can deploy a SubnetEvm subnet to local", func() { - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisPath) - deployOutput := commands.DeploySubnetLocally(subnetName) + ginkgo.It("can deploy a SubnetEvm subnet to local SOV", func() { + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisPath) + deployOutput := commands.DeploySubnetLocallySOV(subnetName) rpcs, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -99,9 +99,9 @@ var _ = ginkgo.Describe("[Local Subnet]", ginkgo.Ordered, func() { commands.DeleteSubnetConfig(subnetName) }) - ginkgo.It("can load viper config and setup node properties for local deploy", func() { - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisPath) - deployOutput := commands.DeploySubnetLocallyWithViperConf(subnetName, confPath) + ginkgo.It("can load viper config and setup node properties for local deploy SOV", func() { + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisPath) + deployOutput := commands.DeploySubnetLocallyWithViperConfSOV(subnetName, confPath) rpcs, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -114,10 +114,10 @@ var _ = ginkgo.Describe("[Local Subnet]", ginkgo.Ordered, func() { commands.DeleteSubnetConfig(subnetName) }) - ginkgo.It("can't deploy the same subnet twice to local", func() { - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisPath) + ginkgo.It("can't deploy the same subnet twice to local SOV", func() { + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisPath) - deployOutput := commands.DeploySubnetLocally(subnetName) + deployOutput := commands.DeploySubnetLocallySOV(subnetName) fmt.Println(deployOutput) rpcs, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { @@ -126,7 +126,7 @@ var _ = ginkgo.Describe("[Local Subnet]", ginkgo.Ordered, func() { gomega.Expect(err).Should(gomega.BeNil()) gomega.Expect(rpcs).Should(gomega.HaveLen(1)) - out, err := commands.DeploySubnetLocallyWithArgsAndOutput(subnetName, "", "") + out, err := commands.DeploySubnetLocallyWithArgsAndOutputSOV(subnetName, "", "") gomega.Expect(err).Should(gomega.HaveOccurred()) deployOutput = string(out) rpcs, err = utils.ParseRPCsFromOutput(deployOutput) @@ -138,11 +138,11 @@ var _ = ginkgo.Describe("[Local Subnet]", ginkgo.Ordered, func() { gomega.Expect(deployOutput).Should(gomega.ContainSubstring("has already been deployed")) }) - ginkgo.It("can deploy multiple subnets to local", func() { - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisPath) - commands.CreateSubnetEvmConfig(secondSubnetName, utils.SubnetEvmGenesis2Path) + ginkgo.It("can deploy multiple subnets to local SOV", func() { + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisPath) + commands.CreateSubnetEvmConfigSOV(secondSubnetName, utils.SubnetEvmGenesis2Path) - deployOutput := commands.DeploySubnetLocally(subnetName) + deployOutput := commands.DeploySubnetLocallySOV(subnetName) rpcs1, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -150,7 +150,7 @@ var _ = ginkgo.Describe("[Local Subnet]", ginkgo.Ordered, func() { gomega.Expect(err).Should(gomega.BeNil()) gomega.Expect(rpcs1).Should(gomega.HaveLen(1)) - deployOutput = commands.DeploySubnetLocally(secondSubnetName) + deployOutput = commands.DeploySubnetLocallySOV(secondSubnetName) rpcs2, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -174,8 +174,8 @@ var _ = ginkgo.Describe("[Local Subnet]", ginkgo.Ordered, func() { commands.DeleteSubnetConfig(secondSubnetName) }) - ginkgo.It("can deploy custom chain config", func() { - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmAllowFeeRecpPath) + ginkgo.It("can deploy custom chain config SOV", func() { + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmAllowFeeRecpPath) addr := "0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC" @@ -189,7 +189,7 @@ var _ = ginkgo.Describe("[Local Subnet]", ginkgo.Ordered, func() { commands.ConfigureChainConfig(subnetName, file.Name()) - deployOutput := commands.DeploySubnetLocally(subnetName) + deployOutput := commands.DeploySubnetLocallySOV(subnetName) rpcs, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -219,8 +219,8 @@ var _ = ginkgo.Describe("[Local Subnet]", ginkgo.Ordered, func() { commands.DeleteSubnetConfig(subnetName) }) - ginkgo.It("can deploy with custom per chain config node", func() { - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisPath) + ginkgo.It("can deploy with custom per chain config node SOV", func() { + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisPath) // create per node chain config nodesRPCTxFeeCap := map[string]string{ @@ -250,7 +250,7 @@ var _ = ginkgo.Describe("[Local Subnet]", ginkgo.Ordered, func() { commands.ConfigurePerNodeChainConfig(subnetName, file.Name()) // deploy - deployOutput := commands.DeploySubnetLocally(subnetName) + deployOutput := commands.DeploySubnetLocallySOV(subnetName) rpcs, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -278,7 +278,7 @@ var _ = ginkgo.Describe("[Local Subnet]", ginkgo.Ordered, func() { commands.DeleteSubnetConfig(subnetName) }) - ginkgo.It("can list a subnet's validators", func() { + ginkgo.It("can list a subnet's validators SOV", func() { nodeIDs := []string{ "NodeID-P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5", "NodeID-GWPcbFJZFfZreETSoWjPimr846mXEKCtu", @@ -287,8 +287,8 @@ var _ = ginkgo.Describe("[Local Subnet]", ginkgo.Ordered, func() { "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg", } - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisPath) - deployOutput := commands.DeploySubnetLocally(subnetName) + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisPath) + deployOutput := commands.DeploySubnetLocallySOV(subnetName) _, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -323,8 +323,8 @@ var _ = ginkgo.Describe("[Subnet Compatibility]", func() { ginkgo.It("can deploy a subnet-evm with old version", func() { subnetEVMVersion := "v0.6.9" - commands.CreateSubnetEvmConfigWithVersion(subnetName, utils.SubnetEvmGenesisPath, subnetEVMVersion) - deployOutput := commands.DeploySubnetLocally(subnetName) + commands.CreateSubnetEvmConfigWithVersionSOV(subnetName, utils.SubnetEvmGenesisPath, subnetEVMVersion) + deployOutput := commands.DeploySubnetLocallySOV(subnetName) rpcs, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -347,10 +347,10 @@ var _ = ginkgo.Describe("[Subnet Compatibility]", func() { subnetEVMVersion1 := "v0.6.9" subnetEVMVersion2 := "v0.6.8" - commands.CreateSubnetEvmConfigWithVersion(subnetName, utils.SubnetEvmGenesisPath, subnetEVMVersion1) - commands.CreateSubnetEvmConfigWithVersion(secondSubnetName, utils.SubnetEvmGenesis2Path, subnetEVMVersion2) + commands.CreateSubnetEvmConfigWithVersionSOV(subnetName, utils.SubnetEvmGenesisPath, subnetEVMVersion1) + commands.CreateSubnetEvmConfigWithVersionSOV(secondSubnetName, utils.SubnetEvmGenesis2Path, subnetEVMVersion2) - deployOutput := commands.DeploySubnetLocally(subnetName) + deployOutput := commands.DeploySubnetLocallySOV(subnetName) rpcs, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -358,7 +358,7 @@ var _ = ginkgo.Describe("[Subnet Compatibility]", func() { gomega.Expect(err).Should(gomega.BeNil()) gomega.Expect(rpcs).Should(gomega.HaveLen(1)) - commands.DeploySubnetLocallyExpectError(secondSubnetName) + commands.DeploySubnetLocallyExpectErrorSOV(secondSubnetName) commands.DeleteSubnetConfig(subnetName) commands.DeleteSubnetConfig(secondSubnetName) diff --git a/tests/e2e/testcases/subnet/sov/public/suite.go b/tests/e2e/testcases/subnet/sov/public/suite.go new file mode 100644 index 000000000..adc19369b --- /dev/null +++ b/tests/e2e/testcases/subnet/sov/public/suite.go @@ -0,0 +1,419 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package subnet + +import ( + "fmt" + "os" + "regexp" + "strings" + "time" + + "github.com/ava-labs/avalanche-cli/pkg/models" + "github.com/ava-labs/avalanche-cli/pkg/subnet" + cliutils "github.com/ava-labs/avalanche-cli/pkg/utils" + "github.com/ava-labs/avalanche-cli/tests/e2e/commands" + "github.com/ava-labs/avalanche-cli/tests/e2e/utils" + "github.com/ava-labs/avalanchego/genesis" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/logging" + ginkgo "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" +) + +const ( + subnetName = "e2eSubnetTest" + controlKeys = "P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p" + keyName = "ewoq" + stakeAmount = "2000" + stakeDuration = "336h" + localNetwork = "Local Network" + ledger1Seed = "ledger1" + ledger2Seed = "ledger2" + ledger3Seed = "ledger3" + txFnamePrefix = "avalanche-cli-tx-" + mainnetChainID = 123456 +) + +func deploySubnetToFujiSOV() (string, map[string]utils.NodeInfo) { + // deploy + s := commands.SimulateFujiDeploySOV(subnetName, keyName, controlKeys) + subnetID, err := utils.ParsePublicDeployOutput(s) + gomega.Expect(err).Should(gomega.BeNil()) + // add validators to subnet + nodeInfos, err := utils.GetNodesInfo() + gomega.Expect(err).Should(gomega.BeNil()) + for _, nodeInfo := range nodeInfos { + start := time.Now().Add(time.Second * 30).UTC().Format("2006-01-02 15:04:05") + _ = commands.SimulateFujiAddValidator(subnetName, keyName, nodeInfo.ID, start, "24h", "20") + } + // join to copy vm binary and update config file + for _, nodeInfo := range nodeInfos { + _ = commands.SimulateFujiJoin(subnetName, nodeInfo.ConfigFile, nodeInfo.PluginDir, nodeInfo.ID) + } + // get and check whitelisted subnets from config file + var whitelistedSubnets string + for _, nodeInfo := range nodeInfos { + whitelistedSubnets, err = utils.GetWhitelistedSubnetsFromConfigFile(nodeInfo.ConfigFile) + gomega.Expect(err).Should(gomega.BeNil()) + whitelistedSubnetsSlice := strings.Split(whitelistedSubnets, ",") + gomega.Expect(whitelistedSubnetsSlice).Should(gomega.ContainElement(subnetID)) + } + // update nodes whitelisted subnets + err = utils.RestartNodesWithWhitelistedSubnets(whitelistedSubnets) + gomega.Expect(err).Should(gomega.BeNil()) + // wait for subnet walidators to be up + err = utils.WaitSubnetValidators(subnetID, nodeInfos) + gomega.Expect(err).Should(gomega.BeNil()) + return subnetID, nodeInfos +} + +var _ = ginkgo.Describe("[Public Subnet SOV]", func() { + ginkgo.BeforeEach(func() { + // key + _ = utils.DeleteKey(keyName) + output, err := commands.CreateKeyFromPath(keyName, utils.EwoqKeyPath) + if err != nil { + fmt.Println(output) + utils.PrintStdErr(err) + } + gomega.Expect(err).Should(gomega.BeNil()) + // subnet config + _ = utils.DeleteConfigs(subnetName) + _, avagoVersion := commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisPath) + + // local network + commands.StartNetworkWithVersion(avagoVersion) + }) + + ginkgo.AfterEach(func() { + commands.DeleteSubnetConfig(subnetName) + err := utils.DeleteKey(keyName) + gomega.Expect(err).Should(gomega.BeNil()) + commands.CleanNetwork() + }) + + ginkgo.It("deploy subnet to fuji SOV", func() { + deploySubnetToFujiSOV() + }) + + ginkgo.It("deploy subnet to mainnet SOV", func() { + var interactionEndCh, ledgerSimEndCh chan struct{} + if os.Getenv("LEDGER_SIM") != "" { + interactionEndCh, ledgerSimEndCh = utils.StartLedgerSim(7, ledger1Seed, true) + } + // fund ledger address + genesisParams := genesis.MainnetParams + err := utils.FundLedgerAddress(genesisParams.TxFeeConfig.StaticFeeConfig.CreateSubnetTxFee + genesisParams.TxFeeConfig.StaticFeeConfig.CreateBlockchainTxFee + genesisParams.TxFeeConfig.StaticFeeConfig.TxFee) + gomega.Expect(err).Should(gomega.BeNil()) + fmt.Println() + fmt.Println(logging.LightRed.Wrap("DEPLOYING SUBNET. VERIFY LEDGER ADDRESS HAS CUSTOM HRP BEFORE SIGNING")) + s := commands.SimulateMainnetDeploySOV(subnetName, 0, false) + // deploy + subnetID, err := utils.ParsePublicDeployOutput(s) + gomega.Expect(err).Should(gomega.BeNil()) + // add validators to subnet + nodeInfos, err := utils.GetNodesInfo() + gomega.Expect(err).Should(gomega.BeNil()) + nodeIdx := 1 + for _, nodeInfo := range nodeInfos { + fmt.Println(logging.LightRed.Wrap( + fmt.Sprintf("ADDING VALIDATOR %d of %d. VERIFY LEDGER ADDRESS HAS CUSTOM HRP BEFORE SIGNING", nodeIdx, len(nodeInfos)))) + start := time.Now().Add(time.Second * 30).UTC().Format("2006-01-02 15:04:05") + _ = commands.SimulateMainnetAddValidator(subnetName, nodeInfo.ID, start, "24h", "20") + nodeIdx++ + } + if os.Getenv("LEDGER_SIM") != "" { + close(interactionEndCh) + <-ledgerSimEndCh + } + fmt.Println(logging.LightBlue.Wrap("EXECUTING NON INTERACTIVE PART OF THE TEST: JOIN/WHITELIST/WAIT/HARDHAT")) + // join to copy vm binary and update config file + for _, nodeInfo := range nodeInfos { + _ = commands.SimulateMainnetJoin(subnetName, nodeInfo.ConfigFile, nodeInfo.PluginDir, nodeInfo.ID) + } + // get and check whitelisted subnets from config file + var whitelistedSubnets string + for _, nodeInfo := range nodeInfos { + whitelistedSubnets, err = utils.GetWhitelistedSubnetsFromConfigFile(nodeInfo.ConfigFile) + gomega.Expect(err).Should(gomega.BeNil()) + whitelistedSubnetsSlice := strings.Split(whitelistedSubnets, ",") + gomega.Expect(whitelistedSubnetsSlice).Should(gomega.ContainElement(subnetID)) + } + // update nodes whitelisted subnets + err = utils.RestartNodesWithWhitelistedSubnets(whitelistedSubnets) + gomega.Expect(err).Should(gomega.BeNil()) + // wait for subnet walidators to be up + err = utils.WaitSubnetValidators(subnetID, nodeInfos) + gomega.Expect(err).Should(gomega.BeNil()) + + // this is a simulation, so app is probably saving the info in the + // `local network` section of the sidecar instead of the `fuji` section... + // ...need to manipulate the `fuji` section of the sidecar to contain the subnetID info + // so that the `stats` command for `fuji` can find it + output := commands.SimulateGetSubnetStatsFuji(subnetName, subnetID) + gomega.Expect(output).Should(gomega.Not(gomega.BeNil())) + gomega.Expect(output).Should(gomega.ContainSubstring("Current validators")) + gomega.Expect(output).Should(gomega.ContainSubstring("NodeID-")) + }) + + ginkgo.It("deploy subnet with new chain id SOV", func() { + subnetMainnetChainID, err := utils.GetSubnetEVMMainneChainID(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(subnetMainnetChainID).Should(gomega.Equal(uint(0))) + _ = commands.SimulateMainnetDeploySOV(subnetName, mainnetChainID, true) + subnetMainnetChainID, err = utils.GetSubnetEVMMainneChainID(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(subnetMainnetChainID).Should(gomega.Equal(uint(mainnetChainID))) + }) + + ginkgo.It("remove validator fuji SOV", func() { + subnetIDStr, nodeInfos := deploySubnetToFujiSOV() + + // pick a validator to remove + var validatorToRemove string + for _, nodeInfo := range nodeInfos { + validatorToRemove = nodeInfo.ID + break + } + + // confirm current validator set + subnetID, err := ids.FromString(subnetIDStr) + gomega.Expect(err).Should(gomega.BeNil()) + validators, err := subnet.GetSubnetValidators(subnetID) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(len(validators)).Should(gomega.Equal(5)) + + // Check that the validatorToRemove is in the subnet validator set + var found bool + for _, validator := range validators { + if validator.NodeID.String() == validatorToRemove { + found = true + break + } + } + gomega.Expect(found).Should(gomega.BeTrue()) + + // remove validator + _ = commands.SimulateFujiRemoveValidator(subnetName, keyName, validatorToRemove) + + // confirm current validator set + validators, err = subnet.GetSubnetValidators(subnetID) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(len(validators)).Should(gomega.Equal(4)) + + // Check that the validatorToRemove is NOT in the subnet validator set + found = false + for _, validator := range validators { + if validator.NodeID.String() == validatorToRemove { + found = true + break + } + } + gomega.Expect(found).Should(gomega.BeFalse()) + }) + + ginkgo.It("mainnet multisig deploy SOV", func() { + // this is not expected to be executed with real ledgers + // as that will complicate too much the test flow + gomega.Expect(os.Getenv("LEDGER_SIM")).Should(gomega.Equal("true"), "multisig test not designed for real ledgers: please set env var LEDGER_SIM to true") + + txPath, err := utils.GetTmpFilePath(txFnamePrefix) + gomega.Expect(err).Should(gomega.BeNil()) + + // obtain ledger2 addr + interactionEndCh, ledgerSimEndCh := utils.StartLedgerSim(0, ledger2Seed, false) + ledger2Addr, err := utils.GetLedgerAddress(models.NewLocalNetwork(), 0) + gomega.Expect(err).Should(gomega.BeNil()) + close(interactionEndCh) + <-ledgerSimEndCh + + // obtain ledger3 addr + interactionEndCh, ledgerSimEndCh = utils.StartLedgerSim(0, ledger3Seed, false) + ledger3Addr, err := utils.GetLedgerAddress(models.NewLocalNetwork(), 0) + gomega.Expect(err).Should(gomega.BeNil()) + close(interactionEndCh) + <-ledgerSimEndCh + + // ledger4 addr + // will not be used to sign, only as a extra control key, so no sim is needed to generate it + ledger4Addr := "P-custom18g2tekxzt60j3sn8ymjx6qvk96xunhctkyzckt" + + // start the deploy process with ledger1 + interactionEndCh, ledgerSimEndCh = utils.StartLedgerSim(2, ledger1Seed, true) + + // obtain ledger1 addr + ledger1Addr, err := utils.GetLedgerAddress(models.NewLocalNetwork(), 0) + gomega.Expect(err).Should(gomega.BeNil()) + + // multisig deploy from unfunded ledger1 should not create any subnet/blockchain + gomega.Expect(err).Should(gomega.BeNil()) + s := commands.SimulateMultisigMainnetDeploySOV( + subnetName, + []string{ledger2Addr, ledger3Addr, ledger4Addr}, + []string{ledger2Addr, ledger3Addr}, + txPath, + true, + ) + toMatch := "(?s).+Not enough funds in the first (?s).+ indices of Ledger(?s).+Error: not enough funds on ledger(?s).+" + matched, err := regexp.MatchString(toMatch, cliutils.RemoveLineCleanChars(s)) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(matched).Should(gomega.Equal(true), "no match between command output %q and pattern %q", s, toMatch) + + // let's fund the ledger + genesisParams := genesis.MainnetParams + err = utils.FundLedgerAddress(genesisParams.TxFeeConfig.StaticFeeConfig.CreateSubnetTxFee + genesisParams.TxFeeConfig.StaticFeeConfig.CreateBlockchainTxFee + genesisParams.TxFeeConfig.StaticFeeConfig.TxFee) + gomega.Expect(err).Should(gomega.BeNil()) + + // multisig deploy from funded ledger1 should create the subnet but not deploy the blockchain, + // instead signing only its tx fee as it is not a subnet auth key, + // and creating the tx file to wait for subnet auths from ledger2 and ledger3 + s = commands.SimulateMultisigMainnetDeploySOV( + subnetName, + []string{ledger2Addr, ledger3Addr, ledger4Addr}, + []string{ledger2Addr, ledger3Addr}, + txPath, + false, + ) + toMatch = "(?s).+Ledger addresses:(?s).+ " + ledger1Addr + "(?s).+Subnet has been created with ID(?s).+" + //nolint:goconst + "0 of 2 required Blockchain Creation signatures have been signed\\. Saving tx to disk to enable remaining signing\\.(?s).+" + + "Addresses remaining to sign the tx\\s+" + ledger2Addr + "(?s).+" + ledger3Addr + "(?s).+" //nolint:goconst + matched, err = regexp.MatchString(toMatch, cliutils.RemoveLineCleanChars(s)) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(matched).Should(gomega.Equal(true), "no match between command output %q and pattern %q", s, toMatch) + + // try to commit before signature is complete (no funded wallet needed for commit) + s = commands.TransactionCommit( + subnetName, + txPath, + true, + ) + toMatch = "(?s).*0 of 2 required signatures have been signed\\.(?s).+" + + "Addresses remaining to sign the tx\\s+" + ledger2Addr + "(?s).+" + ledger3Addr + "(?s).+" + + "(?s).+Error: tx is not fully signed(?s).+" //nolint:goconst + matched, err = regexp.MatchString(toMatch, cliutils.RemoveLineCleanChars(s)) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(matched).Should(gomega.Equal(true), "no match between command output %q and pattern %q", s, toMatch) + + // try to sign using unauthorized ledger1 + s = commands.TransactionSignWithLedger( + subnetName, + txPath, + true, + ) + toMatch = "(?s).+Ledger addresses:(?s).+ " + ledger1Addr + "(?s).+There are no required subnet auth keys present in the wallet(?s).+" + + "Expected one of:\\s+" + ledger2Addr + "(?s).+" + ledger3Addr + "(?s).+Error: no remaining signer address present in wallet.*" + matched, err = regexp.MatchString(toMatch, cliutils.RemoveLineCleanChars(s)) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(matched).Should(gomega.Equal(true), "no match between command output %q and pattern %q", s, toMatch) + + // wait for end of ledger1 simulation + close(interactionEndCh) + <-ledgerSimEndCh + + // try to commit before signature is complete + s = commands.TransactionCommit( + subnetName, + txPath, + true, + ) + toMatch = "(?s).*0 of 2 required signatures have been signed\\.(?s).+" + + "Addresses remaining to sign the tx\\s+" + ledger2Addr + "(?s).+" + ledger3Addr + "(?s).+" + + "(?s).+Error: tx is not fully signed(?s).+" + matched, err = regexp.MatchString(toMatch, cliutils.RemoveLineCleanChars(s)) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(matched).Should(gomega.Equal(true), "no match between command output %q and pattern %q", s, toMatch) + + // sign using ledger2 + interactionEndCh, ledgerSimEndCh = utils.StartLedgerSim(1, ledger2Seed, true) + s = commands.TransactionSignWithLedger( + subnetName, + txPath, + false, + ) + toMatch = "(?s).+Ledger addresses:(?s).+ " + ledger2Addr + "(?s).+1 of 2 required Tx signatures have been signed\\.(?s).+" + + "Addresses remaining to sign the tx\\s+" + ledger3Addr + ".*" + matched, err = regexp.MatchString(toMatch, cliutils.RemoveLineCleanChars(s)) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(matched).Should(gomega.Equal(true), "no match between command output %q and pattern %q", s, toMatch) + + // try to sign using ledger2 which already signed + s = commands.TransactionSignWithLedger( + subnetName, + txPath, + true, + ) + toMatch = "(?s).+Ledger addresses:(?s).+ " + ledger2Addr + "(?s).+There are no required subnet auth keys present in the wallet(?s).+" + + "Expected one of:\\s+" + ledger3Addr + "(?s).+Error: no remaining signer address present in wallet.*" + matched, err = regexp.MatchString(toMatch, cliutils.RemoveLineCleanChars(s)) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(matched).Should(gomega.Equal(true), "no match between command output %q and pattern %q", s, toMatch) + + // wait for end of ledger2 simulation + close(interactionEndCh) + <-ledgerSimEndCh + + // try to commit before signature is complete + s = commands.TransactionCommit( + subnetName, + txPath, + true, + ) + toMatch = "(?s).*1 of 2 required signatures have been signed\\.(?s).+" + + "Addresses remaining to sign the tx\\s+" + ledger3Addr + + "(?s).+Error: tx is not fully signed(?s).+" + matched, err = regexp.MatchString(toMatch, cliutils.RemoveLineCleanChars(s)) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(matched).Should(gomega.Equal(true), "no match between command output %q and pattern %q", s, toMatch) + + // sign with ledger3 + interactionEndCh, ledgerSimEndCh = utils.StartLedgerSim(1, ledger3Seed, true) + s = commands.TransactionSignWithLedger( + subnetName, + txPath, + false, + ) + toMatch = "(?s).+Ledger addresses:(?s).+ " + ledger3Addr + "(?s).+Tx is fully signed, and ready to be committed(?s).+" + matched, err = regexp.MatchString(toMatch, cliutils.RemoveLineCleanChars(s)) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(matched).Should(gomega.Equal(true), "no match between command output %q and pattern %q", s, toMatch) + + // try to sign using ledger3 which already signedtx is already fully signed" + s = commands.TransactionSignWithLedger( + subnetName, + txPath, + true, + ) + toMatch = "(?s).*Tx is fully signed, and ready to be committed(?s).+Error: tx is already fully signed" + matched, err = regexp.MatchString(toMatch, cliutils.RemoveLineCleanChars(s)) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(matched).Should(gomega.Equal(true), "no match between command output %q and pattern %q", s, toMatch) + + // wait for end of ledger3 simulation + close(interactionEndCh) + <-ledgerSimEndCh + + // commit after complete signature + s = commands.TransactionCommit( + subnetName, + txPath, + false, + ) + toMatch = "(?s).+DEPLOYMENT RESULTS(?s).+Blockchain ID(?s).+" + matched, err = regexp.MatchString(toMatch, cliutils.RemoveLineCleanChars(s)) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(matched).Should(gomega.Equal(true), "no match between command output %q and pattern %q", s, toMatch) + + // try to commit again + s = commands.TransactionCommit( + subnetName, + txPath, + true, + ) + toMatch = "(?s).*Error: error issuing tx with ID(?s).+: failed to decode client response: couldn't issue tx: failed to read consumed(?s).+" + matched, err = regexp.MatchString(toMatch, cliutils.RemoveLineCleanChars(s)) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(matched).Should(gomega.Equal(true), "no match between command output %q and pattern %q", s, toMatch) + }) +}) diff --git a/tests/e2e/testcases/subnet/suite.go b/tests/e2e/testcases/subnet/suite.go index 4d66dfd29..3781f3eb1 100644 --- a/tests/e2e/testcases/subnet/suite.go +++ b/tests/e2e/testcases/subnet/suite.go @@ -24,17 +24,34 @@ var _ = ginkgo.Describe("[Subnet]", ginkgo.Ordered, func() { gomega.Expect(err).Should(gomega.BeNil()) }) - ginkgo.It("can create and delete a subnet evm config", func() { - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisPath) + ginkgo.It("can create and delete a subnet evm config non SOV", func() { + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath) commands.DeleteSubnetConfig(subnetName) }) - ginkgo.It("can create and delete a custom vm subnet config", func() { + ginkgo.It("can create and delete a subnet evm config SOV", func() { + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisPath) + commands.DeleteSubnetConfig(subnetName) + }) + + ginkgo.It("can create and delete a custom vm subnet config non SOV", func() { + // let's use a SubnetEVM version which would be compatible with an existing Avago + customVMPath, err := utils.DownloadCustomVMBin(mapping[utils.SoloSubnetEVMKey1]) + gomega.Expect(err).Should(gomega.BeNil()) + + commands.CreateCustomVMConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath, customVMPath) + commands.DeleteSubnetConfig(subnetName) + exists, err := utils.SubnetCustomVMExists(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(exists).Should(gomega.BeFalse()) + }) + + ginkgo.It("can create and delete a custom vm subnet config SOV", func() { // let's use a SubnetEVM version which would be compatible with an existing Avago customVMPath, err := utils.DownloadCustomVMBin(mapping[utils.SoloSubnetEVMKey1]) gomega.Expect(err).Should(gomega.BeNil()) - commands.CreateCustomVMConfig(subnetName, utils.SubnetEvmGenesisPath, customVMPath) + commands.CreateCustomVMConfigSOV(subnetName, utils.SubnetEvmGenesisPath, customVMPath) commands.DeleteSubnetConfig(subnetName) exists, err := utils.SubnetCustomVMExists(subnetName) gomega.Expect(err).Should(gomega.BeNil()) diff --git a/tests/e2e/testcases/upgrade/non-sov/suite.go b/tests/e2e/testcases/upgrade/non-sov/suite.go new file mode 100644 index 000000000..7bddda8a9 --- /dev/null +++ b/tests/e2e/testcases/upgrade/non-sov/suite.go @@ -0,0 +1,459 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package apm + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + "time" + "unicode" + + "github.com/ava-labs/avalanche-cli/cmd/blockchaincmd/upgradecmd" + "github.com/ava-labs/avalanche-cli/pkg/application" + "github.com/ava-labs/avalanche-cli/pkg/binutils" + "github.com/ava-labs/avalanche-cli/pkg/constants" + "github.com/ava-labs/avalanche-cli/pkg/models" + "github.com/ava-labs/avalanche-cli/tests/e2e/commands" + "github.com/ava-labs/avalanche-cli/tests/e2e/utils" + anr_utils "github.com/ava-labs/avalanche-network-runner/utils" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/subnet-evm/params" + ginkgo "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" +) + +const ( + subnetName = "e2eSubnetTest" + secondSubnetName = "e2eSecondSubnetTest" + + subnetEVMVersion1 = "v0.6.5" + subnetEVMVersion2 = "v0.6.6" + + avagoRPC1Version = "v1.11.4" + avagoRPC2Version = "v1.11.5" + + controlKeys = "P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p" + keyName = "ewoq" + + upgradeBytesPath = "tests/e2e/assets/test_upgrade.json" + + upgradeBytesPath2 = "tests/e2e/assets/test_upgrade_2.json" +) + +var ( + binaryToVersion map[string]string + err error +) + +// need to have this outside the normal suite because of the BeforeEach +var _ = ginkgo.Describe("[Upgrade expect network failure non SOV]", ginkgo.Ordered, func() { + ginkgo.AfterEach(func() { + commands.CleanNetworkHard() + err := utils.DeleteConfigs(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + }) + + ginkgo.It("fails on stopped network non SOV", func() { + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath) + + _, err = commands.ImportUpgradeBytes(subnetName, upgradeBytesPath) + gomega.Expect(err).Should(gomega.BeNil()) + + // we want to simulate a situation here where the subnet has been deployed + // but the network is stopped + // the code would detect it hasn't been deployed yet so report that error first + // therefore we can just manually edit the file to fake it had been deployed + app := application.New() + app.Setup(utils.GetBaseDir(), logging.NoLog{}, nil, nil, nil) + sc := models.Sidecar{ + Name: subnetName, + Subnet: subnetName, + Networks: make(map[string]models.NetworkData), + } + sc.Networks[models.Local.String()] = models.NetworkData{ + SubnetID: ids.GenerateTestID(), + BlockchainID: ids.GenerateTestID(), + } + err = app.UpdateSidecar(&sc) + gomega.Expect(err).Should(gomega.BeNil()) + + out, err := commands.ApplyUpgradeLocal(subnetName) + gomega.Expect(err).Should(gomega.HaveOccurred()) + gomega.Expect(out).Should(gomega.ContainSubstring(binutils.ErrGRPCTimeout.Error())) + }) +}) + +// upgrade a public network +// the approach is rather simple: import the upgrade file, +// call the apply command which "just" installs the file at an expected path, +// and then check the file is there and has the correct content. +var _ = ginkgo.Describe("[Upgrade public network non SOV]", ginkgo.Ordered, func() { + ginkgo.AfterEach(func() { + commands.CleanNetworkHard() + err := utils.DeleteConfigs(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + }) + + ginkgo.It("can create and apply to public node non SOV", func() { + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath) + + // simulate as if this had already been deployed to fuji + // by just entering fake data into the struct + app := application.New() + app.Setup(utils.GetBaseDir(), logging.NoLog{}, nil, nil, nil) + + sc, err := app.LoadSidecar(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + + blockchainID := ids.GenerateTestID() + sc.Networks = make(map[string]models.NetworkData) + sc.Networks[models.Fuji.String()] = models.NetworkData{ + SubnetID: ids.GenerateTestID(), + BlockchainID: blockchainID, + } + err = app.UpdateSidecar(&sc) + gomega.Expect(err).Should(gomega.BeNil()) + + // import the upgrade bytes file so have one + _, err = commands.ImportUpgradeBytes(subnetName, upgradeBytesPath) + gomega.Expect(err).Should(gomega.BeNil()) + + // we'll set a fake chain config dir to not mess up with a potential real one + // in the system + avalanchegoConfigDir, err := os.MkdirTemp("", "cli-tmp-avago-conf-dir") + gomega.Expect(err).Should(gomega.BeNil()) + defer os.RemoveAll(avalanchegoConfigDir) + + // now we try to apply + _, err = commands.ApplyUpgradeToPublicNode(subnetName, avalanchegoConfigDir) + gomega.Expect(err).Should(gomega.BeNil()) + + // we expect the file to be present at the expected location and being + // the same content as the original one + expectedPath := filepath.Join(avalanchegoConfigDir, blockchainID.String(), constants.UpgradeFileName) + gomega.Expect(expectedPath).Should(gomega.BeARegularFile()) + ori, err := os.ReadFile(upgradeBytesPath) + gomega.Expect(err).Should(gomega.BeNil()) + cp, err := os.ReadFile(expectedPath) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(ori).Should(gomega.Equal(cp)) + }) +}) + +var _ = ginkgo.Describe("[Upgrade local network non SOV]", ginkgo.Ordered, func() { + _ = ginkgo.BeforeAll(func() { + mapper := utils.NewVersionMapper() + binaryToVersion, err = utils.GetVersionMapping(mapper) + gomega.Expect(err).Should(gomega.BeNil()) + }) + + ginkgo.BeforeEach(func() { + output, err := commands.CreateKeyFromPath(keyName, utils.EwoqKeyPath) + if err != nil { + fmt.Println(output) + utils.PrintStdErr(err) + } + gomega.Expect(err).Should(gomega.BeNil()) + }) + + ginkgo.AfterEach(func() { + commands.CleanNetworkHard() + err := utils.DeleteConfigs(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + err = utils.DeleteConfigs(secondSubnetName) + gomega.Expect(err).Should(gomega.BeNil()) + _ = utils.DeleteKey(keyName) + utils.DeleteCustomBinary(subnetName) + }) + + ginkgo.It("fails on undeployed subnet non SOV", func() { + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath) + + _, err = commands.ImportUpgradeBytes(subnetName, upgradeBytesPath) + gomega.Expect(err).Should(gomega.BeNil()) + + _ = commands.StartNetwork() + + out, err := commands.ApplyUpgradeLocal(subnetName) + gomega.Expect(err).Should(gomega.HaveOccurred()) + gomega.Expect(out).Should(gomega.ContainSubstring(upgradecmd.ErrSubnetNotDeployedOutput)) + }) + + ginkgo.It("can create and apply to locally running subnet non SOV", func() { + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath) + + deployOutput := commands.DeploySubnetLocallyNonSOV(subnetName) + + _, err = commands.ImportUpgradeBytes(subnetName, upgradeBytesPath) + gomega.Expect(err).Should(gomega.BeNil()) + + _, err = commands.ApplyUpgradeLocal(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + + upgradeBytes, err := os.ReadFile(upgradeBytesPath) + gomega.Expect(err).Should(gomega.BeNil()) + + var precmpUpgrades params.UpgradeConfig + err = json.Unmarshal(upgradeBytes, &precmpUpgrades) + gomega.Expect(err).Should(gomega.BeNil()) + + rpcs, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + err = utils.CheckUpgradeIsDeployed(rpcs[0], precmpUpgrades) + gomega.Expect(err).Should(gomega.BeNil()) + + app := application.New() + app.Setup(utils.GetBaseDir(), logging.NoLog{}, nil, nil, nil) + + stripped := stripWhitespaces(string(upgradeBytes)) + lockUpgradeBytes, err := app.ReadLockUpgradeFile(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect([]byte(stripped)).Should(gomega.Equal(lockUpgradeBytes)) + }) + + ginkgo.It("can't upgrade transactionAllowList precompile because admin address doesn't have enough token non SOV", func() { + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath) + + commands.DeploySubnetLocallyNonSOV(subnetName) + + _, err = commands.ImportUpgradeBytes(subnetName, upgradeBytesPath2) + gomega.Expect(err).Should(gomega.BeNil()) + + _, err = commands.ApplyUpgradeLocal(subnetName) + gomega.Expect(err).Should(gomega.HaveOccurred()) + }) + + ginkgo.It("can upgrade transactionAllowList precompile because admin address has enough tokens non SOV", func() { + commands.CreateSubnetEvmConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath) + + commands.DeploySubnetLocallyNonSOV(subnetName) + + _, err = commands.ImportUpgradeBytes(subnetName, upgradeBytesPath) + gomega.Expect(err).Should(gomega.BeNil()) + + _, err = commands.ApplyUpgradeLocal(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + }) + + ginkgo.It("can create and update future non SOV", func() { + subnetEVMVersion1 := binaryToVersion[utils.SoloSubnetEVMKey1] + subnetEVMVersion2 := binaryToVersion[utils.SoloSubnetEVMKey2] + commands.CreateSubnetEvmConfigWithVersionNonSOV(subnetName, utils.SubnetEvmGenesisPath, subnetEVMVersion1) + + // check version + output, err := commands.DescribeSubnet(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + + containsVersion1 := strings.Contains(output, subnetEVMVersion1) + containsVersion2 := strings.Contains(output, subnetEVMVersion2) + gomega.Expect(containsVersion1).Should(gomega.BeTrue()) + gomega.Expect(containsVersion2).Should(gomega.BeFalse()) + + _, err = commands.UpgradeVMConfig(subnetName, subnetEVMVersion2) + gomega.Expect(err).Should(gomega.BeNil()) + + output, err = commands.DescribeSubnet(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + + containsVersion1 = strings.Contains(output, subnetEVMVersion1) + containsVersion2 = strings.Contains(output, subnetEVMVersion2) + gomega.Expect(containsVersion1).Should(gomega.BeFalse()) + gomega.Expect(containsVersion2).Should(gomega.BeTrue()) + + commands.DeleteSubnetConfig(subnetName) + }) + + ginkgo.It("upgrade SubnetEVM local deployment non SOV", func() { + commands.CreateSubnetEvmConfigWithVersionNonSOV(subnetName, utils.SubnetEvmGenesisPath, subnetEVMVersion1) + deployOutput := commands.DeploySubnetLocallyNonSOV(subnetName) + rpcs, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + + // check running version + // remove string suffix starting with /ext + nodeURI := strings.Split(rpcs[0], "/ext")[0] + vmid, err := anr_utils.VMID(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + version, err := utils.GetNodeVMVersion(nodeURI, vmid.String()) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(version).Should(gomega.Equal(subnetEVMVersion1)) + + // stop network + commands.StopNetwork() + + // upgrade + commands.UpgradeVMLocal(subnetName, subnetEVMVersion2) + + // restart network + commands.StartNetwork() + + // check running version + version, err = utils.GetNodeVMVersion(nodeURI, vmid.String()) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(version).Should(gomega.Equal(subnetEVMVersion2)) + + commands.DeleteSubnetConfig(subnetName) + }) + + ginkgo.It("upgrade custom vm local deployment non SOV", func() { + // download vm bins + customVMPath1, err := utils.DownloadCustomVMBin(subnetEVMVersion1) + gomega.Expect(err).Should(gomega.BeNil()) + customVMPath2, err := utils.DownloadCustomVMBin(subnetEVMVersion2) + gomega.Expect(err).Should(gomega.BeNil()) + + // create and deploy + commands.CreateCustomVMConfigNonSOV(subnetName, utils.SubnetEvmGenesisPath, customVMPath1) + // need to set avago version manually since VMs are custom + commands.StartNetworkWithVersion(avagoRPC1Version) + deployOutput := commands.DeploySubnetLocallyNonSOV(subnetName) + rpcs, err := utils.ParseRPCsFromOutput(deployOutput) + if err != nil { + fmt.Println(deployOutput) + } + + // check running version + // remove string suffix starting with /ext from rpc url to get node uri + nodeURI := strings.Split(rpcs[0], "/ext")[0] + vmid, err := anr_utils.VMID(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + version, err := utils.GetNodeVMVersion(nodeURI, vmid.String()) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(version).Should(gomega.Equal(subnetEVMVersion1)) + + // stop network + commands.StopNetwork() + + // upgrade + commands.UpgradeCustomVMLocal(subnetName, customVMPath2) + + // restart network + commands.StartNetworkWithVersion(avagoRPC2Version) + + // check running version + version, err = utils.GetNodeVMVersion(nodeURI, vmid.String()) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(version).Should(gomega.Equal(subnetEVMVersion2)) + + commands.DeleteSubnetConfig(subnetName) + }) + + ginkgo.It("can update a subnet-evm to a custom VM non SOV", func() { + customVMPath, err := utils.DownloadCustomVMBin(binaryToVersion[utils.SoloSubnetEVMKey2]) + gomega.Expect(err).Should(gomega.BeNil()) + + commands.CreateSubnetEvmConfigWithVersionNonSOV( + subnetName, + utils.SubnetEvmGenesisPath, + binaryToVersion[utils.SoloSubnetEVMKey1], + ) + + // check version + output, err := commands.DescribeSubnet(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + + containsVersion1 := strings.Contains(output, binaryToVersion[utils.SoloSubnetEVMKey1]) + containsVersion2 := strings.Contains(output, binaryToVersion[utils.SoloSubnetEVMKey2]) + gomega.Expect(containsVersion1).Should(gomega.BeTrue()) + gomega.Expect(containsVersion2).Should(gomega.BeFalse()) + + _, err = commands.UpgradeCustomVM(subnetName, customVMPath) + gomega.Expect(err).Should(gomega.BeNil()) + + output, err = commands.DescribeSubnet(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + + containsVersion2 = strings.Contains(output, binaryToVersion[utils.SoloSubnetEVMKey2]) + gomega.Expect(containsVersion2).Should(gomega.BeFalse()) + + // the following indicates it is a custom VM + isCustom, err := utils.IsCustomVM(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(isCustom).Should(gomega.BeTrue()) + + commands.DeleteSubnetConfig(subnetName) + }) + + ginkgo.It("can upgrade subnet-evm on public deployment non SOV", func() { + _ = commands.StartNetworkWithVersion(binaryToVersion[utils.SoloAvagoKey]) + commands.CreateSubnetEvmConfigWithVersionNonSOV(subnetName, utils.SubnetEvmGenesisPath, binaryToVersion[utils.SoloSubnetEVMKey1]) + + // Simulate fuji deployment + s := commands.SimulateFujiDeployNonSOV(subnetName, keyName, controlKeys) + subnetID, err := utils.ParsePublicDeployOutput(s) + gomega.Expect(err).Should(gomega.BeNil()) + // add validators to subnet + nodeInfos, err := utils.GetNodesInfo() + gomega.Expect(err).Should(gomega.BeNil()) + for _, nodeInfo := range nodeInfos { + start := time.Now().Add(time.Second * 30).UTC().Format("2006-01-02 15:04:05") + _ = commands.SimulateFujiAddValidator(subnetName, keyName, nodeInfo.ID, start, "24h", "20") + } + // join to copy vm binary and update config file + for _, nodeInfo := range nodeInfos { + _ = commands.SimulateFujiJoin(subnetName, nodeInfo.ConfigFile, nodeInfo.PluginDir, nodeInfo.ID) + } + // get and check whitelisted subnets from config file + var whitelistedSubnets string + for _, nodeInfo := range nodeInfos { + whitelistedSubnets, err = utils.GetWhitelistedSubnetsFromConfigFile(nodeInfo.ConfigFile) + gomega.Expect(err).Should(gomega.BeNil()) + whitelistedSubnetsSlice := strings.Split(whitelistedSubnets, ",") + gomega.Expect(whitelistedSubnetsSlice).Should(gomega.ContainElement(subnetID)) + } + // update nodes whitelisted subnets + err = utils.RestartNodesWithWhitelistedSubnets(whitelistedSubnets) + gomega.Expect(err).Should(gomega.BeNil()) + // wait for subnet walidators to be up + err = utils.WaitSubnetValidators(subnetID, nodeInfos) + gomega.Expect(err).Should(gomega.BeNil()) + + var originalHash string + + // upgrade the vm on each node + vmid, err := anr_utils.VMID(subnetName) + gomega.Expect(err).Should(gomega.BeNil()) + + for _, nodeInfo := range nodeInfos { + originalHash, err = utils.GetFileHash(filepath.Join(nodeInfo.PluginDir, vmid.String())) + gomega.Expect(err).Should(gomega.BeNil()) + } + + // stop network + commands.StopNetwork() + + for _, nodeInfo := range nodeInfos { + _, err := commands.UpgradeVMPublic(subnetName, binaryToVersion[utils.SoloSubnetEVMKey2], nodeInfo.PluginDir) + gomega.Expect(err).Should(gomega.BeNil()) + } + + for _, nodeInfo := range nodeInfos { + measuredHash, err := utils.GetFileHash(filepath.Join(nodeInfo.PluginDir, vmid.String())) + gomega.Expect(err).Should(gomega.BeNil()) + + gomega.Expect(measuredHash).ShouldNot(gomega.Equal(originalHash)) + } + + commands.DeleteSubnetConfig(subnetName) + }) +}) + +func stripWhitespaces(str string) string { + return strings.Map(func(r rune) rune { + if unicode.IsSpace(r) { + // if the character is a space, drop it + return -1 + } + // else keep it in the string + return r + }, str) +} diff --git a/tests/e2e/testcases/upgrade/suite.go b/tests/e2e/testcases/upgrade/sov/suite.go similarity index 86% rename from tests/e2e/testcases/upgrade/suite.go rename to tests/e2e/testcases/upgrade/sov/suite.go index fa6fe0f2a..b23f86362 100644 --- a/tests/e2e/testcases/upgrade/suite.go +++ b/tests/e2e/testcases/upgrade/sov/suite.go @@ -51,15 +51,15 @@ var ( ) // need to have this outside the normal suite because of the BeforeEach -var _ = ginkgo.Describe("[Upgrade expect network failure]", ginkgo.Ordered, func() { +var _ = ginkgo.Describe("[Upgrade expect network failure SOV]", ginkgo.Ordered, func() { ginkgo.AfterEach(func() { commands.CleanNetworkHard() err := utils.DeleteConfigs(subnetName) gomega.Expect(err).Should(gomega.BeNil()) }) - ginkgo.It("fails on stopped network", func() { - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisPath) + ginkgo.It("fails on stopped network SOV", func() { + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisPath) _, err = commands.ImportUpgradeBytes(subnetName, upgradeBytesPath) gomega.Expect(err).Should(gomega.BeNil()) @@ -92,15 +92,15 @@ var _ = ginkgo.Describe("[Upgrade expect network failure]", ginkgo.Ordered, func // the approach is rather simple: import the upgrade file, // call the apply command which "just" installs the file at an expected path, // and then check the file is there and has the correct content. -var _ = ginkgo.Describe("[Upgrade public network]", ginkgo.Ordered, func() { +var _ = ginkgo.Describe("[Upgrade public network SOV]", ginkgo.Ordered, func() { ginkgo.AfterEach(func() { commands.CleanNetworkHard() err := utils.DeleteConfigs(subnetName) gomega.Expect(err).Should(gomega.BeNil()) }) - ginkgo.It("can create and apply to public node", func() { - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisPath) + ginkgo.It("can create and apply to public node SOV", func() { + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisPath) // simulate as if this had already been deployed to fuji // by just entering fake data into the struct @@ -135,7 +135,7 @@ var _ = ginkgo.Describe("[Upgrade public network]", ginkgo.Ordered, func() { // we expect the file to be present at the expected location and being // the same content as the original one - expectedPath := filepath.Join(avalanchegoConfigDir, blockchainID.String(), constants.UpgradeBytesFileName) + expectedPath := filepath.Join(avalanchegoConfigDir, blockchainID.String(), constants.UpgradeFileName) gomega.Expect(expectedPath).Should(gomega.BeARegularFile()) ori, err := os.ReadFile(upgradeBytesPath) gomega.Expect(err).Should(gomega.BeNil()) @@ -145,7 +145,7 @@ var _ = ginkgo.Describe("[Upgrade public network]", ginkgo.Ordered, func() { }) }) -var _ = ginkgo.Describe("[Upgrade local network]", ginkgo.Ordered, func() { +var _ = ginkgo.Describe("[Upgrade local network SOV]", ginkgo.Ordered, func() { _ = ginkgo.BeforeAll(func() { mapper := utils.NewVersionMapper() binaryToVersion, err = utils.GetVersionMapping(mapper) @@ -171,8 +171,8 @@ var _ = ginkgo.Describe("[Upgrade local network]", ginkgo.Ordered, func() { utils.DeleteCustomBinary(subnetName) }) - ginkgo.It("fails on undeployed subnet", func() { - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisPath) + ginkgo.It("fails on undeployed subnet SOV", func() { + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisPath) _, err = commands.ImportUpgradeBytes(subnetName, upgradeBytesPath) gomega.Expect(err).Should(gomega.BeNil()) @@ -184,10 +184,10 @@ var _ = ginkgo.Describe("[Upgrade local network]", ginkgo.Ordered, func() { gomega.Expect(out).Should(gomega.ContainSubstring(upgradecmd.ErrSubnetNotDeployedOutput)) }) - ginkgo.It("can create and apply to locally running subnet", func() { - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisPath) + ginkgo.It("can create and apply to locally running subnet SOV", func() { + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisPath) - deployOutput := commands.DeploySubnetLocally(subnetName) + deployOutput := commands.DeploySubnetLocallySOV(subnetName) _, err = commands.ImportUpgradeBytes(subnetName, upgradeBytesPath) gomega.Expect(err).Should(gomega.BeNil()) @@ -218,10 +218,10 @@ var _ = ginkgo.Describe("[Upgrade local network]", ginkgo.Ordered, func() { gomega.Expect([]byte(stripped)).Should(gomega.Equal(lockUpgradeBytes)) }) - ginkgo.It("can't upgrade transactionAllowList precompile because admin address doesn't have enough token", func() { - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisPath) + ginkgo.It("can't upgrade transactionAllowList precompile because admin address doesn't have enough token SOV", func() { + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisPath) - commands.DeploySubnetLocally(subnetName) + commands.DeploySubnetLocallySOV(subnetName) _, err = commands.ImportUpgradeBytes(subnetName, upgradeBytesPath2) gomega.Expect(err).Should(gomega.BeNil()) @@ -230,10 +230,10 @@ var _ = ginkgo.Describe("[Upgrade local network]", ginkgo.Ordered, func() { gomega.Expect(err).Should(gomega.HaveOccurred()) }) - ginkgo.It("can upgrade transactionAllowList precompile because admin address has enough tokens", func() { - commands.CreateSubnetEvmConfig(subnetName, utils.SubnetEvmGenesisPath) + ginkgo.It("can upgrade transactionAllowList precompile because admin address has enough tokens SOV", func() { + commands.CreateSubnetEvmConfigSOV(subnetName, utils.SubnetEvmGenesisPath) - commands.DeploySubnetLocally(subnetName) + commands.DeploySubnetLocallySOV(subnetName) _, err = commands.ImportUpgradeBytes(subnetName, upgradeBytesPath) gomega.Expect(err).Should(gomega.BeNil()) @@ -242,10 +242,10 @@ var _ = ginkgo.Describe("[Upgrade local network]", ginkgo.Ordered, func() { gomega.Expect(err).Should(gomega.BeNil()) }) - ginkgo.It("can create and update future", func() { + ginkgo.It("can create and update future SOV", func() { subnetEVMVersion1 := binaryToVersion[utils.SoloSubnetEVMKey1] subnetEVMVersion2 := binaryToVersion[utils.SoloSubnetEVMKey2] - commands.CreateSubnetEvmConfigWithVersion(subnetName, utils.SubnetEvmGenesisPath, subnetEVMVersion1) + commands.CreateSubnetEvmConfigWithVersionSOV(subnetName, utils.SubnetEvmGenesisPath, subnetEVMVersion1) // check version output, err := commands.DescribeSubnet(subnetName) @@ -270,9 +270,9 @@ var _ = ginkgo.Describe("[Upgrade local network]", ginkgo.Ordered, func() { commands.DeleteSubnetConfig(subnetName) }) - ginkgo.It("upgrade SubnetEVM local deployment", func() { - commands.CreateSubnetEvmConfigWithVersion(subnetName, utils.SubnetEvmGenesisPath, subnetEVMVersion1) - deployOutput := commands.DeploySubnetLocally(subnetName) + ginkgo.It("upgrade SubnetEVM local deployment SOV", func() { + commands.CreateSubnetEvmConfigWithVersionSOV(subnetName, utils.SubnetEvmGenesisPath, subnetEVMVersion1) + deployOutput := commands.DeploySubnetLocallySOV(subnetName) rpcs, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -304,7 +304,7 @@ var _ = ginkgo.Describe("[Upgrade local network]", ginkgo.Ordered, func() { commands.DeleteSubnetConfig(subnetName) }) - ginkgo.It("upgrade custom vm local deployment", func() { + ginkgo.It("upgrade custom vm local deployment SOV", func() { // download vm bins customVMPath1, err := utils.DownloadCustomVMBin(subnetEVMVersion1) gomega.Expect(err).Should(gomega.BeNil()) @@ -312,10 +312,10 @@ var _ = ginkgo.Describe("[Upgrade local network]", ginkgo.Ordered, func() { gomega.Expect(err).Should(gomega.BeNil()) // create and deploy - commands.CreateCustomVMConfig(subnetName, utils.SubnetEvmGenesisPath, customVMPath1) + commands.CreateCustomVMConfigSOV(subnetName, utils.SubnetEvmGenesisPath, customVMPath1) // need to set avago version manually since VMs are custom commands.StartNetworkWithVersion(avagoRPC1Version) - deployOutput := commands.DeploySubnetLocally(subnetName) + deployOutput := commands.DeploySubnetLocallySOV(subnetName) rpcs, err := utils.ParseRPCsFromOutput(deployOutput) if err != nil { fmt.Println(deployOutput) @@ -347,11 +347,11 @@ var _ = ginkgo.Describe("[Upgrade local network]", ginkgo.Ordered, func() { commands.DeleteSubnetConfig(subnetName) }) - ginkgo.It("can update a subnet-evm to a custom VM", func() { + ginkgo.It("can update a subnet-evm to a custom VM SOV", func() { customVMPath, err := utils.DownloadCustomVMBin(binaryToVersion[utils.SoloSubnetEVMKey2]) gomega.Expect(err).Should(gomega.BeNil()) - commands.CreateSubnetEvmConfigWithVersion( + commands.CreateSubnetEvmConfigWithVersionSOV( subnetName, utils.SubnetEvmGenesisPath, binaryToVersion[utils.SoloSubnetEVMKey1], @@ -383,12 +383,12 @@ var _ = ginkgo.Describe("[Upgrade local network]", ginkgo.Ordered, func() { commands.DeleteSubnetConfig(subnetName) }) - ginkgo.It("can upgrade subnet-evm on public deployment", func() { + ginkgo.It("can upgrade subnet-evm on public deployment SOV", func() { _ = commands.StartNetworkWithVersion(binaryToVersion[utils.SoloAvagoKey]) - commands.CreateSubnetEvmConfigWithVersion(subnetName, utils.SubnetEvmGenesisPath, binaryToVersion[utils.SoloSubnetEVMKey1]) + commands.CreateSubnetEvmConfigWithVersionSOV(subnetName, utils.SubnetEvmGenesisPath, binaryToVersion[utils.SoloSubnetEVMKey1]) // Simulate fuji deployment - s := commands.SimulateFujiDeploy(subnetName, keyName, controlKeys) + s := commands.SimulateFujiDeploySOV(subnetName, keyName, controlKeys) subnetID, err := utils.ParsePublicDeployOutput(s) gomega.Expect(err).Should(gomega.BeNil()) // add validators to subnet