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

Integrate local machine into subnet deploy #2235

Closed
wants to merge 14 commits into from
126 changes: 79 additions & 47 deletions cmd/blockchaincmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import (
"strings"
"time"

"github.com/ava-labs/avalanchego/api/info"
"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"

Expand Down Expand Up @@ -74,6 +75,7 @@ var (
subnetOnly bool
icmSpec subnet.ICMSpec
generateNodeID bool
useLocalMachine bool
bootstrapValidatorsJSONFilePath string
privateKeyFlags contract.PrivateKeyFlags
bootstrapEndpoints []string
Expand Down Expand Up @@ -132,6 +134,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
}

Expand Down Expand Up @@ -466,6 +469,19 @@ func deployBlockchain(cmd *cobra.Command, args []string) error {
return PrintSubnetInfo(blockchainName, true)
}

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"}
}
}

if len(bootstrapEndpoints) > 0 {
var changeAddr string
for _, endpoint := range bootstrapEndpoints {
Expand Down Expand Up @@ -727,71 +743,87 @@ func deployBlockchain(cmd *cobra.Command, args []string) error {
return err
}

if !generateNodeID {
clusterName, err := node.GetClusterNameFromList(app)
if err != nil {
return err
}
clusterName, err := node.GetClusterNameFromList(app)
Copy link
Collaborator

Choose a reason for hiding this comment

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

The cluster name to be taken from the input network itself IMO

Copy link
Collaborator

Choose a reason for hiding this comment

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

which should be a cluster

Copy link
Collaborator

Choose a reason for hiding this comment

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

models.Networks has ClusterName

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

better to do it when integrating local node start

if err != nil {
return err
}

if !useLocalMachine {
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,
}
_, genesisPrivateKey, err := contract.GetEVMSubnetPrefundedKey(
app,
network,
chainSpec,
)
if err != nil {
} else {
if err := node.TrackSubnetWithLocalMachine(app, clusterName, blockchainName); err != nil {
return err
}
rpcURL, _, err := contract.GetBlockchainEndpoints(
app,
network,
chainSpec,
true,
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
}
aggregatorExtraPeerEndpoints, err := GetAggregatorExtraPeerEndpoints(network)
if err != nil {
return err
}
if err := validatormanager.SetupPoA(
app,
network,
rpcURL,
contract.ChainSpec{
BlockchainName: blockchainName,
},
genesisPrivateKey,
common.HexToAddress(sidecar.PoAValidatorManagerOwner),
avaGoBootstrapValidators,
aggregatorExtraPeerEndpoints,
); err != nil {
return err
}
ux.Logger.GreenCheckmarkToUser("L1 is successfully converted to sovereign blockchain")
} else {
ux.Logger.GreenCheckmarkToUser("Generated Node ID and BLS info for bootstrap validator(s)")
ux.Logger.PrintToUser("To convert L1 to sovereign blockchain, create the corresponding Avalanche node(s) with the provided Node ID and BLS Info")
ux.Logger.PrintToUser("Created Node ID and BLS Info can be found at %s", app.GetSidecarPath(blockchainName))
ux.Logger.PrintToUser("Once the Avalanche Node(s) are created and are tracking the blockchain, call `avalanche contract initPoaManager %s` to finish converting L1 to sovereign blockchain", blockchainName)
}
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")
} 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)
Expand Down
122 changes: 12 additions & 110 deletions cmd/nodecmd/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"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"
Expand All @@ -17,10 +19,7 @@
"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"
)

Expand Down Expand Up @@ -205,6 +204,14 @@
}
}
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,
Expand Down Expand Up @@ -350,7 +357,7 @@
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})

Check failure on line 360 in cmd/nodecmd/local.go

View workflow job for this annotation

GitHub Actions / Lint

Error return value is not checked (errcheck)
return fmt.Errorf("failed to start local avalanchego: %w", err)
}
ux.SpinComplete(spinner)
Expand Down Expand Up @@ -414,14 +421,14 @@
func localDestroyNode(_ *cobra.Command, args []string) error {
clusterName := args[0]

localStopNode(nil, nil)

Check failure on line 424 in cmd/nodecmd/local.go

View workflow job for this annotation

GitHub Actions / Lint

Error return value is not checked (errcheck)

rootDir := app.GetLocalDir(clusterName)
if err := os.RemoveAll(rootDir); err != nil {
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)
}

Expand Down Expand Up @@ -451,118 +458,13 @@
return app.WriteClustersConfigFile(&clustersConfig)
}

func checkClusterIsLocal(clusterName string) (bool, error) {
clustersConfig, err := app.GetClustersConfig()
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
blockchainID := sc.Networks[network.Name()].BlockchainID
vmID, 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)
}
rootDir := app.GetLocalDir(clusterName)
pluginPath := filepath.Join(rootDir, "node1", "plugins", vmID.String())
if err := utils.FileCopy(vmBin, pluginPath); err != nil {
return err
}
if err := os.Chmod(pluginPath, constants.DefaultPerms755); err != nil {
return err
}
if app.ChainConfigExists(blockchainName) {
inputChainConfigPath := app.GetChainConfigPath(blockchainName)
outputChainConfigPath := filepath.Join(rootDir, "node1", "configs", "chains", blockchainID.String(), "config.json")
if err := os.MkdirAll(filepath.Dir(outputChainConfigPath), 0o700); err != nil {
return fmt.Errorf("could not create chain conf directory %s: %w", filepath.Dir(outputChainConfigPath), err)
}
if err := utils.FileCopy(inputChainConfigPath, outputChainConfigPath); 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)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I am not sure it is the moment to move this to node pgk. we keep a part of logic here and a part in node.
also, maybe node is not the final dest package.

}

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)
}
Loading
Loading