Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change subnet/blockchain creation order #1375

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
23 changes: 12 additions & 11 deletions cmd/nodecmd/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ import (
"path/filepath"
"sync"

"github.com/ava-labs/avalanche-cli/pkg/ssh"

"github.com/ava-labs/avalanche-cli/pkg/constants"

"github.com/ava-labs/avalanche-cli/pkg/ansible"

"github.com/ava-labs/avalanche-cli/cmd/subnetcmd"
"github.com/ava-labs/avalanche-cli/pkg/ansible"
"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/ux"
"github.com/ava-labs/avalanchego/ids"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -71,15 +69,15 @@ func syncSubnet(_ *cobra.Command, args []string) error {
if len(notHealthyNodes) > 0 {
return fmt.Errorf("node(s) %s are not healthy, please fix the issue and again", notHealthyNodes)
}
sc, err := app.LoadSidecar(subnetName)
if err != nil {
return err
}
incompatibleNodes, err := checkAvalancheGoVersionCompatible(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 {
Expand All @@ -103,7 +101,10 @@ func syncSubnet(_ *cobra.Command, args []string) error {
return fmt.Errorf("node(s) %s failed to sync with subnet %s", untrackedNodes, subnetName)
}
ux.Logger.PrintToUser("Node(s) successfully started syncing with Subnet!")
ux.Logger.PrintToUser(fmt.Sprintf("Check node subnet syncing status with avalanche node status %s --subnet %s", clusterName, subnetName))
blockchainID := sc.Networks[network.Kind.String()].BlockchainID
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to check if sc.Networks[network.Kind.String()] is not nil before trying to access BlockchainID as it can crash

if blockchainID != ids.Empty {
ux.Logger.PrintToUser(fmt.Sprintf("Check node subnet syncing status with avalanche node status %s --subnet %s", clusterName, subnetName))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fmt.Sprintf is not need as PrintToUser is a wrapper around Sprintf itself

}
return nil
}

Expand Down
48 changes: 0 additions & 48 deletions cmd/nodecmd/validateSubnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ import (
"fmt"
"time"

"github.com/ava-labs/avalanchego/vms/platformvm/status"

"github.com/ava-labs/avalanche-cli/pkg/ansible"
"github.com/ava-labs/avalanche-cli/pkg/ssh"

subnetcmd "github.com/ava-labs/avalanche-cli/cmd/subnetcmd"
"github.com/ava-labs/avalanche-cli/pkg/constants"
Expand Down Expand Up @@ -102,26 +99,6 @@ func addNodeAsSubnetValidator(
return nil
}

// getNodeSubnetSyncStatus checks if node is bootstrapped to blockchain blockchainID
// if getNodeSubnetSyncStatus is called from node validate subnet command, it will fail if
// node status is not 'syncing'. If getNodeSubnetSyncStatus is called from node status command,
// it will return true node status is 'syncing'
func getNodeSubnetSyncStatus(
host *models.Host,
blockchainID string,
) (string, error) {
ux.Logger.PrintToUser("Checking if node %s is synced to subnet ...", host.NodeID)
if resp, err := ssh.RunSSHSubnetSyncStatus(host, blockchainID); err != nil {
return "", err
} else {
if subnetSyncStatus, err := parseSubnetSyncOutput(resp); err != nil {
return "", err
} else {
return subnetSyncStatus, nil
}
}
}

func waitForNodeToBePrimaryNetworkValidator(network models.Network, nodeID ids.NodeID) error {
ux.Logger.PrintToUser("Waiting for the node to start as a Primary Network Validator...")
// wait for 20 seconds because we set the start time to be in 20 seconds
Expand Down Expand Up @@ -216,14 +193,6 @@ func validateSubnet(_ *cobra.Command, args []string) error {
if len(notHealthyNodes) > 0 {
return fmt.Errorf("node(s) %s are not healthy, please fix the issue and again", notHealthyNodes)
}
sc, err := app.LoadSidecar(subnetName)
if err != nil {
return err
}
blockchainID := sc.Networks[network.Name()].BlockchainID
if blockchainID == ids.Empty {
return ErrNoBlockchainID
}
nodeErrors := map[string]error{}
ux.Logger.PrintToUser("Note that we have staggered the end time of validation period to increase by 24 hours for each node added if multiple nodes are added as Primary Network validators simultaneously")
for i, host := range hosts {
Expand All @@ -243,23 +212,6 @@ func validateSubnet(_ *cobra.Command, args []string) error {
nodeErrors[host.NodeID] = err
continue
}
// we have to check if node is synced to subnet before adding the node as a validator
subnetSyncStatus, err := getNodeSubnetSyncStatus(host, blockchainID.String())
if err != nil {
ux.Logger.PrintToUser("Failed to get subnet sync status for node %s", host.NodeID)
nodeErrors[host.NodeID] = err
continue
}
if subnetSyncStatus != status.Syncing.String() {
if subnetSyncStatus == status.Validating.String() {
ux.Logger.PrintToUser("Failed to add node %s as subnet validator as node is already a subnet validator", host.NodeID)
nodeErrors[host.NodeID] = errors.New("node is already a subnet validator")
} else {
ux.Logger.PrintToUser("Failed to add node %s as subnet validator as node is not synced to subnet yet", host.NodeID)
nodeErrors[host.NodeID] = errors.New("node is not synced to subnet yet, please try again later")
}
continue
}
clusterNodeID := host.GetCloudID()
addedNodeAsPrimaryNetworkValidator, err := addNodeAsPrimaryNetworkValidator(network, kc, nodeID, i, clusterNodeID)
if err != nil {
Expand Down
139 changes: 122 additions & 17 deletions cmd/nodecmd/wiz.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"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/subnet"
"github.com/ava-labs/avalanche-cli/pkg/utils"
"github.com/ava-labs/avalanche-cli/pkg/ux"

"github.com/ava-labs/avalanchego/ids"
Expand Down Expand Up @@ -153,7 +155,6 @@ func wiz(cmd *cobra.Command, args []string) error {
} else {
ux.Logger.PrintToUser("")
ux.Logger.PrintToUser(logging.Green.Wrap("Adding subnet into existing devnet %s..."), clusterName)
ux.Logger.PrintToUser("")
}

// check all validators are found
Expand Down Expand Up @@ -184,6 +185,23 @@ func wiz(cmd *cobra.Command, args []string) error {
return err
}

ux.Logger.PrintToUser("")
ux.Logger.PrintToUser(logging.Green.Wrap("Adding nodes as subnet validators"))
ux.Logger.PrintToUser("")
if err := validateSubnet(cmd, []string{clusterName, subnetName}); err != nil {
return err
}
if err := waitForSubnetValidators(clusterName, subnetName, validateCheckTimeout, validateCheckPoolTime); err != nil {
return err
}

ux.Logger.PrintToUser("")
ux.Logger.PrintToUser(logging.Green.Wrap("Deploying the blockchain"))
ux.Logger.PrintToUser("")
if err := deploySubnet(cmd, []string{clusterName, subnetName}); err != nil {
return err
}

ux.Logger.PrintToUser("")
ux.Logger.PrintToUser(logging.Green.Wrap("Setting the nodes as subnet trackers"))
ux.Logger.PrintToUser("")
Expand All @@ -193,6 +211,7 @@ func wiz(cmd *cobra.Command, args []string) error {
if err := waitForHealthyCluster(clusterName, healthCheckTimeout, healthCheckPoolTime); err != nil {
return err
}

sc, err := app.LoadSidecar(subnetName)
if err != nil {
return err
Expand All @@ -201,24 +220,17 @@ func wiz(cmd *cobra.Command, args []string) error {
if blockchainID == ids.Empty {
return ErrNoBlockchainID
}
if err := waitForClusterSubnetStatus(clusterName, subnetName, blockchainID, status.Syncing, syncCheckTimeout, syncCheckPoolTime); err != nil {
return err
}
ux.Logger.PrintToUser("")
ux.Logger.PrintToUser(logging.Green.Wrap("Adding nodes as subnet validators"))
ux.Logger.PrintToUser("")
if err := validateSubnet(cmd, []string{clusterName, subnetName}); err != nil {
return err
}
if err := waitForClusterSubnetStatus(clusterName, subnetName, blockchainID, status.Validating, validateCheckTimeout, validateCheckPoolTime); err != nil {
return err
}

ux.Logger.PrintToUser("")
if clusterAlreadyExists {
ux.Logger.PrintToUser(logging.Green.Wrap("Devnet %s is now validating subnet %s"), clusterName, subnetName)
} else {
ux.Logger.PrintToUser(logging.Green.Wrap("Devnet %s is successfully created and is now validating subnet %s!"), clusterName, subnetName)
}

return nil
}

Expand Down Expand Up @@ -326,21 +338,29 @@ func waitForClusterSubnetStatus(
}

func checkClusterIsADevnet(clusterName string) error {
exists, err := clusterExists(clusterName)
network, err := getClusterNetwork(clusterName)
if err != nil {
return err
}
if network.Kind != models.Devnet {
return fmt.Errorf("cluster %q is not a Devnet", clusterName)
}
return nil
}

func getClusterNetwork(clusterName string) (models.Network, error) {
exists, err := clusterExists(clusterName)
if err != nil {
return models.UndefinedNetwork, err
}
if !exists {
return fmt.Errorf("cluster %q does not exists", clusterName)
return models.UndefinedNetwork, fmt.Errorf("cluster %q does not exists", clusterName)
}
clustersConfig, err := app.LoadClustersConfig()
if err != nil {
return err
}
if clustersConfig.Clusters[clusterName].Network.Kind != models.Devnet {
return fmt.Errorf("cluster %q is not a Devnet", clusterName)
return models.UndefinedNetwork, err
}
return nil
return clustersConfig.Clusters[clusterName].Network, nil
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check if clustersConfig.Clusters[clusterName] is not nil before accessing Network

}

func filterHosts(hosts []*models.Host, nodes []string) ([]*models.Host, error) {
Expand Down Expand Up @@ -371,3 +391,88 @@ func filterHosts(hosts []*models.Host, nodes []string) ([]*models.Host, error) {
}
return filteredHosts, nil
}

func checkForSubnetValidators(
clusterName string,
subnetName string,
) ([]string, error) {
ux.Logger.PrintToUser("Checking if node(s) are subnet validator(s)")
hosts, err := ansible.GetInventoryFromAnsibleInventoryFile(app.GetAnsibleInventoryDirPath(clusterName))
if err != nil {
return nil, err
}
if len(validators) != 0 {
hosts, err = filterHosts(hosts, validators)
if err != nil {
return nil, err
}
}
hostIDs, err := utils.MapWithError(hosts, func(h *models.Host) (string, error) {
_, o, err := models.HostAnsibleIDToCloudID(h.NodeID)
return o, err
})
if err != nil {
return nil, err
}
nodeIDs, err := utils.MapWithError(hostIDs, func(s string) (ids.NodeID, error) {
n, err := getNodeID(app.GetNodeInstanceDirPath(s))
return n, err
})
if err != nil {
return nil, err
}
network, err := getClusterNetwork(clusterName)
if err != nil {
return nil, err
}
sc, err := app.LoadSidecar(subnetName)
if err != nil {
return nil, err
}
subnetID := sc.Networks[network.Kind.String()].SubnetID
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check if sc.Networks[network.Kind.String()] is not nil

if subnetID == ids.Empty {
return nil, fmt.Errorf("expected a subnet ID different from primary network")
}
nonValidators := []string{}
for i, nodeID := range nodeIDs {
isValidator, err := subnet.IsSubnetValidator(subnetID, nodeID, network)
Copy link
Collaborator

@arturrez arturrez Jan 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: can be done in parallel to increase performance

if err != nil {
return nil, err
}
if !isValidator {
nonValidators = append(nonValidators, hostIDs[i])
}
}
return nonValidators, nil
}

func waitForSubnetValidators(
clusterName string,
subnetName string,
timeout time.Duration,
poolTime time.Duration,
) error {
ux.Logger.PrintToUser("")
ux.Logger.PrintToUser("Waiting for node(s) in cluster %s to become subnet validator(s) of %s...", clusterName, subnetName)
startTime := time.Now()
for {
nonValidators, err := checkForSubnetValidators(clusterName, subnetName)
if err != nil {
return err
}
if len(nonValidators) == 0 {
ux.Logger.PrintToUser("All nodes are subnet validators after %d seconds", uint32(time.Since(startTime).Seconds()))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can it be %f s or %0.2f?

return nil
}
if time.Since(startTime) > timeout {
ux.Logger.PrintToUser("")
ux.Logger.PrintToUser("Non validator Nodes")
for _, nonValidator := range nonValidators {
ux.Logger.PrintToUser(" " + nonValidator)
}
ux.Logger.PrintToUser("")
return fmt.Errorf("failed to verify all nodes are subnet validators after %d seconds", uint32(timeout.Seconds()))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can it be replaced with return fmt.Errorf("failed to verify all nodes as subnet validators after %f seconds", timeout.Seconds())?

}
time.Sleep(poolTime)
}
}
2 changes: 1 addition & 1 deletion cmd/subnetcmd/addValidator.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func addValidator(_ *cobra.Command, args []string) error {
if err != nil {
return err
}
network.HandlePublicNetworkSimulation()
fee := network.GenesisParams().AddSubnetValidatorFee
kc, err := keychain.GetKeychainFromCmdLineFlags(
app,
Expand All @@ -109,7 +110,6 @@ func addValidator(_ *cobra.Command, args []string) error {
if err != nil {
return err
}
network.HandlePublicNetworkSimulation()
return CallAddValidator(network, kc, useLedger, args[0], nodeIDStr, defaultValidatorParams)
}

Expand Down