diff --git a/cmd/blockchaincmd/add_validator.go b/cmd/blockchaincmd/add_validator.go index e6ae29068..d6d92107f 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" @@ -32,6 +36,7 @@ var ( } nodeIDStr string + balance uint64 weight uint64 startTimeStr string duration time.Duration @@ -40,6 +45,9 @@ var ( useDefaultDuration bool useDefaultWeight bool 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") @@ -69,28 +77,26 @@ Testnet or Mainnet.`, 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", 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().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().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") - cmd.Flags().BoolVar(&waitForTxAcceptance, "wait-for-tx-acceptance", true, "just issue the add validator tx, without waiting for its acceptance") + 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 := prompts.ValidateNodeID(nodeIDStr) + if err != nil { + return err + } + network, err := networkoptions.GetNetworkFromCmdLineFlags( app, "", @@ -118,14 +124,148 @@ func addValidator(_ *cobra.Command, args []string) error { return err } network.HandlePublicNetworkSimulation() - if err := UpdateKeychainWithSubnetControlKeys(kc, network, blockchainName); err != nil { - return err + if !sovereign { + if err := UpdateKeychainWithSubnetControlKeys(kc, network, blockchainName); err != nil { + return err + } } deployer := subnet.NewPublicDeployer(app, kc, network) - return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeIDStr, defaultValidatorParams, waitForTxAcceptance) + if !sovereign { + return CallAddValidatorNonSOV(deployer, network, kc, useLedger, blockchainName, nodeIDStr, defaultValidatorParams, waitForTxAcceptance) + } + return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeIDStr) +} + +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, + nodeIDStrFormat string, +) error { + useLedger = useLedgerSetting + + _, err := ValidateSubnetNameAndGetChains([]string{blockchainName}) + if err != nil { + return err + } + + switch network.Kind { + case models.Devnet: + 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 == "" { + 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 + } + + 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 a blockchain validator") + if err != nil { + return err + } + nodeIDStr = nodeID.String() + } + + publicKey, pop, err = promptProofOfPossession(publicKey == "", pop == "") + if err != nil { + return err + } + + if balance == 0 { + balance, err = promptValidatorBalance() + if err != nil { + return err + } + } + + if changeAddr == "" { + changeAddr, err = getKeyForChangeOwner("", network) + if err != nil { + return err + } + } + + 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) + ux.Logger.PrintToUser("Change Address: %s", changeAddr) + ux.Logger.PrintToUser("Inputs complete, issuing transaction to add the provided validator information...") + + 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, + } + nodeID, err := ids.NodeIDFromString(nodeIDStrFormat) + if err != nil { + return err + } + // TODO: generate warp message + // 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.RegisterL1Validator(balance, blsInfo, changeOwner, message) + if err != nil { + return err + } + ux.Logger.GreenCheckmarkToUser("Register Subnet Validator Tx ID: %s", tx.ID()) + return nil +} + +func generateWarpMessageAddValidator(subnetID ids.ID, nodeID ids.NodeID, weight uint64, blsPublicKey string, expiry uint64) (warpPlatformVM.Message, error) { + return warpPlatformVM.Message{}, nil +} + +func CallAddValidatorNonSOV( deployer *subnet.PublicDeployer, network models.Network, kc *keychain.Keychain, @@ -135,12 +275,11 @@ func CallAddValidator( 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 @@ -208,18 +347,6 @@ func CallAddValidator( } 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 @@ -240,7 +367,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/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..af1760ba6 --- /dev/null +++ b/cmd/blockchaincmd/change_weight.go @@ -0,0 +1,191 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +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" + "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/ux" + "github.com/ava-labs/avalanchego/ids" + "github.com/spf13/cobra" +) + +// avalanche blockchain addValidator +func newChangeWeightCmd() *cobra.Command { + cmd := &cobra.Command{ + 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: setWeight, + 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 +} + +func setWeight(_ *cobra.Command, args []string) error { + blockchainName := args[0] + err := prompts.ValidateNodeID(nodeIDStr) + if err != nil { + return err + } + + 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.Devnet: + 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 == "" { + 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 + } + + 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) + 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, network, subnetID, nodeID) + if err != nil { + return err + } + + // TODO: we need to wait for the balance from the removed validator to arrive in changeAddr + // set arbitrary time.sleep here? + + weight, err = app.Prompt.CaptureWeight("What weight would you like to assign to the validator?") + if err != nil { + return err + } + + balance, err = getValidatorBalanceFromPChain() + if err != nil { + return err + } + + publicKey, pop, err = getBLSInfoFromPChain() + if err != nil { + return err + } + + changeAddr, err = getChangeAddrFromPChain() + if err != nil { + return fmt.Errorf("failure parsing change owner address: %w", err) + } + + // add back validator to subnet with updated weight + return CallAddValidator(deployer, network, kc, useLedger, blockchainName, nodeID.String()) +} + +// getValidatorBalanceFromPChain gets remaining balance of validator from p chain +func getValidatorBalanceFromPChain() (uint64, error) { + return 0, nil +} + +// getBLSInfoFromPChain gets BLS public key and pop from info api +func getBLSInfoFromPChain() (string, string, error) { + return "", "", nil +} + +// getChangeAddrFromPChain gets validator change addr from info api +func getChangeAddrFromPChain() (string, error) { + 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 f512498f1..78e19cf7a 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -9,8 +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/avalanche-cli/pkg/binutils" "github.com/ava-labs/avalanche-cli/pkg/cobrautils" @@ -24,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" @@ -53,6 +62,7 @@ var ( useLedger bool useEwoq bool ledgerAddresses []string + sovereign bool subnetIDStr string mainnetChainID uint32 skipCreatePrompt bool @@ -61,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") @@ -88,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") @@ -116,6 +129,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, @@ -312,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, "", @@ -356,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 @@ -469,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, @@ -525,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 { @@ -575,13 +615,190 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { } } + 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("ConvertL1Tx ID: %s", ConvertL1TxID) + + if savePartialTx { + if err := SaveNotFullySignedTx( + "ConvertL1Tx", + tx, + chain, + subnetAuthKeys, + remainingSubnetAuthKeys, + outputTxPath, + false, + ); err != nil { + return err + } + } + + 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) 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 +} + +func getBLSInfo(publicKey, proofOfPossesion string) (signer.ProofOfPossession, error) { + type jsonProofOfPossession struct { + PublicKey string + ProofOfPossession string + } + jsonPop := jsonProofOfPossession{ + PublicKey: publicKey, + ProofOfPossession: proofOfPossesion, + } + popBytes, err := json.Marshal(jsonPop) + if err != nil { + return signer.ProofOfPossession{}, err + } + pop := &signer.ProofOfPossession{} + err = pop.UnmarshalJSON(popBytes) + if err != nil { + return signer.ProofOfPossession{}, err + } + return *pop, nil +} + +// 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 { + 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 := &txs.ConvertSubnetValidator{ + NodeID: nodeID[:], + Weight: validator.Weight, + Balance: validator.Balance, + Signer: blsInfo, + RemainingBalanceOwner: message.PChainOwner{ + Threshold: 1, + Addresses: addrs, + }, + } + bootstrapValidators = append(bootstrapValidators, bootstrapValidator) + } + return bootstrapValidators, nil } func ValidateSubnetNameAndGetChains(args []string) ([]string, error) { 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 0ef77f3f7..842a2cc45 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 } @@ -116,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 @@ -139,7 +146,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/blockchaincmd/remove_validator.go b/cmd/blockchaincmd/remove_validator.go index 72ece3c53..c903066ae 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" @@ -44,19 +46,22 @@ 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().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 } func removeValidator(_ *cobra.Command, args []string) error { - var ( - nodeID ids.NodeID - err error - ) + blockchainName := args[0] + _, err := ids.NodeIDFromString(args[1]) + if err != nil { + return err + } + nodeIDStr = args[1] network, err := networkoptions.GetNetworkFromCmdLineFlags( app, @@ -71,6 +76,16 @@ func removeValidator(_ *cobra.Command, args []string) error { return err } + if !sovereign { + 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) @@ -85,15 +100,23 @@ 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: - return removeFromLocal(blockchainName) + if !sovereign { + return removeFromLocalNonSOV(blockchainName) + } + case models.Devnet: + 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 == "" { useLedger, keyName, err = prompts.GetKeyOrLedger(app.Prompt, constants.PayTxsFeesMsg, app.GetKeyDir(), false) @@ -129,6 +152,81 @@ func removeValidator(_ *cobra.Command, args []string) error { return errNoSubnetID } + 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 { + // 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 !sovereign { + return removeValidatorNonSOV(deployer, network, subnetID, kc, blockchainName, nodeID) + } + return removeValidatorSOV(deployer, network, subnetID, nodeID) +} + +// 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(nodeID ids.NodeID) [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, network models.Network, subnetID ids.ID, nodeID ids.NodeID) 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(nodeID) + 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 + } + tx, err := deployer.SetL1ValidatorWeight(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, nodeID ids.NodeID) error { isPermissioned, controlKeys, threshold, err := txutils.GetOwners(network, subnetID) if err != nil { return err @@ -160,33 +258,10 @@ 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("remove as 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 { - // just warn the user, don't fail - ux.Logger.PrintToUser("failed to check if node is a validator on the subnet: %s", err) - } else if !isValidator { - // this is actually an error - return fmt.Errorf("node %s is not a validator on subnet %s", nodeID, subnetID) - } - ux.Logger.PrintToUser("NodeID: %s", nodeID.String()) ux.Logger.PrintToUser("Network: %s", network.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,11 +284,10 @@ func removeValidator(_ *cobra.Command, args []string) error { return err } } - return err } -func removeFromLocal(blockchainName string) error { +func removeFromLocalNonSOV(blockchainName string) error { sc, err := app.LoadSidecar(blockchainName) if err != nil { return err @@ -236,13 +310,6 @@ func removeFromLocal(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 { 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/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/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 4d43df3df..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 { @@ -76,34 +84,14 @@ 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) + 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 { @@ -128,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 { @@ -152,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 { @@ -176,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 { @@ -200,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 { @@ -224,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 { @@ -248,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 { @@ -274,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 { @@ -300,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 { @@ -320,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) } @@ -348,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 { @@ -372,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 { @@ -396,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 { @@ -420,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 { @@ -444,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 { @@ -468,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 { @@ -494,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 { @@ -518,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 { @@ -544,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 { @@ -568,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 { @@ -592,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 { @@ -616,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 { @@ -640,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 { @@ -664,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 { @@ -688,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 { @@ -712,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 { @@ -736,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 { @@ -760,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 { @@ -780,10 +876,42 @@ 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) + + 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 { + 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) + 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 8486b6f62..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,13 +155,15 @@ 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 - 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 @@ -273,6 +276,8 @@ const ( DevnetLocalAWMRelayerMetricsPort = 9092 FujiLocalAWMRelayerMetricsPort = 9093 + DevnetFlagsProposerVMUseCurrentHeight = true + SubnetEVMBin = "subnet-evm" DefaultNodeRunURL = "http://127.0.0.1:9650" @@ -327,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 fd4b0b84e..94af78ac8 100644 --- a/pkg/prompts/prompts.go +++ b/pkg/prompts/prompts.go @@ -106,9 +106,9 @@ 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) + 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) @@ -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() @@ -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() @@ -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 3a510c70f..1d6f4bad9 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 } @@ -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") + if val < 1 { + return fmt.Errorf("subnet validator balance must be at least 1 AVAX") } return nil } @@ -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 e82de963e..3f717f723 100644 --- a/pkg/subnet/public.go +++ b/pkg/subnet/public.go @@ -8,8 +8,14 @@ import ( "fmt" "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" @@ -33,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 { @@ -58,7 +66,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 +119,21 @@ func (d *PublicDeployer) AddValidator( return false, tx, remainingSubnetAuthKeys, nil } +func (d *PublicDeployer) SetL1ValidatorWeight( + message warp.Message, +) (*txs.Tx, error) { + return nil, nil +} + +func (d *PublicDeployer) RegisterL1Validator( + balance uint64, + signer signer.ProofOfPossession, + changeOwner fx.Owner, + message warp.Message, +) (*txs.Tx, error) { + 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) @@ -360,6 +383,87 @@ func (d *PublicDeployer) DeployBlockchain( return isFullySigned, id, tx, remainingSubnetAuthKeys, nil } +func (d *PublicDeployer) ConvertL1( + controlKeys []string, + subnetAuthKeysStrs []string, + subnetID ids.ID, + chainID ids.ID, + validatorManagerAddress goethereumcommon.Address, + validators []*txs.ConvertSubnetValidator, +) (bool, ids.ID, *txs.Tx, []string, error) { + ux.Logger.PrintToUser("Now calling ConvertL1 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(), "ConvertL1 transaction") + + tx, err := d.createConvertL1Tx(subnetAuthKeys, subnetID, chainID, validatorManagerAddress.Bytes(), validators, 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) 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, @@ -393,7 +497,7 @@ func (d *PublicDeployer) Commit( time.Sleep(sleepBetweenRepeats) } if issueTxErr != nil { - d.cleanCacheWallet() + d.CleanCacheWallet() } return tx.ID(), issueTxErr } @@ -447,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 } @@ -499,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 { @@ -507,6 +616,37 @@ func (d *PublicDeployer) createBlockchainTx( return &tx, nil } +func (d *PublicDeployer) createConvertL1Tx( + subnetAuthKeys []ids.ShortID, + subnetID ids.ID, + chainID ids.ID, + address []byte, + validators []*txs.ConvertSubnetValidator, + wallet primary.Wallet, +) (*txs.Tx, error) { + 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( subnetAuthKeys []ids.ShortID, subnetID ids.ID, @@ -691,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) } @@ -702,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