From 7175e303a005a65c678093b632782e05b54e0fa8 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 10 Oct 2024 18:43:17 -0400 Subject: [PATCH 01/11] one command --- cmd/blockchaincmd/deploy.go | 132 +++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 54 deletions(-) diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index aa7707f6e..0b3ecbebf 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -7,6 +7,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/ava-labs/avalanche-cli/pkg/node" "os" "path/filepath" "strings" @@ -464,6 +465,18 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { return PrintSubnetInfo(blockchainName, true) } + ux.Logger.PrintToUser("You can use your local machine as a bootstrap validator on the blockchain") + ux.Logger.PrintToUser("This means that you don't have to to set up a remote server on a cloud service (e.g. AWS / GCP) to be a validator on the blockchain.") + + useLocalMachine, err := app.Prompt.CaptureYesNo("Do you want to use your local machine as a bootstrap validator?") + if err != nil { + return err + } + + if useLocalMachine { + bootstrapEndpoints = []string{"http://127.0.0.1:9650"} + } + if len(bootstrapEndpoints) > 0 { var changeAddr string for _, endpoint := range bootstrapEndpoints { @@ -725,65 +738,76 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { 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, + clusterName, err := node.GetClusterNameFromList(app) + if err != nil { + return err + } + + if err = node.SyncSubnet(app, clusterName, blockchainName, true, nil); err != nil { + return err + } + + if err := node.WaitForHealthyCluster(app, clusterName, node.HealthCheckTimeout, node.HealthCheckPoolTime); err != nil { + return err + } + + 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 } - aggregatorExtraPeerEndpoints, err := GetAggregatorExtraPeerEndpoints(network) - if err != nil { - return err - } - if err := validatormanager.SetupPoA( - app, - network, - rpcURL, - contract.ChainSpec{ - BlockchainName: blockchainName, - }, - privateKey, - common.HexToAddress(sidecar.PoAValidatorManagerOwner), - avaGoBootstrapValidators, - aggregatorExtraPeerEndpoints, - ); err != nil { - return err - } - ux.Logger.GreenCheckmarkToUser("Subnet is successfully converted into Subnet Only Validator") } + rpcURL, _, err := contract.GetBlockchainEndpoints( + app, + network, + chainSpec, + true, + false, + ) + if err != nil { + return err + } + aggregatorExtraPeerEndpoints, err := GetAggregatorExtraPeerEndpoints(network) + if err != nil { + return err + } + if err := validatormanager.SetupPoA( + app, + network, + rpcURL, + contract.ChainSpec{ + BlockchainName: blockchainName, + }, + privateKey, + common.HexToAddress(sidecar.PoAValidatorManagerOwner), + avaGoBootstrapValidators, + aggregatorExtraPeerEndpoints, + ); err != nil { + return err + } + ux.Logger.GreenCheckmarkToUser("Subnet is successfully converted into Subnet Only Validator") } flags := make(map[string]string) From 151e1a927daa84a81b2cab114e8db840a8c16d97 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 10 Oct 2024 19:03:38 -0400 Subject: [PATCH 02/11] 1 command sync local --- cmd/blockchaincmd/deploy.go | 9 +- cmd/nodecmd/local.go | 93 +--------- pkg/node/helper.go | 298 ++++++++++++++++++++++++++++++++ pkg/node/sync.go | 330 ++++++++++++++++++++++++++++++++++++ 4 files changed, 637 insertions(+), 93 deletions(-) create mode 100644 pkg/node/helper.go create mode 100644 pkg/node/sync.go diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index 0b3ecbebf..1b8c7ab92 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -738,9 +738,12 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { return err } - clusterName, err := node.GetClusterNameFromList(app) - if err != nil { - return err + var clusterName string + if !useLocalMachine { + clusterName, err = node.GetClusterNameFromList(app) + if err != nil { + return err + } } if err = node.SyncSubnet(app, clusterName, blockchainName, true, nil); err != nil { diff --git a/cmd/nodecmd/local.go b/cmd/nodecmd/local.go index b627f41b0..6934f4b73 100644 --- a/cmd/nodecmd/local.go +++ b/cmd/nodecmd/local.go @@ -4,6 +4,7 @@ package nodecmd import ( "fmt" + "github.com/ava-labs/avalanche-cli/pkg/node" "os" "path/filepath" @@ -17,9 +18,6 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanche-network-runner/client" - anrutils "github.com/ava-labs/avalanche-network-runner/utils" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/set" "github.com/spf13/cobra" ) @@ -395,7 +393,7 @@ func localDestroyNode(_ *cobra.Command, args []string) error { return err } - if ok, err := checkClusterIsLocal(clusterName); err != nil || !ok { + if ok, err := node.CheckClusterIsLocal(app, clusterName); err != nil || !ok { return fmt.Errorf("local cluster %q not found", clusterName) } @@ -425,93 +423,8 @@ func addLocalClusterConfig(network models.Network) error { return app.WriteClustersConfigFile(&clustersConfig) } -func checkClusterIsLocal(clusterName string) (bool, error) { - clustersConfig := models.ClustersConfig{} - if app.ClustersConfigExists() { - var err error - clustersConfig, err = app.LoadClustersConfig() - if err != nil { - return false, err - } - } - clusterConf, ok := clustersConfig.Clusters[clusterName] - return ok && clusterConf.Local, nil -} - func localTrack(_ *cobra.Command, args []string) error { clusterName := args[0] blockchainName := args[1] - if ok, err := checkClusterIsLocal(clusterName); err != nil || !ok { - return fmt.Errorf("local node %q is not found", clusterName) - } - sc, err := app.LoadSidecar(blockchainName) - if err != nil { - return err - } - clustersConfig, err := app.LoadClustersConfig() - if err != nil { - return err - } - clusterConfig := clustersConfig.Clusters[clusterName] - network := clusterConfig.Network - if sc.Networks[network.Name()].BlockchainID == ids.Empty { - return fmt.Errorf("blockchain %s has not been deployed to %s", blockchainName, network.Name()) - } - subnetID := sc.Networks[network.Name()].SubnetID - chainVMID, err := anrutils.VMID(blockchainName) - if err != nil { - return fmt.Errorf("failed to create VM ID from %s: %w", blockchainName, err) - } - var vmBin string - switch sc.VM { - case models.SubnetEvm: - _, vmBin, err = binutils.SetupSubnetEVM(app, sc.VMVersion) - if err != nil { - return fmt.Errorf("failed to install subnet-evm: %w", err) - } - case models.CustomVM: - vmBin = binutils.SetupCustomBin(app, blockchainName) - default: - return fmt.Errorf("unknown vm: %s", sc.VM) - } - binaryDownloader := binutils.NewPluginBinaryDownloader(app) - if err := binaryDownloader.InstallVM(chainVMID.String(), vmBin); err != nil { - return err - } - cli, err := binutils.NewGRPCClientWithEndpoint( - binutils.LocalClusterGRPCServerEndpoint, - binutils.WithAvoidRPCVersionCheck(true), - binutils.WithDialTimeout(constants.FastGRPCDialTimeout), - ) - if err != nil { - return err - } - ctx, cancel := utils.GetANRContext() - defer cancel() - status, err := cli.Status(ctx) - if err != nil { - return err - } - publicEndpoints := []string{} - for _, nodeInfo := range status.ClusterInfo.NodeInfos { - if _, err := cli.RestartNode(ctx, nodeInfo.Name, client.WithWhitelistedSubnets(subnetID.String())); err != nil { - return err - } - publicEndpoints = append(publicEndpoints, nodeInfo.Uri) - } - networkInfo := sc.Networks[network.Name()] - rpcEndpoints := set.Of(networkInfo.RPCEndpoints...) - wsEndpoints := set.Of(networkInfo.WSEndpoints...) - for _, publicEndpoint := range publicEndpoints { - rpcEndpoints.Add(getRPCEndpoint(publicEndpoint, networkInfo.BlockchainID.String())) - wsEndpoints.Add(getWSEndpoint(publicEndpoint, networkInfo.BlockchainID.String())) - } - networkInfo.RPCEndpoints = rpcEndpoints.List() - networkInfo.WSEndpoints = wsEndpoints.List() - sc.Networks[clusterConfig.Network.Name()] = networkInfo - if err := app.UpdateSidecar(&sc); err != nil { - return err - } - ux.Logger.GreenCheckmarkToUser("%s successfully tracking %s", clusterName, blockchainName) - return nil + return node.TrackSubnetWithLocalMachine(app, clusterName, blockchainName) } diff --git a/pkg/node/helper.go b/pkg/node/helper.go new file mode 100644 index 000000000..d97fec3f9 --- /dev/null +++ b/pkg/node/helper.go @@ -0,0 +1,298 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package node + +import ( + "encoding/json" + "fmt" + "sync" + "time" + + "github.com/ava-labs/avalanche-cli/pkg/ansible" + + "github.com/ava-labs/avalanche-cli/pkg/application" + "github.com/ava-labs/avalanche-cli/pkg/constants" + "github.com/ava-labs/avalanche-cli/pkg/models" + "github.com/ava-labs/avalanche-cli/pkg/ssh" + "github.com/ava-labs/avalanche-cli/pkg/utils" + "github.com/ava-labs/avalanche-cli/pkg/ux" + "github.com/ava-labs/avalanchego/api/info" +) + +const ( + HealthCheckPoolTime = 60 * time.Second + HealthCheckTimeout = 3 * time.Minute +) + +func AuthorizedAccessFromSettings(app *application.Avalanche) bool { + return app.Conf.GetConfigBoolValue(constants.ConfigAuthorizeCloudAccessKey) +} + +func CheckCluster(app *application.Avalanche, clusterName string) error { + _, err := GetClusterNodes(app, clusterName) + return err +} + +func GetClusterNodes(app *application.Avalanche, clusterName string) ([]string, error) { + if exists, err := CheckClusterExists(app, clusterName); err != nil || !exists { + return nil, fmt.Errorf("cluster %q not found", clusterName) + } + clustersConfig, err := app.LoadClustersConfig() + if err != nil { + return nil, err + } + clusterNodes := clustersConfig.Clusters[clusterName].Nodes + if len(clusterNodes) == 0 { + return nil, fmt.Errorf("no nodes found in cluster %s", clusterName) + } + return clusterNodes, nil +} + +func CheckClusterExists(app *application.Avalanche, clusterName string) (bool, error) { + clustersConfig := models.ClustersConfig{} + if app.ClustersConfigExists() { + var err error + clustersConfig, err = app.LoadClustersConfig() + if err != nil { + return false, err + } + } + _, ok := clustersConfig.Clusters[clusterName] + return ok, nil +} + +func CheckHostsAreRPCCompatible(app *application.Avalanche, hosts []*models.Host, subnetName string) error { + incompatibleNodes, err := getRPCIncompatibleNodes(app, hosts, subnetName) + if err != nil { + return err + } + if len(incompatibleNodes) > 0 { + sc, err := app.LoadSidecar(subnetName) + if err != nil { + return err + } + ux.Logger.PrintToUser("Either modify your Avalanche Go version or modify your VM version") + ux.Logger.PrintToUser("To modify your Avalanche Go version: https://docs.avax.network/nodes/maintain/upgrade-your-avalanchego-node") + switch sc.VM { + case models.SubnetEvm: + ux.Logger.PrintToUser("To modify your Subnet-EVM version: https://docs.avax.network/build/subnet/upgrade/upgrade-subnet-vm") + case models.CustomVM: + ux.Logger.PrintToUser("To modify your Custom VM binary: avalanche subnet upgrade vm %s --config", subnetName) + } + ux.Logger.PrintToUser("Yoy can use \"avalanche node upgrade\" to upgrade Avalanche Go and/or Subnet-EVM to their latest versions") + return fmt.Errorf("the Avalanche Go version of node(s) %s is incompatible with VM RPC version of %s", incompatibleNodes, subnetName) + } + return nil +} + +func getRPCIncompatibleNodes(app *application.Avalanche, hosts []*models.Host, subnetName string) ([]string, error) { + ux.Logger.PrintToUser("Checking compatibility of node(s) avalanche go RPC protocol version with Subnet EVM RPC of subnet %s ...", subnetName) + sc, err := app.LoadSidecar(subnetName) + if err != nil { + return nil, err + } + wg := sync.WaitGroup{} + wgResults := models.NodeResults{} + for _, host := range hosts { + wg.Add(1) + go func(nodeResults *models.NodeResults, host *models.Host) { + defer wg.Done() + if resp, err := ssh.RunSSHCheckAvalancheGoVersion(host); err != nil { + nodeResults.AddResult(host.GetCloudID(), nil, err) + return + } else { + if _, rpcVersion, err := ParseAvalancheGoOutput(resp); err != nil { + nodeResults.AddResult(host.GetCloudID(), nil, err) + } else { + nodeResults.AddResult(host.GetCloudID(), rpcVersion, err) + } + } + }(&wgResults, host) + } + wg.Wait() + if wgResults.HasErrors() { + return nil, fmt.Errorf("failed to get rpc protocol version for node(s) %s", wgResults.GetErrorHostMap()) + } + incompatibleNodes := []string{} + for nodeID, rpcVersionI := range wgResults.GetResultMap() { + rpcVersion := rpcVersionI.(uint32) + if rpcVersion != uint32(sc.RPCVersion) { + incompatibleNodes = append(incompatibleNodes, nodeID) + } + } + if len(incompatibleNodes) > 0 { + ux.Logger.PrintToUser(fmt.Sprintf("Compatible Avalanche Go RPC version is %d", sc.RPCVersion)) + } + return incompatibleNodes, nil +} + +func ParseAvalancheGoOutput(byteValue []byte) (string, uint32, error) { + reply := map[string]interface{}{} + if err := json.Unmarshal(byteValue, &reply); err != nil { + return "", 0, err + } + resultMap := reply["result"] + resultJSON, err := json.Marshal(resultMap) + if err != nil { + return "", 0, err + } + + nodeVersionReply := info.GetNodeVersionReply{} + if err := json.Unmarshal(resultJSON, &nodeVersionReply); err != nil { + return "", 0, err + } + return nodeVersionReply.VMVersions["platform"], uint32(nodeVersionReply.RPCProtocolVersion), nil +} + +func DisconnectHosts(hosts []*models.Host) { + for _, host := range hosts { + _ = host.Disconnect() + } +} + +func getWSEndpoint(endpoint string, blockchainID string) string { + return models.NewDevnetNetwork(endpoint, 0).BlockchainWSEndpoint(blockchainID) +} + +func getPublicEndpoints( + app *application.Avalanche, + clusterName string, + trackers []*models.Host, +) ([]string, error) { + clusterConfig, err := app.GetClusterConfig(clusterName) + if err != nil { + return nil, err + } + publicNodes := clusterConfig.APINodes + if clusterConfig.Network.Kind == models.Devnet { + publicNodes = clusterConfig.Nodes + } + 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 +} + +func getRPCEndpoint(endpoint string, blockchainID string) string { + return models.NewDevnetNetwork(endpoint, 0).BlockchainEndpoint(blockchainID) +} + +func GetAvalancheGoEndpoint(ip string) string { + return fmt.Sprintf("http://%s:%d", ip, constants.AvalanchegoAPIPort) +} + +func GetUnhealthyNodes(hosts []*models.Host) ([]string, error) { + wg := sync.WaitGroup{} + wgResults := models.NodeResults{} + for _, host := range hosts { + wg.Add(1) + go func(nodeResults *models.NodeResults, host *models.Host) { + defer wg.Done() + if resp, err := ssh.RunSSHCheckHealthy(host); err != nil { + nodeResults.AddResult(host.GetCloudID(), nil, err) + return + } else { + if isHealthy, err := parseHealthyOutput(resp); err != nil { + nodeResults.AddResult(host.GetCloudID(), nil, err) + } else { + nodeResults.AddResult(host.GetCloudID(), isHealthy, err) + } + } + }(&wgResults, host) + } + wg.Wait() + if wgResults.HasErrors() { + return nil, fmt.Errorf("failed to get health status for node(s) %s", wgResults.GetErrorHostMap()) + } + return utils.Filter(wgResults.GetNodeList(), func(nodeID string) bool { + return !wgResults.GetResultMap()[nodeID].(bool) + }), nil +} + +func parseHealthyOutput(byteValue []byte) (bool, error) { + var result map[string]interface{} + if err := json.Unmarshal(byteValue, &result); err != nil { + return false, err + } + isHealthyInterface, ok := result["result"].(map[string]interface{}) + if ok { + isHealthy, ok := isHealthyInterface["healthy"].(bool) + if ok { + return isHealthy, nil + } + } + return false, fmt.Errorf("unable to parse node healthy status") +} + +func WaitForHealthyCluster( + app *application.Avalanche, + clusterName string, + timeout time.Duration, + poolTime time.Duration, +) error { + ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("Waiting for node(s) in cluster %s to be healthy...", clusterName) + clustersConfig, err := app.LoadClustersConfig() + if err != nil { + return err + } + cluster, ok := clustersConfig.Clusters[clusterName] + if !ok { + return fmt.Errorf("cluster %s does not exist", clusterName) + } + allHosts, err := ansible.GetInventoryFromAnsibleInventoryFile(app.GetAnsibleInventoryDirPath(clusterName)) + if err != nil { + return err + } + hosts := cluster.GetValidatorHosts(allHosts) // exlude api nodes + defer DisconnectHosts(hosts) + startTime := time.Now() + spinSession := ux.NewUserSpinner() + spinner := spinSession.SpinToUser("Checking if node(s) are healthy...") + for { + unhealthyNodes, err := GetUnhealthyNodes(hosts) + if err != nil { + ux.SpinFailWithError(spinner, "", err) + return err + } + if len(unhealthyNodes) == 0 { + ux.SpinComplete(spinner) + spinSession.Stop() + ux.Logger.GreenCheckmarkToUser("Nodes healthy after %d seconds", uint32(time.Since(startTime).Seconds())) + return nil + } + if time.Since(startTime) > timeout { + ux.SpinFailWithError(spinner, "", fmt.Errorf("cluster not healthy after %d seconds", uint32(timeout.Seconds()))) + spinSession.Stop() + ux.Logger.PrintToUser("") + ux.Logger.RedXToUser("Unhealthy Nodes") + for _, failedNode := range unhealthyNodes { + ux.Logger.PrintToUser(" " + failedNode) + } + ux.Logger.PrintToUser("") + return fmt.Errorf("cluster not healthy after %d seconds", uint32(timeout.Seconds())) + } + time.Sleep(poolTime) + } +} + +func GetClusterNameFromList(app *application.Avalanche) (string, error) { + clusterNames, err := app.ListClusterNames() + if err != nil { + return "", err + } + if len(clusterNames) == 0 { + return "", fmt.Errorf("no Avalanche nodes found that can track the blockchain, please create Avalanche nodes first through `avalanche node create`") + } + clusterName, err := app.Prompt.CaptureList( + "Which cluster of Avalanche nodes would you like to use to track the blockchain?", + clusterNames, + ) + if err != nil { + return "", err + } + return clusterName, nil +} diff --git a/pkg/node/sync.go b/pkg/node/sync.go new file mode 100644 index 000000000..9b7b1a9e8 --- /dev/null +++ b/pkg/node/sync.go @@ -0,0 +1,330 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package node + +import ( + "encoding/json" + "errors" + "fmt" + "github.com/ava-labs/avalanche-cli/pkg/binutils" + "github.com/ava-labs/avalanche-cli/pkg/constants" + "github.com/ava-labs/avalanche-network-runner/client" + anrutils "github.com/ava-labs/avalanche-network-runner/utils" + "github.com/ava-labs/avalanchego/ids" + "sync" + + "github.com/ava-labs/avalanche-cli/pkg/ansible" + "github.com/ava-labs/avalanche-cli/pkg/application" + "github.com/ava-labs/avalanche-cli/pkg/models" + "github.com/ava-labs/avalanche-cli/pkg/ssh" + "github.com/ava-labs/avalanche-cli/pkg/subnet" + "github.com/ava-labs/avalanche-cli/pkg/utils" + "github.com/ava-labs/avalanche-cli/pkg/ux" + "github.com/ava-labs/avalanchego/utils/set" +) + +func SyncSubnet(app *application.Avalanche, clusterName, blockchainName string, avoidChecks bool, subnetAliases []string) error { + if err := CheckCluster(app, clusterName); err != nil { + return err + } + clusterConfig, err := app.GetClusterConfig(clusterName) + if err != nil { + return err + } + if _, err := subnet.ValidateSubnetNameAndGetChains(app, []string{blockchainName}); err != nil { + return err + } + hosts, err := ansible.GetInventoryFromAnsibleInventoryFile(app.GetAnsibleInventoryDirPath(clusterName)) + if err != nil { + return err + } + defer DisconnectHosts(hosts) + if !avoidChecks { + if err := CheckHostsAreBootstrapped(hosts); err != nil { + return err + } + if err := CheckHostsAreHealthy(hosts); err != nil { + return err + } + if err := CheckHostsAreRPCCompatible(app, hosts, blockchainName); err != nil { + return err + } + } + if err := prepareSubnetPlugin(app, hosts, blockchainName); err != nil { + return err + } + if err := trackSubnet(app, hosts, clusterName, clusterConfig.Network, blockchainName, subnetAliases); err != nil { + return err + } + ux.Logger.PrintToUser("Node(s) successfully started syncing with blockchain!") + ux.Logger.PrintToUser(fmt.Sprintf("Check node blockchain syncing status with avalanche node status %s --blockchain %s", clusterName, blockchainName)) + return nil +} + +// prepareSubnetPlugin creates subnet plugin to all nodes in the cluster +func prepareSubnetPlugin(app *application.Avalanche, hosts []*models.Host, blockchainName string) error { + sc, err := app.LoadSidecar(blockchainName) + if err != nil { + return err + } + wg := sync.WaitGroup{} + wgResults := models.NodeResults{} + for _, host := range hosts { + wg.Add(1) + go func(nodeResults *models.NodeResults, host *models.Host) { + defer wg.Done() + if err := ssh.RunSSHCreatePlugin(host, sc); err != nil { + nodeResults.AddResult(host.NodeID, nil, err) + } + }(&wgResults, host) + } + wg.Wait() + if wgResults.HasErrors() { + return fmt.Errorf("failed to upload plugin to node(s) %s", wgResults.GetErrorHostMap()) + } + return nil +} + +func trackSubnet( + app *application.Avalanche, + hosts []*models.Host, + clusterName string, + network models.Network, + blockchainName string, + subnetAliases []string, +) error { + // load cluster config + clusterConfig, err := app.GetClusterConfig(clusterName) + if err != nil { + return err + } + // and get list of subnets + allSubnets := utils.Unique(append(clusterConfig.Subnets, blockchainName)) + + // load sidecar to get subnet blockchain ID + sc, err := app.LoadSidecar(blockchainName) + if err != nil { + return err + } + blockchainID := sc.Networks[network.Name()].BlockchainID + + wg := sync.WaitGroup{} + wgResults := models.NodeResults{} + subnetAliases = append([]string{blockchainName}, subnetAliases...) + for _, host := range hosts { + wg.Add(1) + go func(nodeResults *models.NodeResults, host *models.Host) { + defer wg.Done() + if err := ssh.RunSSHStopNode(host); err != nil { + nodeResults.AddResult(host.NodeID, nil, err) + } + + if err := ssh.RunSSHRenderAvagoAliasConfigFile( + host, + blockchainID.String(), + subnetAliases, + ); err != nil { + nodeResults.AddResult(host.NodeID, nil, err) + } + if err := ssh.RunSSHRenderAvalancheNodeConfig( + app, + host, + network, + allSubnets, + clusterConfig.IsAPIHost(host.GetCloudID()), + ); err != nil { + nodeResults.AddResult(host.NodeID, nil, err) + } + if err := ssh.RunSSHSyncSubnetData(app, host, network, blockchainName); err != nil { + nodeResults.AddResult(host.NodeID, nil, err) + } + if err := ssh.RunSSHStartNode(host); err != nil { + nodeResults.AddResult(host.NodeID, nil, err) + return + } + }(&wgResults, host) + } + wg.Wait() + if wgResults.HasErrors() { + return fmt.Errorf("failed to track subnet for node(s) %s", wgResults.GetErrorHostMap()) + } + + // update slice of subnets synced by the cluster + clusterConfig.Subnets = allSubnets + err = app.SetClusterConfig(network.ClusterName, clusterConfig) + if err != nil { + return err + } + + // update slice of blockchain endpoints with the cluster ones + networkInfo := sc.Networks[clusterConfig.Network.Name()] + rpcEndpoints := set.Of(networkInfo.RPCEndpoints...) + wsEndpoints := set.Of(networkInfo.WSEndpoints...) + publicEndpoints, err := getPublicEndpoints(app, clusterName, hosts) + if err != nil { + return err + } + for _, publicEndpoint := range publicEndpoints { + rpcEndpoints.Add(getRPCEndpoint(publicEndpoint, networkInfo.BlockchainID.String())) + wsEndpoints.Add(getWSEndpoint(publicEndpoint, networkInfo.BlockchainID.String())) + } + networkInfo.RPCEndpoints = rpcEndpoints.List() + networkInfo.WSEndpoints = wsEndpoints.List() + sc.Networks[clusterConfig.Network.Name()] = networkInfo + return app.UpdateSidecar(&sc) +} + +func CheckHostsAreBootstrapped(hosts []*models.Host) error { + notBootstrappedNodes, err := GetNotBootstrappedNodes(hosts) + if err != nil { + return err + } + if len(notBootstrappedNodes) > 0 { + return fmt.Errorf("node(s) %s are not bootstrapped yet, please try again later", notBootstrappedNodes) + } + return nil +} + +func CheckHostsAreHealthy(hosts []*models.Host) error { + ux.Logger.PrintToUser("Checking if node(s) are healthy...") + unhealthyNodes, err := GetUnhealthyNodes(hosts) + if err != nil { + return err + } + if len(unhealthyNodes) > 0 { + return fmt.Errorf("node(s) %s are not healthy, please check the issue and try again later", unhealthyNodes) + } + return nil +} + +func GetNotBootstrappedNodes(hosts []*models.Host) ([]string, error) { + wg := sync.WaitGroup{} + wgResults := models.NodeResults{} + for _, host := range hosts { + wg.Add(1) + go func(nodeResults *models.NodeResults, host *models.Host) { + defer wg.Done() + if resp, err := ssh.RunSSHCheckBootstrapped(host); err != nil { + nodeResults.AddResult(host.GetCloudID(), nil, err) + return + } else { + if isBootstrapped, err := parseBootstrappedOutput(resp); err != nil { + nodeResults.AddResult(host.GetCloudID(), nil, err) + } else { + nodeResults.AddResult(host.GetCloudID(), isBootstrapped, err) + } + } + }(&wgResults, host) + } + wg.Wait() + if wgResults.HasErrors() { + return nil, fmt.Errorf("failed to get avalanchego bootstrap status for node(s) %s", wgResults.GetErrorHostMap()) + } + return utils.Filter(wgResults.GetNodeList(), func(nodeID string) bool { + return !wgResults.GetResultMap()[nodeID].(bool) + }), nil +} + +func parseBootstrappedOutput(byteValue []byte) (bool, error) { + var result map[string]interface{} + if err := json.Unmarshal(byteValue, &result); err != nil { + return false, err + } + isBootstrappedInterface, ok := result["result"].(map[string]interface{}) + if ok { + isBootstrapped, ok := isBootstrappedInterface["isBootstrapped"].(bool) + if ok { + return isBootstrapped, nil + } + } + return false, errors.New("unable to parse node bootstrap status") +} + +func TrackSubnetWithLocalMachine(app *application.Avalanche, clusterName, blockchainName string) error { + if ok, err := checkClusterIsLocal(app, clusterName); err != nil || !ok { + return fmt.Errorf("local node %q is not found", clusterName) + } + sc, err := app.LoadSidecar(blockchainName) + if err != nil { + return err + } + clustersConfig, err := app.LoadClustersConfig() + if err != nil { + return err + } + clusterConfig := clustersConfig.Clusters[clusterName] + network := clusterConfig.Network + if sc.Networks[network.Name()].BlockchainID == ids.Empty { + return fmt.Errorf("blockchain %s has not been deployed to %s", blockchainName, network.Name()) + } + subnetID := sc.Networks[network.Name()].SubnetID + chainVMID, err := anrutils.VMID(blockchainName) + if err != nil { + return fmt.Errorf("failed to create VM ID from %s: %w", blockchainName, err) + } + var vmBin string + switch sc.VM { + case models.SubnetEvm: + _, vmBin, err = binutils.SetupSubnetEVM(app, sc.VMVersion) + if err != nil { + return fmt.Errorf("failed to install subnet-evm: %w", err) + } + case models.CustomVM: + vmBin = binutils.SetupCustomBin(app, blockchainName) + default: + return fmt.Errorf("unknown vm: %s", sc.VM) + } + binaryDownloader := binutils.NewPluginBinaryDownloader(app) + if err := binaryDownloader.InstallVM(chainVMID.String(), vmBin); err != nil { + return err + } + cli, err := binutils.NewGRPCClientWithEndpoint( + binutils.LocalClusterGRPCServerEndpoint, + binutils.WithAvoidRPCVersionCheck(true), + binutils.WithDialTimeout(constants.FastGRPCDialTimeout), + ) + if err != nil { + return err + } + ctx, cancel := utils.GetANRContext() + defer cancel() + status, err := cli.Status(ctx) + if err != nil { + return err + } + publicEndpoints := []string{} + for _, nodeInfo := range status.ClusterInfo.NodeInfos { + if _, err := cli.RestartNode(ctx, nodeInfo.Name, client.WithWhitelistedSubnets(subnetID.String())); err != nil { + return err + } + publicEndpoints = append(publicEndpoints, nodeInfo.Uri) + } + networkInfo := sc.Networks[network.Name()] + rpcEndpoints := set.Of(networkInfo.RPCEndpoints...) + wsEndpoints := set.Of(networkInfo.WSEndpoints...) + for _, publicEndpoint := range publicEndpoints { + rpcEndpoints.Add(getRPCEndpoint(publicEndpoint, networkInfo.BlockchainID.String())) + wsEndpoints.Add(getWSEndpoint(publicEndpoint, networkInfo.BlockchainID.String())) + } + networkInfo.RPCEndpoints = rpcEndpoints.List() + networkInfo.WSEndpoints = wsEndpoints.List() + sc.Networks[clusterConfig.Network.Name()] = networkInfo + if err := app.UpdateSidecar(&sc); err != nil { + return err + } + ux.Logger.GreenCheckmarkToUser("%s successfully tracking %s", clusterName, blockchainName) + return nil +} + +func CheckClusterIsLocal(app *application.Avalanche, clusterName string) (bool, error) { + clustersConfig := models.ClustersConfig{} + if app.ClustersConfigExists() { + var err error + clustersConfig, err = app.LoadClustersConfig() + if err != nil { + return false, err + } + } + clusterConf, ok := clustersConfig.Clusters[clusterName] + return ok && clusterConf.Local, nil +} From 32ef65cad2ab6d0db9006046b051eea6abddf3b8 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 10 Oct 2024 19:18:26 -0400 Subject: [PATCH 03/11] 1 command --- cmd/blockchaincmd/deploy.go | 25 ++++++++----- pkg/node/sync.go | 2 +- pkg/subnet/helpers.go | 73 +++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 11 deletions(-) diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index 1b8c7ab92..e3465a40a 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -738,20 +738,25 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { return err } - var clusterName string + clusterName, err := node.GetClusterNameFromList(app) + if err != nil { + return err + } + if !useLocalMachine { - clusterName, err = node.GetClusterNameFromList(app) - if err != nil { + if err = node.SyncSubnet(app, clusterName, blockchainName, true, nil); err != nil { + return err + } + if err := node.WaitForHealthyCluster(app, clusterName, node.HealthCheckTimeout, node.HealthCheckPoolTime); err != nil { + return err + } + } else { + if err := node.TrackSubnetWithLocalMachine(app, clusterName, blockchainName); err != nil { return err } - } - - if err = node.SyncSubnet(app, clusterName, blockchainName, true, nil); err != nil { - return err - } - if err := node.WaitForHealthyCluster(app, clusterName, node.HealthCheckTimeout, node.HealthCheckPoolTime); err != nil { - return err + // TODO: replace wait below wiht check for healhty in local machine + time.Sleep(70 * time.Second) } chainSpec := contract.ChainSpec{ diff --git a/pkg/node/sync.go b/pkg/node/sync.go index 9b7b1a9e8..a682d5b0a 100644 --- a/pkg/node/sync.go +++ b/pkg/node/sync.go @@ -241,7 +241,7 @@ func parseBootstrappedOutput(byteValue []byte) (bool, error) { } func TrackSubnetWithLocalMachine(app *application.Avalanche, clusterName, blockchainName string) error { - if ok, err := checkClusterIsLocal(app, clusterName); err != nil || !ok { + if ok, err := CheckClusterIsLocal(app, clusterName); err != nil || !ok { return fmt.Errorf("local node %q is not found", clusterName) } sc, err := app.LoadSidecar(blockchainName) diff --git a/pkg/subnet/helpers.go b/pkg/subnet/helpers.go index 49b1e0b80..90568e38a 100644 --- a/pkg/subnet/helpers.go +++ b/pkg/subnet/helpers.go @@ -3,12 +3,22 @@ package subnet import ( + "encoding/json" + "errors" + "fmt" "github.com/ava-labs/avalanche-cli/pkg/application" + "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/key" "github.com/ava-labs/avalanche-cli/pkg/models" "github.com/ava-labs/avalanche-cli/pkg/utils" + "os" + "path/filepath" + "unicode" ) +var errIllegalNameCharacter = errors.New( + "illegal name character: only letters, no special characters allowed") + func GetDefaultSubnetAirdropKeyInfo(app *application.Avalanche, subnetName string) (string, string, string, error) { keyName := utils.GetDefaultBlockchainAirdropKeyName(subnetName) keyPath := app.GetKeyPath(keyName) @@ -67,3 +77,66 @@ func GetSubnetAirdropKeyInfo( } return "", "", "", nil } + +func ValidateSubnetNameAndGetChains(app *application.Avalanche, args []string) ([]string, error) { + // this should not be necessary but some bright guy might just be creating + // the genesis by hand or something... + if err := checkInvalidSubnetNames(args[0]); err != nil { + return nil, fmt.Errorf("subnet name %s is invalid: %w", args[0], err) + } + // Check subnet exists + // TODO create a file that lists chains by subnet for fast querying + chains, err := getChainsInSubnet(app, args[0]) + if err != nil { + return nil, fmt.Errorf("failed to getChainsInSubnet: %w", err) + } + + if len(chains) == 0 { + return nil, errors.New("Invalid subnet " + args[0]) + } + + return chains, nil +} + +func checkInvalidSubnetNames(name string) error { + // this is currently exactly the same code as in avalanchego/vms/platformvm/create_chain_tx.go + for _, r := range name { + if r > unicode.MaxASCII || !(unicode.IsLetter(r) || unicode.IsNumber(r) || r == ' ') { + return errIllegalNameCharacter + } + } + return nil +} + +func getChainsInSubnet(app *application.Avalanche, blockchainName string) ([]string, error) { + subnets, err := os.ReadDir(app.GetSubnetDir()) + if err != nil { + return nil, fmt.Errorf("failed to read baseDir: %w", err) + } + + chains := []string{} + + for _, s := range subnets { + if !s.IsDir() { + continue + } + sidecarFile := filepath.Join(app.GetSubnetDir(), s.Name(), constants.SidecarFileName) + if _, err := os.Stat(sidecarFile); err == nil { + // read in sidecar file + jsonBytes, err := os.ReadFile(sidecarFile) + if err != nil { + return nil, fmt.Errorf("failed reading file %s: %w", sidecarFile, err) + } + + var sc models.Sidecar + err = json.Unmarshal(jsonBytes, &sc) + if err != nil { + return nil, fmt.Errorf("failed unmarshaling file %s: %w", sidecarFile, err) + } + if sc.Subnet == blockchainName { + chains = append(chains, sc.Name) + } + } + } + return chains, nil +} From 0e7067c8e5524c532faaa98d4682fa6fc088b0bb Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Thu, 10 Oct 2024 19:19:20 -0400 Subject: [PATCH 04/11] fix lint --- cmd/blockchaincmd/deploy.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index e3465a40a..abb708555 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -7,12 +7,13 @@ import ( "encoding/json" "errors" "fmt" - "github.com/ava-labs/avalanche-cli/pkg/node" "os" "path/filepath" "strings" "time" + "github.com/ava-labs/avalanche-cli/pkg/node" + "github.com/ava-labs/avalanchego/api/info" "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" "github.com/ethereum/go-ethereum/common" From 8b7f331a9042abd0dcebf02af76fb2db6c83163e Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 14 Oct 2024 14:46:54 -0400 Subject: [PATCH 05/11] wait for healthy local --- cmd/nodecmd/local.go | 6 ++---- pkg/node/sync.go | 4 ++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cmd/nodecmd/local.go b/cmd/nodecmd/local.go index 8ad475604..6345f71ed 100644 --- a/cmd/nodecmd/local.go +++ b/cmd/nodecmd/local.go @@ -4,10 +4,11 @@ package nodecmd import ( "fmt" - "github.com/ava-labs/avalanche-cli/pkg/node" "os" "path/filepath" + "github.com/ava-labs/avalanche-cli/pkg/node" + "github.com/ava-labs/avalanche-cli/pkg/binutils" "github.com/ava-labs/avalanche-cli/pkg/cobrautils" "github.com/ava-labs/avalanche-cli/pkg/constants" @@ -18,10 +19,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanche-network-runner/client" - anrutils "github.com/ava-labs/avalanche-network-runner/utils" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" - "github.com/ava-labs/avalanchego/utils/set" "github.com/spf13/cobra" ) diff --git a/pkg/node/sync.go b/pkg/node/sync.go index a682d5b0a..a0af66576 100644 --- a/pkg/node/sync.go +++ b/pkg/node/sync.go @@ -299,6 +299,10 @@ func TrackSubnetWithLocalMachine(app *application.Avalanche, clusterName, blockc } publicEndpoints = append(publicEndpoints, nodeInfo.Uri) } + _, err = cli.WaitForHealthy(ctx) + if err != nil { + return err + } networkInfo := sc.Networks[network.Name()] rpcEndpoints := set.Of(networkInfo.RPCEndpoints...) wsEndpoints := set.Of(networkInfo.WSEndpoints...) From 66144491dcfb58aa71e3fbdd373947a9ee72336e Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 14 Oct 2024 14:47:11 -0400 Subject: [PATCH 06/11] wait for healthy local --- cmd/blockchaincmd/deploy.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index abb708555..eb2c40813 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -755,9 +755,6 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { if err := node.TrackSubnetWithLocalMachine(app, clusterName, blockchainName); err != nil { return err } - - // TODO: replace wait below wiht check for healhty in local machine - time.Sleep(70 * time.Second) } chainSpec := contract.ChainSpec{ From 6217d993d0a2c03a3761095b68b38c845f9500c0 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 14 Oct 2024 14:51:20 -0400 Subject: [PATCH 07/11] fix lint --- cmd/nodecmd/local.go | 9 +++++++-- pkg/node/sync.go | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cmd/nodecmd/local.go b/cmd/nodecmd/local.go index 6345f71ed..02fbaa988 100644 --- a/cmd/nodecmd/local.go +++ b/cmd/nodecmd/local.go @@ -350,7 +350,9 @@ func localStartNode(_ *cobra.Command, args []string) error { spinner := spinSession.SpinToUser("Booting Network. Wait until healthy...") if _, err := cli.Start(ctx, avalancheGoBinPath, anrOpts...); err != nil { ux.SpinFailWithError(spinner, "", err) - localDestroyNode(nil, []string{clusterName}) + if destroyErr := localDestroyNode(nil, []string{clusterName}); destroyErr != nil { + return fmt.Errorf("failed to start local avalanchego: %w, and failed to destroy the local node %s due to %w", err, clusterName, destroyErr) + } return fmt.Errorf("failed to start local avalanchego: %w", err) } ux.SpinComplete(spinner) @@ -414,7 +416,10 @@ func localStopNode(_ *cobra.Command, _ []string) error { func localDestroyNode(_ *cobra.Command, args []string) error { clusterName := args[0] - localStopNode(nil, nil) + if err := localStopNode(nil, nil); err != nil { + return fmt.Errorf("failed to destroy local node: %w", err) + + } rootDir := app.GetLocalDir(clusterName) if err := os.RemoveAll(rootDir); err != nil { diff --git a/pkg/node/sync.go b/pkg/node/sync.go index a0af66576..40f21f715 100644 --- a/pkg/node/sync.go +++ b/pkg/node/sync.go @@ -6,12 +6,13 @@ import ( "encoding/json" "errors" "fmt" + "sync" + "github.com/ava-labs/avalanche-cli/pkg/binutils" "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-network-runner/client" anrutils "github.com/ava-labs/avalanche-network-runner/utils" "github.com/ava-labs/avalanchego/ids" - "sync" "github.com/ava-labs/avalanche-cli/pkg/ansible" "github.com/ava-labs/avalanche-cli/pkg/application" From 54479df433e1681645b303f488c83b951d5a8d60 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 14 Oct 2024 17:58:17 -0400 Subject: [PATCH 08/11] address comments --- cmd/blockchaincmd/deploy.go | 21 ++++++++++++--------- cmd/nodecmd/local.go | 5 +---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index eb2c40813..b50d4e2f5 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -74,6 +74,7 @@ var ( subnetOnly bool icmSpec subnet.ICMSpec generateNodeID bool + useLocalMachine bool bootstrapValidatorsJSONFilePath string privateKeyFlags contract.PrivateKeyFlags bootstrapEndpoints []string @@ -132,6 +133,7 @@ so you can take your locally tested Subnet and deploy it on Fuji or Mainnet.`, cmd.Flags().StringVar(&bootstrapValidatorsJSONFilePath, "bootstrap-filepath", "", "JSON file path that provides details about bootstrap validators, leave Node-ID and BLS values empty if using --generate-node-id=true") cmd.Flags().BoolVar(&generateNodeID, "generate-node-id", false, "whether to create new node id for bootstrap validators (Node-ID and BLS values in bootstrap JSON file will be overridden if --bootstrap-filepath flag is used)") cmd.Flags().StringSliceVar(&bootstrapEndpoints, "bootstrap-endpoints", nil, "take validator node info from the given endpoints") + cmd.Flags().BoolVar(&useLocalMachine, "use-local-machine", false, "use local machine as a blockchain validator") return cmd } @@ -466,16 +468,17 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { return PrintSubnetInfo(blockchainName, true) } - ux.Logger.PrintToUser("You can use your local machine as a bootstrap validator on the blockchain") - ux.Logger.PrintToUser("This means that you don't have to to set up a remote server on a cloud service (e.g. AWS / GCP) to be a validator on the blockchain.") + if !useLocalMachine { + ux.Logger.PrintToUser("You can use your local machine as a bootstrap validator on the blockchain") + ux.Logger.PrintToUser("This means that you don't have to to set up a remote server on a cloud service (e.g. AWS / GCP) to be a validator on the blockchain.") - useLocalMachine, err := app.Prompt.CaptureYesNo("Do you want to use your local machine as a bootstrap validator?") - if err != nil { - return err - } - - if useLocalMachine { - bootstrapEndpoints = []string{"http://127.0.0.1:9650"} + useLocalMachine, err = app.Prompt.CaptureYesNo("Do you want to use your local machine as a bootstrap validator?") + if err != nil { + return err + } + if useLocalMachine { + bootstrapEndpoints = []string{"http://127.0.0.1:9650"} + } } if len(bootstrapEndpoints) > 0 { diff --git a/cmd/nodecmd/local.go b/cmd/nodecmd/local.go index 02fbaa988..0c7dac4a2 100644 --- a/cmd/nodecmd/local.go +++ b/cmd/nodecmd/local.go @@ -416,10 +416,7 @@ func localStopNode(_ *cobra.Command, _ []string) error { func localDestroyNode(_ *cobra.Command, args []string) error { clusterName := args[0] - if err := localStopNode(nil, nil); err != nil { - return fmt.Errorf("failed to destroy local node: %w", err) - - } + localStopNode(nil, nil) rootDir := app.GetLocalDir(clusterName) if err := os.RemoveAll(rootDir); err != nil { From 3dfad06b23889ce47e1db0d504a42800de6d980a Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 14 Oct 2024 19:08:59 -0400 Subject: [PATCH 09/11] remove err --- cmd/nodecmd/local.go | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/cmd/nodecmd/local.go b/cmd/nodecmd/local.go index 0c7dac4a2..315c3c663 100644 --- a/cmd/nodecmd/local.go +++ b/cmd/nodecmd/local.go @@ -196,6 +196,14 @@ func localStartNode(_ *cobra.Command, args []string) error { } } serverLogPath := filepath.Join(rootDir, "server.log") + // make sure rootDir exists + if err := os.MkdirAll(rootDir, 0o700); err != nil { + return fmt.Errorf("could not create root directory %s: %w", rootDir, err) + } + // make sure pluginDir exists + if err := os.MkdirAll(pluginDir, 0o700); err != nil { + return fmt.Errorf("could not create plugin directory %s: %w", pluginDir, err) + } sd := subnet.NewLocalDeployer(app, avalancheGoVersion, avalanchegoBinaryPath, "") if err := sd.StartServer( constants.ServerRunFileLocalClusterPrefix, @@ -298,15 +306,6 @@ func localStartNode(_ *cobra.Command, args []string) error { defer os.Remove(upgradePath) } - // make sure rootDir exists - if err := os.MkdirAll(rootDir, 0o700); err != nil { - return fmt.Errorf("could not create root directory %s: %w", rootDir, err) - } - // make sure pluginDir exists - if err := os.MkdirAll(pluginDir, 0o700); err != nil { - return fmt.Errorf("could not create plugin directory %s: %w", pluginDir, err) - } - if stakingTLSKeyPath != "" && stakingCertKeyPath != "" && stakingSignerKeyPath != "" { if err := os.MkdirAll(filepath.Join(rootDir, "node1", "staking"), 0o700); err != nil { return fmt.Errorf("could not create root directory %s: %w", rootDir, err) @@ -350,9 +349,7 @@ func localStartNode(_ *cobra.Command, args []string) error { spinner := spinSession.SpinToUser("Booting Network. Wait until healthy...") if _, err := cli.Start(ctx, avalancheGoBinPath, anrOpts...); err != nil { ux.SpinFailWithError(spinner, "", err) - if destroyErr := localDestroyNode(nil, []string{clusterName}); destroyErr != nil { - return fmt.Errorf("failed to start local avalanchego: %w, and failed to destroy the local node %s due to %w", err, clusterName, destroyErr) - } + localDestroyNode(nil, []string{clusterName}) return fmt.Errorf("failed to start local avalanchego: %w", err) } ux.SpinComplete(spinner) From 2c56e099e13dc3bc1ea7ace92d9b2eb9c0424c45 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 14 Oct 2024 21:35:39 -0400 Subject: [PATCH 10/11] fix merge --- cmd/blockchaincmd/deploy.go | 1 - pkg/subnet/helpers.go | 4 ---- 2 files changed, 5 deletions(-) diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index bf9884cb6..b98a2ae84 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -15,7 +15,6 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/node" "github.com/ava-labs/avalanchego/api/info" - "github.com/ava-labs/avalanche-cli/pkg/node" "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" "github.com/ethereum/go-ethereum/common" diff --git a/pkg/subnet/helpers.go b/pkg/subnet/helpers.go index 61ad54c99..4011a0537 100644 --- a/pkg/subnet/helpers.go +++ b/pkg/subnet/helpers.go @@ -9,15 +9,11 @@ import ( "os" "path/filepath" "unicode" - "github.com/ava-labs/avalanche-cli/pkg/application" "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/key" "github.com/ava-labs/avalanche-cli/pkg/models" "github.com/ava-labs/avalanche-cli/pkg/utils" - "os" - "path/filepath" - "unicode" ) var errIllegalNameCharacter = errors.New( From 84e8ab5fd7a38c13c03087e21da0909ffac5ec56 Mon Sep 17 00:00:00 2001 From: Raymond Sukanto Date: Mon, 14 Oct 2024 21:55:52 -0400 Subject: [PATCH 11/11] update sidecar legacy --- cmd/blockchaincmd/deploy.go | 5 +++++ cmd/nodecmd/local.go | 8 -------- pkg/node/sync.go | 10 +++------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index b98a2ae84..91a7206af 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -818,7 +818,12 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { return err } ux.Logger.GreenCheckmarkToUser("Subnet is successfully converted into Subnet Only Validator") + } else { + if err := app.UpdateSidecarNetworks(&sidecar, network, subnetID, blockchainID, "", "", nil); err != nil { + return err + } } + flags := make(map[string]string) flags[constants.MetricsNetwork] = network.Name() metrics.HandleTracking(cmd, constants.MetricsSubnetDeployCommand, app, flags) diff --git a/cmd/nodecmd/local.go b/cmd/nodecmd/local.go index 60ce163db..5c165961f 100644 --- a/cmd/nodecmd/local.go +++ b/cmd/nodecmd/local.go @@ -468,11 +468,3 @@ func notImplementedForLocal(what string) error { ux.Logger.PrintToUser("Unsupported cmd: %s is not supported by local clusters", logging.LightBlue.Wrap(what)) return nil } - -func getRPCEndpoint(endpoint string, blockchainID string) string { - return models.NewDevnetNetwork(endpoint, 0).BlockchainEndpoint(blockchainID) -} - -func getWSEndpoint(endpoint string, blockchainID string) string { - return models.NewDevnetNetwork(endpoint, 0).BlockchainWSEndpoint(blockchainID) -} diff --git a/pkg/node/sync.go b/pkg/node/sync.go index 40f21f715..eb3125f39 100644 --- a/pkg/node/sync.go +++ b/pkg/node/sync.go @@ -322,13 +322,9 @@ func TrackSubnetWithLocalMachine(app *application.Avalanche, clusterName, blockc } func CheckClusterIsLocal(app *application.Avalanche, clusterName string) (bool, error) { - clustersConfig := models.ClustersConfig{} - if app.ClustersConfigExists() { - var err error - clustersConfig, err = app.LoadClustersConfig() - if err != nil { - return false, err - } + clustersConfig, err := app.GetClustersConfig() + if err != nil { + return false, err } clusterConf, ok := clustersConfig.Clusters[clusterName] return ok && clusterConf.Local, nil